Codebase list ros-rospack / 2990646
Imported Upstream version 2.1.25 Jochen Sprickerhof 9 years ago
86 changed file(s) with 8506 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 build
1 doxygen_output
0 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1 Changelog for package rospack
2 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3
4 2.1.25 (2014-10-14)
5 -------------------
6 * fix find_package(PythonLibs ...) with CMake 3 (`#42 <https://github.com/ros/rospack/issues/42>`_)
7
8 2.1.24 (2014-09-04)
9 -------------------
10 * support tags defined in package format 2 (`#43 <https://github.com/ros/rospack/issues/43>`_)
11
12 2.1.23 (2014-02-25)
13 -------------------
14 * only perform backquote substitution when needed (`#34 <https://github.com/ros/rospack/issues/34>`_)
15
16 2.1.22 (2014-01-07)
17 -------------------
18 * use specific python version catkin has decided on (`#29 <https://github.com/ros/rospack/issues/29>`_)
19 * python 3 compatibility (`#25 <https://github.com/ros/rospack/issues/25>`_, `#27 <https://github.com/ros/rospack/issues/27>`_)
20 * fall back gracefully whe gtest is not available
21 * update package urls
22
23 2.1.21 (2013-07-05)
24 -------------------
25 * honor CATKIN_IGNORE marker file when crawling for packages (`#21 <https://github.com/ros/rospack/issues/21>`_)
26
27 2.1.20 (2013-07-03)
28 -------------------
29 * improve error message to include package names when circular dependency is detected (`#18 <https://github.com/ros/rospack/issues/18>`_)
30 * check for CATKIN_ENABLE_TESTING to enable configure without tests
31 * add '-h' option
32
33 2.1.19 (2013-06-06)
34 -------------------
35 * modified command 'list-duplicates' to output the paths where the packages were found (`#3 <https://github.com/ros/rospack/issues/3>`_)
36 * modified 'rospack plugins' to not use rosdep (`#5 <https://github.com/ros/rospack/issues/5>`_)
37 * improve Windows support (`#10 <https://github.com/ros/rospack/issues/10>`_)
38 * use find_package() for tinyxml (if available)
39
40 2.1.18 (2013-03-21)
41 -------------------
42 * invert order of package type detection (dry before wet) (`ros-infrastructure/rospkg#30 <https://github.com/ros-infrastructure/rospkg/issues/30>`_)
43
44 2.1.17 (2013-03-08)
45 -------------------
46 * output full pkg-config command in case of errors (`#8 <https://github.com/ros/rospack/issues/8>`_)
47 * handle None as return value for call_pkg_config (`#8 <https://github.com/ros/rospack/issues/8>`_)
48 * fix crawling to always recrawl when forced (`#9 <https://github.com/ros/rospack/issues/9>`_)
49
50 2.1.16 (2013-01-13)
51 -------------------
52 * fix segfault for command depends1 which ignores exceptions and calls isSysPackage again (`#4 <https://github.com/ros/rospack/issues/4>`_)
53
54 2.1.15 (2012-12-06)
55 -------------------
56 * first public release for Groovy
0 cmake_minimum_required(VERSION 2.8.3)
1 project(rospack)
2
3 find_package(catkin REQUIRED)
4 find_package(Boost REQUIRED COMPONENTS filesystem program_options system)
5 set(Python_ADDITIONAL_VERSIONS "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}")
6 find_package(PythonLibs "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" REQUIRED)
7 find_package(tinyxml QUIET)
8 if(NOT tinyxml_FOUND)
9 # Just assume it's located in the system directories
10 # though you could try and detect stuff via the pkgconfig file
11 # it provides
12 set(tinyxml_LIBRARIES tinyxml)
13 endif()
14
15 catkin_package(
16 INCLUDE_DIRS include
17 LIBRARIES rospack tinyxml ${PYTHON_LIBRARIES}
18 DEPENDS Boost
19 )
20
21 #add_definitions(-Wall)
22
23 set(API_BACKCOMPAT_V1 "YES" CACHE BOOL "Whether to enable backwards compatibility with old C++ API")
24 if(API_BACKCOMPAT_V1)
25 add_definitions(-DROSPACK_API_BACKCOMPAT_V1)
26 set(backcompat_source src/rospack_backcompat.cpp)
27 endif()
28
29 include_directories(include ${tinyxml_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})
30
31 add_library(rospack
32 src/rospack.cpp
33 ${backcompat_source}
34 src/rospack_cmdline.cpp
35 src/utils.cpp
36 )
37 target_link_libraries(rospack ${tinyxml_LIBRARIES} ${Boost_LIBRARIES} ${PYTHON_LIBRARIES})
38
39 add_executable(rospackexe src/rospack_main.cpp)
40 # Set the name, and make it a "global" executable
41 set_target_properties(rospackexe PROPERTIES
42 OUTPUT_NAME rospack
43 RUNTIME_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/bin)
44 target_link_libraries(rospackexe rospack ${Boost_LIBRARIES})
45 add_executable(rosstackexe src/rosstack_main.cpp)
46 target_link_libraries(rosstackexe rospack ${Boost_LIBRARIES})
47 # Set the name, and make it a "global" executable
48 set_target_properties(rosstackexe PROPERTIES
49 OUTPUT_NAME rosstack
50 RUNTIME_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/bin)
51
52 install(TARGETS rospack rospackexe rosstackexe
53 ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
54 LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
55 RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION})
56 install(DIRECTORY include/${PROJECT_NAME}/
57 DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
58 FILES_MATCHING PATTERN "*.h")
59
60 # uninstall target
61 configure_file(
62 "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
63 "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
64 IMMEDIATE @ONLY)
65 add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
66
67 if(CATKIN_ENABLE_TESTING)
68 add_subdirectory(test)
69 endif()
70
71 if(DOXYGEN_FOUND)
72 add_custom_target(rospack-docs
73 COMMAND doxygen Doxyfile
74 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
75 endif()
0 # Doxyfile 1.6.3
1
2 # This file describes the settings to be used by the documentation system
3 # doxygen (www.doxygen.org) for a project
4 #
5 # All text after a hash (#) is considered a comment and will be ignored
6 # The format is:
7 # TAG = value [value, ...]
8 # For lists items can also be appended using:
9 # TAG += value [value, ...]
10 # Values that contain spaces should be placed between quotes (" ")
11
12 #---------------------------------------------------------------------------
13 # Project related configuration options
14 #---------------------------------------------------------------------------
15
16 # This tag specifies the encoding used for all characters in the config file
17 # that follow. The default is UTF-8 which is also the encoding used for all
18 # text before the first occurrence of this tag. Doxygen uses libiconv (or the
19 # iconv built into libc) for the transcoding. See
20 # http://www.gnu.org/software/libiconv for the list of possible encodings.
21
22 DOXYFILE_ENCODING = UTF-8
23
24 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded
25 # by quotes) that should identify the project.
26
27 PROJECT_NAME = librospack
28
29 # The PROJECT_NUMBER tag can be used to enter a project or revision number.
30 # This could be handy for archiving the generated documentation or
31 # if some version control system is used.
32
33 PROJECT_NUMBER =
34
35 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
36 # base path where the generated documentation will be put.
37 # If a relative path is entered, it will be relative to the location
38 # where doxygen was started. If left blank the current directory will be used.
39
40 OUTPUT_DIRECTORY = doxygen_output
41
42 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
43 # 4096 sub-directories (in 2 levels) under the output directory of each output
44 # format and will distribute the generated files over these directories.
45 # Enabling this option can be useful when feeding doxygen a huge amount of
46 # source files, where putting all generated files in the same directory would
47 # otherwise cause performance problems for the file system.
48
49 CREATE_SUBDIRS = NO
50
51 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
52 # documentation generated by doxygen is written. Doxygen will use this
53 # information to generate all constant output in the proper language.
54 # The default language is English, other supported languages are:
55 # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
56 # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
57 # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
58 # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
59 # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
60 # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
61
62 OUTPUT_LANGUAGE = English
63
64 # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
65 # include brief member descriptions after the members that are listed in
66 # the file and class documentation (similar to JavaDoc).
67 # Set to NO to disable this.
68
69 BRIEF_MEMBER_DESC = YES
70
71 # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
72 # the brief description of a member or function before the detailed description.
73 # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
74 # brief descriptions will be completely suppressed.
75
76 REPEAT_BRIEF = YES
77
78 # This tag implements a quasi-intelligent brief description abbreviator
79 # that is used to form the text in various listings. Each string
80 # in this list, if found as the leading text of the brief description, will be
81 # stripped from the text and the result after processing the whole list, is
82 # used as the annotated text. Otherwise, the brief description is used as-is.
83 # If left blank, the following values are used ("$name" is automatically
84 # replaced with the name of the entity): "The $name class" "The $name widget"
85 # "The $name file" "is" "provides" "specifies" "contains"
86 # "represents" "a" "an" "the"
87
88 ABBREVIATE_BRIEF =
89
90 # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
91 # Doxygen will generate a detailed section even if there is only a brief
92 # description.
93
94 ALWAYS_DETAILED_SEC = NO
95
96 # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
97 # inherited members of a class in the documentation of that class as if those
98 # members were ordinary class members. Constructors, destructors and assignment
99 # operators of the base classes will not be shown.
100
101 INLINE_INHERITED_MEMB = NO
102
103 # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
104 # path before files name in the file list and in the header files. If set
105 # to NO the shortest path that makes the file name unique will be used.
106
107 FULL_PATH_NAMES = NO
108
109 # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
110 # can be used to strip a user-defined part of the path. Stripping is
111 # only done if one of the specified strings matches the left-hand part of
112 # the path. The tag can be used to show relative paths in the file list.
113 # If left blank the directory from which doxygen is run is used as the
114 # path to strip.
115
116 STRIP_FROM_PATH =
117
118 # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
119 # the path mentioned in the documentation of a class, which tells
120 # the reader which header file to include in order to use a class.
121 # If left blank only the name of the header file containing the class
122 # definition is used. Otherwise one should specify the include paths that
123 # are normally passed to the compiler using the -I flag.
124
125 STRIP_FROM_INC_PATH =
126
127 # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
128 # (but less readable) file names. This can be useful is your file systems
129 # doesn't support long names like on DOS, Mac, or CD-ROM.
130
131 SHORT_NAMES = NO
132
133 # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
134 # will interpret the first line (until the first dot) of a JavaDoc-style
135 # comment as the brief description. If set to NO, the JavaDoc
136 # comments will behave just like regular Qt-style comments
137 # (thus requiring an explicit @brief command for a brief description.)
138
139 JAVADOC_AUTOBRIEF = NO
140
141 # If the QT_AUTOBRIEF tag is set to YES then Doxygen will
142 # interpret the first line (until the first dot) of a Qt-style
143 # comment as the brief description. If set to NO, the comments
144 # will behave just like regular Qt-style comments (thus requiring
145 # an explicit \brief command for a brief description.)
146
147 QT_AUTOBRIEF = NO
148
149 # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
150 # treat a multi-line C++ special comment block (i.e. a block of //! or ///
151 # comments) as a brief description. This used to be the default behaviour.
152 # The new default is to treat a multi-line C++ comment block as a detailed
153 # description. Set this tag to YES if you prefer the old behaviour instead.
154
155 MULTILINE_CPP_IS_BRIEF = NO
156
157 # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
158 # member inherits the documentation from any documented member that it
159 # re-implements.
160
161 INHERIT_DOCS = YES
162
163 # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
164 # a new page for each member. If set to NO, the documentation of a member will
165 # be part of the file/class/namespace that contains it.
166
167 SEPARATE_MEMBER_PAGES = NO
168
169 # The TAB_SIZE tag can be used to set the number of spaces in a tab.
170 # Doxygen uses this value to replace tabs by spaces in code fragments.
171
172 TAB_SIZE = 8
173
174 # This tag can be used to specify a number of aliases that acts
175 # as commands in the documentation. An alias has the form "name=value".
176 # For example adding "sideeffect=\par Side Effects:\n" will allow you to
177 # put the command \sideeffect (or @sideeffect) in the documentation, which
178 # will result in a user-defined paragraph with heading "Side Effects:".
179 # You can put \n's in the value part of an alias to insert newlines.
180
181 ALIASES =
182
183 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
184 # sources only. Doxygen will then generate output that is more tailored for C.
185 # For instance, some of the names that are used will be different. The list
186 # of all members will be omitted, etc.
187
188 OPTIMIZE_OUTPUT_FOR_C = NO
189
190 # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
191 # sources only. Doxygen will then generate output that is more tailored for
192 # Java. For instance, namespaces will be presented as packages, qualified
193 # scopes will look different, etc.
194
195 OPTIMIZE_OUTPUT_JAVA = NO
196
197 # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
198 # sources only. Doxygen will then generate output that is more tailored for
199 # Fortran.
200
201 OPTIMIZE_FOR_FORTRAN = NO
202
203 # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
204 # sources. Doxygen will then generate output that is tailored for
205 # VHDL.
206
207 OPTIMIZE_OUTPUT_VHDL = NO
208
209 # Doxygen selects the parser to use depending on the extension of the files it parses.
210 # With this tag you can assign which parser to use for a given extension.
211 # Doxygen has a built-in mapping, but you can override or extend it using this tag.
212 # The format is ext=language, where ext is a file extension, and language is one of
213 # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
214 # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
215 # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
216 # use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
217
218 EXTENSION_MAPPING =
219
220 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
221 # to include (a tag file for) the STL sources as input, then you should
222 # set this tag to YES in order to let doxygen match functions declarations and
223 # definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
224 # func(std::string) {}). This also make the inheritance and collaboration
225 # diagrams that involve STL classes more complete and accurate.
226
227 BUILTIN_STL_SUPPORT = NO
228
229 # If you use Microsoft's C++/CLI language, you should set this option to YES to
230 # enable parsing support.
231
232 CPP_CLI_SUPPORT = NO
233
234 # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
235 # Doxygen will parse them like normal C++ but will assume all classes use public
236 # instead of private inheritance when no explicit protection keyword is present.
237
238 SIP_SUPPORT = NO
239
240 # For Microsoft's IDL there are propget and propput attributes to indicate getter
241 # and setter methods for a property. Setting this option to YES (the default)
242 # will make doxygen to replace the get and set methods by a property in the
243 # documentation. This will only work if the methods are indeed getting or
244 # setting a simple type. If this is not the case, or you want to show the
245 # methods anyway, you should set this option to NO.
246
247 IDL_PROPERTY_SUPPORT = YES
248
249 # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
250 # tag is set to YES, then doxygen will reuse the documentation of the first
251 # member in the group (if any) for the other members of the group. By default
252 # all members of a group must be documented explicitly.
253
254 DISTRIBUTE_GROUP_DOC = NO
255
256 # Set the SUBGROUPING tag to YES (the default) to allow class member groups of
257 # the same type (for instance a group of public functions) to be put as a
258 # subgroup of that type (e.g. under the Public Functions section). Set it to
259 # NO to prevent subgrouping. Alternatively, this can be done per class using
260 # the \nosubgrouping command.
261
262 SUBGROUPING = YES
263
264 # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
265 # is documented as struct, union, or enum with the name of the typedef. So
266 # typedef struct TypeS {} TypeT, will appear in the documentation as a struct
267 # with name TypeT. When disabled the typedef will appear as a member of a file,
268 # namespace, or class. And the struct will be named TypeS. This can typically
269 # be useful for C code in case the coding convention dictates that all compound
270 # types are typedef'ed and only the typedef is referenced, never the tag name.
271
272 TYPEDEF_HIDES_STRUCT = NO
273
274 # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
275 # determine which symbols to keep in memory and which to flush to disk.
276 # When the cache is full, less often used symbols will be written to disk.
277 # For small to medium size projects (<1000 input files) the default value is
278 # probably good enough. For larger projects a too small cache size can cause
279 # doxygen to be busy swapping symbols to and from disk most of the time
280 # causing a significant performance penality.
281 # If the system has enough physical memory increasing the cache will improve the
282 # performance by keeping more symbols in memory. Note that the value works on
283 # a logarithmic scale so increasing the size by one will rougly double the
284 # memory usage. The cache size is given by this formula:
285 # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
286 # corresponding to a cache size of 2^16 = 65536 symbols
287
288 SYMBOL_CACHE_SIZE = 0
289
290 #---------------------------------------------------------------------------
291 # Build related configuration options
292 #---------------------------------------------------------------------------
293
294 # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
295 # documentation are documented, even if no documentation was available.
296 # Private class members and static file members will be hidden unless
297 # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
298
299 EXTRACT_ALL = NO
300
301 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class
302 # will be included in the documentation.
303
304 EXTRACT_PRIVATE = NO
305
306 # If the EXTRACT_STATIC tag is set to YES all static members of a file
307 # will be included in the documentation.
308
309 EXTRACT_STATIC = NO
310
311 # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
312 # defined locally in source files will be included in the documentation.
313 # If set to NO only classes defined in header files are included.
314
315 EXTRACT_LOCAL_CLASSES = YES
316
317 # This flag is only useful for Objective-C code. When set to YES local
318 # methods, which are defined in the implementation section but not in
319 # the interface are included in the documentation.
320 # If set to NO (the default) only methods in the interface are included.
321
322 EXTRACT_LOCAL_METHODS = NO
323
324 # If this flag is set to YES, the members of anonymous namespaces will be
325 # extracted and appear in the documentation as a namespace called
326 # 'anonymous_namespace{file}', where file will be replaced with the base
327 # name of the file that contains the anonymous namespace. By default
328 # anonymous namespace are hidden.
329
330 EXTRACT_ANON_NSPACES = NO
331
332 # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
333 # undocumented members of documented classes, files or namespaces.
334 # If set to NO (the default) these members will be included in the
335 # various overviews, but no documentation section is generated.
336 # This option has no effect if EXTRACT_ALL is enabled.
337
338 HIDE_UNDOC_MEMBERS = NO
339
340 # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
341 # undocumented classes that are normally visible in the class hierarchy.
342 # If set to NO (the default) these classes will be included in the various
343 # overviews. This option has no effect if EXTRACT_ALL is enabled.
344
345 HIDE_UNDOC_CLASSES = NO
346
347 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
348 # friend (class|struct|union) declarations.
349 # If set to NO (the default) these declarations will be included in the
350 # documentation.
351
352 HIDE_FRIEND_COMPOUNDS = NO
353
354 # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
355 # documentation blocks found inside the body of a function.
356 # If set to NO (the default) these blocks will be appended to the
357 # function's detailed documentation block.
358
359 HIDE_IN_BODY_DOCS = NO
360
361 # The INTERNAL_DOCS tag determines if documentation
362 # that is typed after a \internal command is included. If the tag is set
363 # to NO (the default) then the documentation will be excluded.
364 # Set it to YES to include the internal documentation.
365
366 INTERNAL_DOCS = NO
367
368 # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
369 # file names in lower-case letters. If set to YES upper-case letters are also
370 # allowed. This is useful if you have classes or files whose names only differ
371 # in case and if your file system supports case sensitive file names. Windows
372 # and Mac users are advised to set this option to NO.
373
374 CASE_SENSE_NAMES = YES
375
376 # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
377 # will show members with their full class and namespace scopes in the
378 # documentation. If set to YES the scope will be hidden.
379
380 HIDE_SCOPE_NAMES = NO
381
382 # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
383 # will put a list of the files that are included by a file in the documentation
384 # of that file.
385
386 SHOW_INCLUDE_FILES = YES
387
388 # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
389 # will list include files with double quotes in the documentation
390 # rather than with sharp brackets.
391
392 FORCE_LOCAL_INCLUDES = NO
393
394 # If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
395 # is inserted in the documentation for inline members.
396
397 INLINE_INFO = YES
398
399 # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
400 # will sort the (detailed) documentation of file and class members
401 # alphabetically by member name. If set to NO the members will appear in
402 # declaration order.
403
404 SORT_MEMBER_DOCS = YES
405
406 # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
407 # brief documentation of file, namespace and class members alphabetically
408 # by member name. If set to NO (the default) the members will appear in
409 # declaration order.
410
411 SORT_BRIEF_DOCS = NO
412
413 # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
414
415 SORT_MEMBERS_CTORS_1ST = NO
416
417 # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
418 # hierarchy of group names into alphabetical order. If set to NO (the default)
419 # the group names will appear in their defined order.
420
421 SORT_GROUP_NAMES = NO
422
423 # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
424 # sorted by fully-qualified names, including namespaces. If set to
425 # NO (the default), the class list will be sorted only by class name,
426 # not including the namespace part.
427 # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
428 # Note: This option applies only to the class list, not to the
429 # alphabetical list.
430
431 SORT_BY_SCOPE_NAME = NO
432
433 # The GENERATE_TODOLIST tag can be used to enable (YES) or
434 # disable (NO) the todo list. This list is created by putting \todo
435 # commands in the documentation.
436
437 GENERATE_TODOLIST = YES
438
439 # The GENERATE_TESTLIST tag can be used to enable (YES) or
440 # disable (NO) the test list. This list is created by putting \test
441 # commands in the documentation.
442
443 GENERATE_TESTLIST = YES
444
445 # The GENERATE_BUGLIST tag can be used to enable (YES) or
446 # disable (NO) the bug list. This list is created by putting \bug
447 # commands in the documentation.
448
449 GENERATE_BUGLIST = YES
450
451 # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
452 # disable (NO) the deprecated list. This list is created by putting
453 # \deprecated commands in the documentation.
454
455 GENERATE_DEPRECATEDLIST= YES
456
457 # The ENABLED_SECTIONS tag can be used to enable conditional
458 # documentation sections, marked by \if sectionname ... \endif.
459
460 ENABLED_SECTIONS =
461
462 # The MAX_INITIALIZER_LINES tag determines the maximum number of lines
463 # the initial value of a variable or define consists of for it to appear in
464 # the documentation. If the initializer consists of more lines than specified
465 # here it will be hidden. Use a value of 0 to hide initializers completely.
466 # The appearance of the initializer of individual variables and defines in the
467 # documentation can be controlled using \showinitializer or \hideinitializer
468 # command in the documentation regardless of this setting.
469
470 MAX_INITIALIZER_LINES = 30
471
472 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated
473 # at the bottom of the documentation of classes and structs. If set to YES the
474 # list will mention the files that were used to generate the documentation.
475
476 SHOW_USED_FILES = YES
477
478 # If the sources in your project are distributed over multiple directories
479 # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
480 # in the documentation. The default is NO.
481
482 SHOW_DIRECTORIES = NO
483
484 # Set the SHOW_FILES tag to NO to disable the generation of the Files page.
485 # This will remove the Files entry from the Quick Index and from the
486 # Folder Tree View (if specified). The default is YES.
487
488 SHOW_FILES = YES
489
490 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the
491 # Namespaces page.
492 # This will remove the Namespaces entry from the Quick Index
493 # and from the Folder Tree View (if specified). The default is YES.
494
495 SHOW_NAMESPACES = YES
496
497 # The FILE_VERSION_FILTER tag can be used to specify a program or script that
498 # doxygen should invoke to get the current version for each file (typically from
499 # the version control system). Doxygen will invoke the program by executing (via
500 # popen()) the command <command> <input-file>, where <command> is the value of
501 # the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
502 # provided by doxygen. Whatever the program writes to standard output
503 # is used as the file version. See the manual for examples.
504
505 FILE_VERSION_FILTER =
506
507 # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
508 # doxygen. The layout file controls the global structure of the generated output files
509 # in an output format independent way. The create the layout file that represents
510 # doxygen's defaults, run doxygen with the -l option. You can optionally specify a
511 # file name after the option, if omitted DoxygenLayout.xml will be used as the name
512 # of the layout file.
513
514 LAYOUT_FILE =
515
516 #---------------------------------------------------------------------------
517 # configuration options related to warning and progress messages
518 #---------------------------------------------------------------------------
519
520 # The QUIET tag can be used to turn on/off the messages that are generated
521 # by doxygen. Possible values are YES and NO. If left blank NO is used.
522
523 QUIET = NO
524
525 # The WARNINGS tag can be used to turn on/off the warning messages that are
526 # generated by doxygen. Possible values are YES and NO. If left blank
527 # NO is used.
528
529 WARNINGS = YES
530
531 # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
532 # for undocumented members. If EXTRACT_ALL is set to YES then this flag will
533 # automatically be disabled.
534
535 WARN_IF_UNDOCUMENTED = YES
536
537 # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
538 # potential errors in the documentation, such as not documenting some
539 # parameters in a documented function, or documenting parameters that
540 # don't exist or using markup commands wrongly.
541
542 WARN_IF_DOC_ERROR = YES
543
544 # This WARN_NO_PARAMDOC option can be abled to get warnings for
545 # functions that are documented, but have no documentation for their parameters
546 # or return value. If set to NO (the default) doxygen will only warn about
547 # wrong or incomplete parameter documentation, but not about the absence of
548 # documentation.
549
550 WARN_NO_PARAMDOC = NO
551
552 # The WARN_FORMAT tag determines the format of the warning messages that
553 # doxygen can produce. The string should contain the $file, $line, and $text
554 # tags, which will be replaced by the file and line number from which the
555 # warning originated and the warning text. Optionally the format may contain
556 # $version, which will be replaced by the version of the file (if it could
557 # be obtained via FILE_VERSION_FILTER)
558
559 WARN_FORMAT = "$file:$line: $text"
560
561 # The WARN_LOGFILE tag can be used to specify a file to which warning
562 # and error messages should be written. If left blank the output is written
563 # to stderr.
564
565 WARN_LOGFILE =
566
567 #---------------------------------------------------------------------------
568 # configuration options related to the input files
569 #---------------------------------------------------------------------------
570
571 # The INPUT tag can be used to specify the files and/or directories that contain
572 # documented source files. You may enter file names like "myfile.cpp" or
573 # directories like "/usr/src/myproject". Separate the files or directories
574 # with spaces.
575
576 INPUT = include/rospack
577
578 # This tag can be used to specify the character encoding of the source files
579 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
580 # also the default input encoding. Doxygen uses libiconv (or the iconv built
581 # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
582 # the list of possible encodings.
583
584 INPUT_ENCODING = UTF-8
585
586 # If the value of the INPUT tag contains directories, you can use the
587 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
588 # and *.h) to filter out the source-files in the directories. If left
589 # blank the following patterns are tested:
590 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
591 # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
592
593 FILE_PATTERNS =
594
595 # The RECURSIVE tag can be used to turn specify whether or not subdirectories
596 # should be searched for input files as well. Possible values are YES and NO.
597 # If left blank NO is used.
598
599 RECURSIVE = NO
600
601 # The EXCLUDE tag can be used to specify files and/or directories that should
602 # excluded from the INPUT source files. This way you can easily exclude a
603 # subdirectory from a directory tree whose root is specified with the INPUT tag.
604
605 EXCLUDE =
606
607 # The EXCLUDE_SYMLINKS tag can be used select whether or not files or
608 # directories that are symbolic links (a Unix filesystem feature) are excluded
609 # from the input.
610
611 EXCLUDE_SYMLINKS = NO
612
613 # If the value of the INPUT tag contains directories, you can use the
614 # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
615 # certain files from those directories. Note that the wildcards are matched
616 # against the file with absolute path, so to exclude all test directories
617 # for example use the pattern */test/*
618
619 EXCLUDE_PATTERNS =
620
621 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
622 # (namespaces, classes, functions, etc.) that should be excluded from the
623 # output. The symbol name can be a fully qualified name, a word, or if the
624 # wildcard * is used, a substring. Examples: ANamespace, AClass,
625 # AClass::ANamespace, ANamespace::*Test
626
627 EXCLUDE_SYMBOLS =
628
629 # The EXAMPLE_PATH tag can be used to specify one or more files or
630 # directories that contain example code fragments that are included (see
631 # the \include command).
632
633 EXAMPLE_PATH =
634
635 # If the value of the EXAMPLE_PATH tag contains directories, you can use the
636 # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
637 # and *.h) to filter out the source-files in the directories. If left
638 # blank all files are included.
639
640 EXAMPLE_PATTERNS =
641
642 # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
643 # searched for input files to be used with the \include or \dontinclude
644 # commands irrespective of the value of the RECURSIVE tag.
645 # Possible values are YES and NO. If left blank NO is used.
646
647 EXAMPLE_RECURSIVE = NO
648
649 # The IMAGE_PATH tag can be used to specify one or more files or
650 # directories that contain image that are included in the documentation (see
651 # the \image command).
652
653 IMAGE_PATH =
654
655 # The INPUT_FILTER tag can be used to specify a program that doxygen should
656 # invoke to filter for each input file. Doxygen will invoke the filter program
657 # by executing (via popen()) the command <filter> <input-file>, where <filter>
658 # is the value of the INPUT_FILTER tag, and <input-file> is the name of an
659 # input file. Doxygen will then use the output that the filter program writes
660 # to standard output.
661 # If FILTER_PATTERNS is specified, this tag will be
662 # ignored.
663
664 INPUT_FILTER =
665
666 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
667 # basis.
668 # Doxygen will compare the file name with each pattern and apply the
669 # filter if there is a match.
670 # The filters are a list of the form:
671 # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
672 # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
673 # is applied to all files.
674
675 FILTER_PATTERNS =
676
677 # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
678 # INPUT_FILTER) will be used to filter the input files when producing source
679 # files to browse (i.e. when SOURCE_BROWSER is set to YES).
680
681 FILTER_SOURCE_FILES = NO
682
683 #---------------------------------------------------------------------------
684 # configuration options related to source browsing
685 #---------------------------------------------------------------------------
686
687 # If the SOURCE_BROWSER tag is set to YES then a list of source files will
688 # be generated. Documented entities will be cross-referenced with these sources.
689 # Note: To get rid of all source code in the generated output, make sure also
690 # VERBATIM_HEADERS is set to NO.
691
692 SOURCE_BROWSER = NO
693
694 # Setting the INLINE_SOURCES tag to YES will include the body
695 # of functions and classes directly in the documentation.
696
697 INLINE_SOURCES = NO
698
699 # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
700 # doxygen to hide any special comment blocks from generated source code
701 # fragments. Normal C and C++ comments will always remain visible.
702
703 STRIP_CODE_COMMENTS = YES
704
705 # If the REFERENCED_BY_RELATION tag is set to YES
706 # then for each documented function all documented
707 # functions referencing it will be listed.
708
709 REFERENCED_BY_RELATION = NO
710
711 # If the REFERENCES_RELATION tag is set to YES
712 # then for each documented function all documented entities
713 # called/used by that function will be listed.
714
715 REFERENCES_RELATION = NO
716
717 # If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
718 # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
719 # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
720 # link to the source code.
721 # Otherwise they will link to the documentation.
722
723 REFERENCES_LINK_SOURCE = YES
724
725 # If the USE_HTAGS tag is set to YES then the references to source code
726 # will point to the HTML generated by the htags(1) tool instead of doxygen
727 # built-in source browser. The htags tool is part of GNU's global source
728 # tagging system (see http://www.gnu.org/software/global/global.html). You
729 # will need version 4.8.6 or higher.
730
731 USE_HTAGS = NO
732
733 # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
734 # will generate a verbatim copy of the header file for each class for
735 # which an include is specified. Set to NO to disable this.
736
737 VERBATIM_HEADERS = YES
738
739 #---------------------------------------------------------------------------
740 # configuration options related to the alphabetical class index
741 #---------------------------------------------------------------------------
742
743 # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
744 # of all compounds will be generated. Enable this if the project
745 # contains a lot of classes, structs, unions or interfaces.
746
747 ALPHABETICAL_INDEX = NO
748
749 # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
750 # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
751 # in which this list will be split (can be a number in the range [1..20])
752
753 COLS_IN_ALPHA_INDEX = 5
754
755 # In case all classes in a project start with a common prefix, all
756 # classes will be put under the same header in the alphabetical index.
757 # The IGNORE_PREFIX tag can be used to specify one or more prefixes that
758 # should be ignored while generating the index headers.
759
760 IGNORE_PREFIX =
761
762 #---------------------------------------------------------------------------
763 # configuration options related to the HTML output
764 #---------------------------------------------------------------------------
765
766 # If the GENERATE_HTML tag is set to YES (the default) Doxygen will
767 # generate HTML output.
768
769 GENERATE_HTML = YES
770
771 # The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
772 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
773 # put in front of it. If left blank `html' will be used as the default path.
774
775 HTML_OUTPUT = html
776
777 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for
778 # each generated HTML page (for example: .htm,.php,.asp). If it is left blank
779 # doxygen will generate files with .html extension.
780
781 HTML_FILE_EXTENSION = .html
782
783 # The HTML_HEADER tag can be used to specify a personal HTML header for
784 # each generated HTML page. If it is left blank doxygen will generate a
785 # standard header.
786
787 HTML_HEADER =
788
789 # The HTML_FOOTER tag can be used to specify a personal HTML footer for
790 # each generated HTML page. If it is left blank doxygen will generate a
791 # standard footer.
792
793 HTML_FOOTER =
794
795 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
796 # style sheet that is used by each HTML page. It can be used to
797 # fine-tune the look of the HTML output. If the tag is left blank doxygen
798 # will generate a default style sheet. Note that doxygen will try to copy
799 # the style sheet file to the HTML output directory, so don't put your own
800 # stylesheet in the HTML output directory as well, or it will be erased!
801
802 HTML_STYLESHEET =
803
804 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
805 # page will contain the date and time when the page was generated. Setting
806 # this to NO can help when comparing the output of multiple runs.
807
808 HTML_TIMESTAMP = YES
809
810 # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
811 # files or namespaces will be aligned in HTML using tables. If set to
812 # NO a bullet list will be used.
813
814 HTML_ALIGN_MEMBERS = YES
815
816 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
817 # documentation will contain sections that can be hidden and shown after the
818 # page has loaded. For this to work a browser that supports
819 # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
820 # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
821
822 HTML_DYNAMIC_SECTIONS = NO
823
824 # If the GENERATE_DOCSET tag is set to YES, additional index files
825 # will be generated that can be used as input for Apple's Xcode 3
826 # integrated development environment, introduced with OSX 10.5 (Leopard).
827 # To create a documentation set, doxygen will generate a Makefile in the
828 # HTML output directory. Running make will produce the docset in that
829 # directory and running "make install" will install the docset in
830 # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
831 # it at startup.
832 # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
833
834 GENERATE_DOCSET = NO
835
836 # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
837 # feed. A documentation feed provides an umbrella under which multiple
838 # documentation sets from a single provider (such as a company or product suite)
839 # can be grouped.
840
841 DOCSET_FEEDNAME = "Doxygen generated docs"
842
843 # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
844 # should uniquely identify the documentation set bundle. This should be a
845 # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
846 # will append .docset to the name.
847
848 DOCSET_BUNDLE_ID = org.doxygen.Project
849
850 # If the GENERATE_HTMLHELP tag is set to YES, additional index files
851 # will be generated that can be used as input for tools like the
852 # Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
853 # of the generated HTML documentation.
854
855 GENERATE_HTMLHELP = NO
856
857 # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
858 # be used to specify the file name of the resulting .chm file. You
859 # can add a path in front of the file if the result should not be
860 # written to the html output directory.
861
862 CHM_FILE =
863
864 # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
865 # be used to specify the location (absolute path including file name) of
866 # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
867 # the HTML help compiler on the generated index.hhp.
868
869 HHC_LOCATION =
870
871 # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
872 # controls if a separate .chi index file is generated (YES) or that
873 # it should be included in the master .chm file (NO).
874
875 GENERATE_CHI = NO
876
877 # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
878 # is used to encode HtmlHelp index (hhk), content (hhc) and project file
879 # content.
880
881 CHM_INDEX_ENCODING =
882
883 # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
884 # controls whether a binary table of contents is generated (YES) or a
885 # normal table of contents (NO) in the .chm file.
886
887 BINARY_TOC = NO
888
889 # The TOC_EXPAND flag can be set to YES to add extra items for group members
890 # to the contents of the HTML help documentation and to the tree view.
891
892 TOC_EXPAND = NO
893
894 # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
895 # are set, an additional index file will be generated that can be used as input for
896 # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
897 # HTML documentation.
898
899 GENERATE_QHP = NO
900
901 # If the QHG_LOCATION tag is specified, the QCH_FILE tag can
902 # be used to specify the file name of the resulting .qch file.
903 # The path specified is relative to the HTML output folder.
904
905 QCH_FILE =
906
907 # The QHP_NAMESPACE tag specifies the namespace to use when generating
908 # Qt Help Project output. For more information please see
909 # http://doc.trolltech.com/qthelpproject.html#namespace
910
911 QHP_NAMESPACE = org.doxygen.Project
912
913 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
914 # Qt Help Project output. For more information please see
915 # http://doc.trolltech.com/qthelpproject.html#virtual-folders
916
917 QHP_VIRTUAL_FOLDER = doc
918
919 # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
920 # For more information please see
921 # http://doc.trolltech.com/qthelpproject.html#custom-filters
922
923 QHP_CUST_FILTER_NAME =
924
925 # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
926 # <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
927
928 QHP_CUST_FILTER_ATTRS =
929
930 # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
931 # filter section matches.
932 # <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
933
934 QHP_SECT_FILTER_ATTRS =
935
936 # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
937 # be used to specify the location of Qt's qhelpgenerator.
938 # If non-empty doxygen will try to run qhelpgenerator on the generated
939 # .qhp file.
940
941 QHG_LOCATION =
942
943 # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
944 # will be generated, which together with the HTML files, form an Eclipse help
945 # plugin. To install this plugin and make it available under the help contents
946 # menu in Eclipse, the contents of the directory containing the HTML and XML
947 # files needs to be copied into the plugins directory of eclipse. The name of
948 # the directory within the plugins directory should be the same as
949 # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears.
950
951 GENERATE_ECLIPSEHELP = NO
952
953 # A unique identifier for the eclipse help plugin. When installing the plugin
954 # the directory name containing the HTML and XML files should also have
955 # this name.
956
957 ECLIPSE_DOC_ID = org.doxygen.Project
958
959 # The DISABLE_INDEX tag can be used to turn on/off the condensed index at
960 # top of each HTML page. The value NO (the default) enables the index and
961 # the value YES disables it.
962
963 DISABLE_INDEX = NO
964
965 # This tag can be used to set the number of enum values (range [1..20])
966 # that doxygen will group on one line in the generated HTML documentation.
967
968 ENUM_VALUES_PER_LINE = 4
969
970 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
971 # structure should be generated to display hierarchical information.
972 # If the tag value is set to YES, a side panel will be generated
973 # containing a tree-like index structure (just like the one that
974 # is generated for HTML Help). For this to work a browser that supports
975 # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
976 # Windows users are probably better off using the HTML help feature.
977
978 GENERATE_TREEVIEW = NO
979
980 # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
981 # and Class Hierarchy pages using a tree view instead of an ordered list.
982
983 USE_INLINE_TREES = NO
984
985 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
986 # used to set the initial width (in pixels) of the frame in which the tree
987 # is shown.
988
989 TREEVIEW_WIDTH = 250
990
991 # Use this tag to change the font size of Latex formulas included
992 # as images in the HTML documentation. The default is 10. Note that
993 # when you change the font size after a successful doxygen run you need
994 # to manually remove any form_*.png images from the HTML output directory
995 # to force them to be regenerated.
996
997 FORMULA_FONTSIZE = 10
998
999 # When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
1000 # and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should
1001 # typically be disabled. For large projects the javascript based search engine
1002 # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
1003
1004 SEARCHENGINE = YES
1005
1006 # When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index
1007 # file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup
1008 # and does not have live searching capabilities.
1009
1010 SERVER_BASED_SEARCH = NO
1011
1012 #---------------------------------------------------------------------------
1013 # configuration options related to the LaTeX output
1014 #---------------------------------------------------------------------------
1015
1016 # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
1017 # generate Latex output.
1018
1019 GENERATE_LATEX = YES
1020
1021 # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
1022 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1023 # put in front of it. If left blank `latex' will be used as the default path.
1024
1025 LATEX_OUTPUT = latex
1026
1027 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
1028 # invoked. If left blank `latex' will be used as the default command name.
1029 # Note that when enabling USE_PDFLATEX this option is only used for
1030 # generating bitmaps for formulas in the HTML output, but not in the
1031 # Makefile that is written to the output directory.
1032
1033 LATEX_CMD_NAME = latex
1034
1035 # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
1036 # generate index for LaTeX. If left blank `makeindex' will be used as the
1037 # default command name.
1038
1039 MAKEINDEX_CMD_NAME = makeindex
1040
1041 # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
1042 # LaTeX documents. This may be useful for small projects and may help to
1043 # save some trees in general.
1044
1045 COMPACT_LATEX = NO
1046
1047 # The PAPER_TYPE tag can be used to set the paper type that is used
1048 # by the printer. Possible values are: a4, a4wide, letter, legal and
1049 # executive. If left blank a4wide will be used.
1050
1051 PAPER_TYPE = a4wide
1052
1053 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
1054 # packages that should be included in the LaTeX output.
1055
1056 EXTRA_PACKAGES =
1057
1058 # The LATEX_HEADER tag can be used to specify a personal LaTeX header for
1059 # the generated latex document. The header should contain everything until
1060 # the first chapter. If it is left blank doxygen will generate a
1061 # standard header. Notice: only use this tag if you know what you are doing!
1062
1063 LATEX_HEADER =
1064
1065 # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
1066 # is prepared for conversion to pdf (using ps2pdf). The pdf file will
1067 # contain links (just like the HTML output) instead of page references
1068 # This makes the output suitable for online browsing using a pdf viewer.
1069
1070 PDF_HYPERLINKS = YES
1071
1072 # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
1073 # plain latex in the generated Makefile. Set this option to YES to get a
1074 # higher quality PDF documentation.
1075
1076 USE_PDFLATEX = YES
1077
1078 # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
1079 # command to the generated LaTeX files. This will instruct LaTeX to keep
1080 # running if errors occur, instead of asking the user for help.
1081 # This option is also used when generating formulas in HTML.
1082
1083 LATEX_BATCHMODE = NO
1084
1085 # If LATEX_HIDE_INDICES is set to YES then doxygen will not
1086 # include the index chapters (such as File Index, Compound Index, etc.)
1087 # in the output.
1088
1089 LATEX_HIDE_INDICES = NO
1090
1091 # If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
1092
1093 LATEX_SOURCE_CODE = NO
1094
1095 #---------------------------------------------------------------------------
1096 # configuration options related to the RTF output
1097 #---------------------------------------------------------------------------
1098
1099 # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
1100 # The RTF output is optimized for Word 97 and may not look very pretty with
1101 # other RTF readers or editors.
1102
1103 GENERATE_RTF = NO
1104
1105 # The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
1106 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1107 # put in front of it. If left blank `rtf' will be used as the default path.
1108
1109 RTF_OUTPUT = rtf
1110
1111 # If the COMPACT_RTF tag is set to YES Doxygen generates more compact
1112 # RTF documents. This may be useful for small projects and may help to
1113 # save some trees in general.
1114
1115 COMPACT_RTF = NO
1116
1117 # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
1118 # will contain hyperlink fields. The RTF file will
1119 # contain links (just like the HTML output) instead of page references.
1120 # This makes the output suitable for online browsing using WORD or other
1121 # programs which support those fields.
1122 # Note: wordpad (write) and others do not support links.
1123
1124 RTF_HYPERLINKS = NO
1125
1126 # Load stylesheet definitions from file. Syntax is similar to doxygen's
1127 # config file, i.e. a series of assignments. You only have to provide
1128 # replacements, missing definitions are set to their default value.
1129
1130 RTF_STYLESHEET_FILE =
1131
1132 # Set optional variables used in the generation of an rtf document.
1133 # Syntax is similar to doxygen's config file.
1134
1135 RTF_EXTENSIONS_FILE =
1136
1137 #---------------------------------------------------------------------------
1138 # configuration options related to the man page output
1139 #---------------------------------------------------------------------------
1140
1141 # If the GENERATE_MAN tag is set to YES (the default) Doxygen will
1142 # generate man pages
1143
1144 GENERATE_MAN = NO
1145
1146 # The MAN_OUTPUT tag is used to specify where the man pages will be put.
1147 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1148 # put in front of it. If left blank `man' will be used as the default path.
1149
1150 MAN_OUTPUT = man
1151
1152 # The MAN_EXTENSION tag determines the extension that is added to
1153 # the generated man pages (default is the subroutine's section .3)
1154
1155 MAN_EXTENSION = .3
1156
1157 # If the MAN_LINKS tag is set to YES and Doxygen generates man output,
1158 # then it will generate one additional man file for each entity
1159 # documented in the real man page(s). These additional files
1160 # only source the real man page, but without them the man command
1161 # would be unable to find the correct page. The default is NO.
1162
1163 MAN_LINKS = NO
1164
1165 #---------------------------------------------------------------------------
1166 # configuration options related to the XML output
1167 #---------------------------------------------------------------------------
1168
1169 # If the GENERATE_XML tag is set to YES Doxygen will
1170 # generate an XML file that captures the structure of
1171 # the code including all documentation.
1172
1173 GENERATE_XML = NO
1174
1175 # The XML_OUTPUT tag is used to specify where the XML pages will be put.
1176 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1177 # put in front of it. If left blank `xml' will be used as the default path.
1178
1179 XML_OUTPUT = xml
1180
1181 # The XML_SCHEMA tag can be used to specify an XML schema,
1182 # which can be used by a validating XML parser to check the
1183 # syntax of the XML files.
1184
1185 XML_SCHEMA =
1186
1187 # The XML_DTD tag can be used to specify an XML DTD,
1188 # which can be used by a validating XML parser to check the
1189 # syntax of the XML files.
1190
1191 XML_DTD =
1192
1193 # If the XML_PROGRAMLISTING tag is set to YES Doxygen will
1194 # dump the program listings (including syntax highlighting
1195 # and cross-referencing information) to the XML output. Note that
1196 # enabling this will significantly increase the size of the XML output.
1197
1198 XML_PROGRAMLISTING = YES
1199
1200 #---------------------------------------------------------------------------
1201 # configuration options for the AutoGen Definitions output
1202 #---------------------------------------------------------------------------
1203
1204 # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
1205 # generate an AutoGen Definitions (see autogen.sf.net) file
1206 # that captures the structure of the code including all
1207 # documentation. Note that this feature is still experimental
1208 # and incomplete at the moment.
1209
1210 GENERATE_AUTOGEN_DEF = NO
1211
1212 #---------------------------------------------------------------------------
1213 # configuration options related to the Perl module output
1214 #---------------------------------------------------------------------------
1215
1216 # If the GENERATE_PERLMOD tag is set to YES Doxygen will
1217 # generate a Perl module file that captures the structure of
1218 # the code including all documentation. Note that this
1219 # feature is still experimental and incomplete at the
1220 # moment.
1221
1222 GENERATE_PERLMOD = NO
1223
1224 # If the PERLMOD_LATEX tag is set to YES Doxygen will generate
1225 # the necessary Makefile rules, Perl scripts and LaTeX code to be able
1226 # to generate PDF and DVI output from the Perl module output.
1227
1228 PERLMOD_LATEX = NO
1229
1230 # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
1231 # nicely formatted so it can be parsed by a human reader.
1232 # This is useful
1233 # if you want to understand what is going on.
1234 # On the other hand, if this
1235 # tag is set to NO the size of the Perl module output will be much smaller
1236 # and Perl will parse it just the same.
1237
1238 PERLMOD_PRETTY = YES
1239
1240 # The names of the make variables in the generated doxyrules.make file
1241 # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
1242 # This is useful so different doxyrules.make files included by the same
1243 # Makefile don't overwrite each other's variables.
1244
1245 PERLMOD_MAKEVAR_PREFIX =
1246
1247 #---------------------------------------------------------------------------
1248 # Configuration options related to the preprocessor
1249 #---------------------------------------------------------------------------
1250
1251 # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
1252 # evaluate all C-preprocessor directives found in the sources and include
1253 # files.
1254
1255 ENABLE_PREPROCESSING = YES
1256
1257 # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
1258 # names in the source code. If set to NO (the default) only conditional
1259 # compilation will be performed. Macro expansion can be done in a controlled
1260 # way by setting EXPAND_ONLY_PREDEF to YES.
1261
1262 MACRO_EXPANSION = NO
1263
1264 # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
1265 # then the macro expansion is limited to the macros specified with the
1266 # PREDEFINED and EXPAND_AS_DEFINED tags.
1267
1268 EXPAND_ONLY_PREDEF = NO
1269
1270 # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
1271 # in the INCLUDE_PATH (see below) will be search if a #include is found.
1272
1273 SEARCH_INCLUDES = YES
1274
1275 # The INCLUDE_PATH tag can be used to specify one or more directories that
1276 # contain include files that are not input files but should be processed by
1277 # the preprocessor.
1278
1279 INCLUDE_PATH =
1280
1281 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
1282 # patterns (like *.h and *.hpp) to filter out the header-files in the
1283 # directories. If left blank, the patterns specified with FILE_PATTERNS will
1284 # be used.
1285
1286 INCLUDE_FILE_PATTERNS =
1287
1288 # The PREDEFINED tag can be used to specify one or more macro names that
1289 # are defined before the preprocessor is started (similar to the -D option of
1290 # gcc). The argument of the tag is a list of macros of the form: name
1291 # or name=definition (no spaces). If the definition and the = are
1292 # omitted =1 is assumed. To prevent a macro definition from being
1293 # undefined via #undef or recursively expanded use the := operator
1294 # instead of the = operator.
1295
1296 PREDEFINED =
1297
1298 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
1299 # this tag can be used to specify a list of macro names that should be expanded.
1300 # The macro definition that is found in the sources will be used.
1301 # Use the PREDEFINED tag if you want to use a different macro definition.
1302
1303 EXPAND_AS_DEFINED =
1304
1305 # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
1306 # doxygen's preprocessor will remove all function-like macros that are alone
1307 # on a line, have an all uppercase name, and do not end with a semicolon. Such
1308 # function macros are typically used for boiler-plate code, and will confuse
1309 # the parser if not removed.
1310
1311 SKIP_FUNCTION_MACROS = YES
1312
1313 #---------------------------------------------------------------------------
1314 # Configuration::additions related to external references
1315 #---------------------------------------------------------------------------
1316
1317 # The TAGFILES option can be used to specify one or more tagfiles.
1318 # Optionally an initial location of the external documentation
1319 # can be added for each tagfile. The format of a tag file without
1320 # this location is as follows:
1321 #
1322 # TAGFILES = file1 file2 ...
1323 # Adding location for the tag files is done as follows:
1324 #
1325 # TAGFILES = file1=loc1 "file2 = loc2" ...
1326 # where "loc1" and "loc2" can be relative or absolute paths or
1327 # URLs. If a location is present for each tag, the installdox tool
1328 # does not have to be run to correct the links.
1329 # Note that each tag file must have a unique name
1330 # (where the name does NOT include the path)
1331 # If a tag file is not located in the directory in which doxygen
1332 # is run, you must also specify the path to the tagfile here.
1333
1334 TAGFILES =
1335
1336 # When a file name is specified after GENERATE_TAGFILE, doxygen will create
1337 # a tag file that is based on the input files it reads.
1338
1339 GENERATE_TAGFILE =
1340
1341 # If the ALLEXTERNALS tag is set to YES all external classes will be listed
1342 # in the class index. If set to NO only the inherited external classes
1343 # will be listed.
1344
1345 ALLEXTERNALS = NO
1346
1347 # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
1348 # in the modules index. If set to NO, only the current project's groups will
1349 # be listed.
1350
1351 EXTERNAL_GROUPS = YES
1352
1353 # The PERL_PATH should be the absolute path and name of the perl script
1354 # interpreter (i.e. the result of `which perl').
1355
1356 PERL_PATH = /usr/bin/perl
1357
1358 #---------------------------------------------------------------------------
1359 # Configuration options related to the dot tool
1360 #---------------------------------------------------------------------------
1361
1362 # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
1363 # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
1364 # or super classes. Setting the tag to NO turns the diagrams off. Note that
1365 # this option is superseded by the HAVE_DOT option below. This is only a
1366 # fallback. It is recommended to install and use dot, since it yields more
1367 # powerful graphs.
1368
1369 CLASS_DIAGRAMS = YES
1370
1371 # You can define message sequence charts within doxygen comments using the \msc
1372 # command. Doxygen will then run the mscgen tool (see
1373 # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
1374 # documentation. The MSCGEN_PATH tag allows you to specify the directory where
1375 # the mscgen tool resides. If left empty the tool is assumed to be found in the
1376 # default search path.
1377
1378 MSCGEN_PATH =
1379
1380 # If set to YES, the inheritance and collaboration graphs will hide
1381 # inheritance and usage relations if the target is undocumented
1382 # or is not a class.
1383
1384 HIDE_UNDOC_RELATIONS = YES
1385
1386 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
1387 # available from the path. This tool is part of Graphviz, a graph visualization
1388 # toolkit from AT&T and Lucent Bell Labs. The other options in this section
1389 # have no effect if this option is set to NO (the default)
1390
1391 HAVE_DOT = NO
1392
1393 # By default doxygen will write a font called FreeSans.ttf to the output
1394 # directory and reference it in all dot files that doxygen generates. This
1395 # font does not include all possible unicode characters however, so when you need
1396 # these (or just want a differently looking font) you can specify the font name
1397 # using DOT_FONTNAME. You need need to make sure dot is able to find the font,
1398 # which can be done by putting it in a standard location or by setting the
1399 # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
1400 # containing the font.
1401
1402 DOT_FONTNAME = FreeSans
1403
1404 # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
1405 # The default size is 10pt.
1406
1407 DOT_FONTSIZE = 10
1408
1409 # By default doxygen will tell dot to use the output directory to look for the
1410 # FreeSans.ttf font (which doxygen will put there itself). If you specify a
1411 # different font using DOT_FONTNAME you can set the path where dot
1412 # can find it using this tag.
1413
1414 DOT_FONTPATH =
1415
1416 # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
1417 # will generate a graph for each documented class showing the direct and
1418 # indirect inheritance relations. Setting this tag to YES will force the
1419 # the CLASS_DIAGRAMS tag to NO.
1420
1421 CLASS_GRAPH = YES
1422
1423 # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
1424 # will generate a graph for each documented class showing the direct and
1425 # indirect implementation dependencies (inheritance, containment, and
1426 # class references variables) of the class with other documented classes.
1427
1428 COLLABORATION_GRAPH = YES
1429
1430 # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
1431 # will generate a graph for groups, showing the direct groups dependencies
1432
1433 GROUP_GRAPHS = YES
1434
1435 # If the UML_LOOK tag is set to YES doxygen will generate inheritance and
1436 # collaboration diagrams in a style similar to the OMG's Unified Modeling
1437 # Language.
1438
1439 UML_LOOK = NO
1440
1441 # If set to YES, the inheritance and collaboration graphs will show the
1442 # relations between templates and their instances.
1443
1444 TEMPLATE_RELATIONS = NO
1445
1446 # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
1447 # tags are set to YES then doxygen will generate a graph for each documented
1448 # file showing the direct and indirect include dependencies of the file with
1449 # other documented files.
1450
1451 INCLUDE_GRAPH = YES
1452
1453 # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
1454 # HAVE_DOT tags are set to YES then doxygen will generate a graph for each
1455 # documented header file showing the documented files that directly or
1456 # indirectly include this file.
1457
1458 INCLUDED_BY_GRAPH = YES
1459
1460 # If the CALL_GRAPH and HAVE_DOT options are set to YES then
1461 # doxygen will generate a call dependency graph for every global function
1462 # or class method. Note that enabling this option will significantly increase
1463 # the time of a run. So in most cases it will be better to enable call graphs
1464 # for selected functions only using the \callgraph command.
1465
1466 CALL_GRAPH = NO
1467
1468 # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
1469 # doxygen will generate a caller dependency graph for every global function
1470 # or class method. Note that enabling this option will significantly increase
1471 # the time of a run. So in most cases it will be better to enable caller
1472 # graphs for selected functions only using the \callergraph command.
1473
1474 CALLER_GRAPH = NO
1475
1476 # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
1477 # will graphical hierarchy of all classes instead of a textual one.
1478
1479 GRAPHICAL_HIERARCHY = YES
1480
1481 # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
1482 # then doxygen will show the dependencies a directory has on other directories
1483 # in a graphical way. The dependency relations are determined by the #include
1484 # relations between the files in the directories.
1485
1486 DIRECTORY_GRAPH = YES
1487
1488 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
1489 # generated by dot. Possible values are png, jpg, or gif
1490 # If left blank png will be used.
1491
1492 DOT_IMAGE_FORMAT = png
1493
1494 # The tag DOT_PATH can be used to specify the path where the dot tool can be
1495 # found. If left blank, it is assumed the dot tool can be found in the path.
1496
1497 DOT_PATH =
1498
1499 # The DOTFILE_DIRS tag can be used to specify one or more directories that
1500 # contain dot files that are included in the documentation (see the
1501 # \dotfile command).
1502
1503 DOTFILE_DIRS =
1504
1505 # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
1506 # nodes that will be shown in the graph. If the number of nodes in a graph
1507 # becomes larger than this value, doxygen will truncate the graph, which is
1508 # visualized by representing a node as a red box. Note that doxygen if the
1509 # number of direct children of the root node in a graph is already larger than
1510 # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
1511 # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
1512
1513 DOT_GRAPH_MAX_NODES = 50
1514
1515 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
1516 # graphs generated by dot. A depth value of 3 means that only nodes reachable
1517 # from the root by following a path via at most 3 edges will be shown. Nodes
1518 # that lay further from the root node will be omitted. Note that setting this
1519 # option to 1 or 2 may greatly reduce the computation time needed for large
1520 # code bases. Also note that the size of a graph can be further restricted by
1521 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
1522
1523 MAX_DOT_GRAPH_DEPTH = 0
1524
1525 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
1526 # background. This is disabled by default, because dot on Windows does not
1527 # seem to support this out of the box. Warning: Depending on the platform used,
1528 # enabling this option may lead to badly anti-aliased labels on the edges of
1529 # a graph (i.e. they become hard to read).
1530
1531 DOT_TRANSPARENT = NO
1532
1533 # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
1534 # files in one run (i.e. multiple -o and -T options on the command line). This
1535 # makes dot run faster, but since only newer versions of dot (>1.8.10)
1536 # support this, this feature is disabled by default.
1537
1538 DOT_MULTI_TARGETS = YES
1539
1540 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
1541 # generate a legend page explaining the meaning of the various boxes and
1542 # arrows in the dot generated graphs.
1543
1544 GENERATE_LEGEND = YES
1545
1546 # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
1547 # remove the intermediate dot files that are used to generate
1548 # the various graphs.
1549
1550 DOT_CLEANUP = YES
0 cmake_policy(SET CMP0007 OLD)
1 if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
2 message(FATAL_ERROR "Cannot find install manifest:
3 \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
4 endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
5
6 file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
7 string(REGEX REPLACE "\n" ";" files "${files}")
8 list(REVERSE files)
9 foreach (file ${files})
10 message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
11 if (EXISTS "$ENV{DESTDIR}${file}")
12 execute_process(
13 COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}"
14 OUTPUT_VARIABLE rm_out
15 RESULT_VARIABLE rm_retval
16 )
17 if(NOT ${rm_retval} EQUAL 0)
18 message(FATAL_ERROR "Problem when removing
19 \"$ENV{DESTDIR}${file}\"")
20 endif (NOT ${rm_retval} EQUAL 0)
21 else (EXISTS "$ENV{DESTDIR}${file}")
22 message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
23 endif (EXISTS "$ENV{DESTDIR}${file}")
24 endforeach(file)
25
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #ifndef ROSPACK_MACROS_H_
28 #define ROSPACK_MACROS_H_
29
30 /*
31 * Windows macros for dll exports
32 *
33 * This replicates what ros has in roscpp_core/cpp_common/macros.h
34 * so rospack can be standalone.
35 *
36 * See also http://gcc.gnu.org/wiki/Visibility
37 */
38
39 #if defined(_MSC_VER)
40 #define ROS_HELPER_IMPORT __declspec(dllimport)
41 #define ROS_HELPER_EXPORT __declspec(dllexport)
42 #define ROS_HELPER_LOCAL
43 #elif __GNUC__ >= 4
44 #define ROS_HELPER_IMPORT __attribute__ ((visibility("default")))
45 #define ROS_HELPER_EXPORT __attribute__ ((visibility("default")))
46 #define ROS_HELPER_LOCAL __attribute__ ((visibility("hidden")))
47 #else
48 #define ROS_HELPER_IMPORT
49 #define ROS_HELPER_EXPORT
50 #define ROS_HELPER_LOCAL
51 #endif
52
53 // Ignore warnings about import/exports when deriving from std classes.
54 #ifdef _MSC_VER
55 #pragma warning(disable:4251)
56 #pragma warning(disable:4275)
57 #pragma warning(disable:4514) // unreferenced inline function has been removed
58 #pragma warning(disable:4668) // warnigns about used, but not defined macros, replacing with 0
59 #pragma warning(disable:4710) // function not inlined
60 #pragma warning(disable:4738) // storing floats, memory splitting, possible loss in performance
61 #pragma warning(disable:4820) // structs and padding
62 #endif
63
64 #ifdef ROS_BUILD_STATIC_LIBS // ros is being built around shared libraries
65 #define ROSPACK_DECL
66 #else // ros is being built around shared libraries
67 #ifdef rospack_EXPORTS // we are building a shared lib/dll
68 #define ROSPACK_DECL ROS_HELPER_EXPORT
69 #else // we are using shared lib/dll
70 #define ROSPACK_DECL ROS_HELPER_IMPORT
71 #endif
72 #endif
73
74 #endif /* ROSPACK_MACROS_H_ */
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 /**
28
29 \mainpage
30
31 \section overview Overview
32
33 \b librospack is the ROS stackage (stack/package) management library.
34
35 librospack is part dpkg, part pkg-config. The main function of librospack is
36 to crawl through the stackages in ROS_ROOT and ROS_PACKAGE_PATH, read and
37 parse the \b manifest for each stackage, and assemble a complete
38 dependency tree for all stackages.
39
40 Using this tree, librospack can answer a number of queries about stackages and
41 their dependencies. Common queries include:
42 - find : return the absolute path to a stackage
43 - depends : return a list of all of a stackage's dependencies
44 - depends-on : return a list of stackages that depend on the given stackage
45 - export : return flags necessary for building and linking against a package
46
47 librospack is intended to be cross-platform.
48
49 \subsection crawling Crawling algorithm
50 librospack crawls in the following order: the directory ROS_ROOT, followed by
51 the colon-separated list of directories ROS_PACKAGE_PATH, in the order they
52 are listed.
53
54 During the crawl, librospack examines the contents of each directory, looking
55 for a file called @b manifest.xml (for packages) or @b stack.xml (for stacks).
56 If such a file is found, the directory
57 containing it is considered to be a ROS stackage, with the stackage name
58 equal to the directory name. The crawl does not descend further once a
59 manifest is found (i.e., stackages cannot be nested inside one another).
60
61 If a manifest is not found in a given directory, each subdirectory
62 is searched. This subdirectory search is prevented if a file called @b
63 rospack_nosubdirs is found. The directory itself is still searched for a
64 manifest, but its subdirectories are not crawled.
65
66 If multiple stackages by the same name exist within the search path, the
67 first one found wins. It is strongly recommended that you keep stackages by
68 the same name in separate trees, each having its own element within
69 ROS_PACKAGE_PATH. That way, you can deterministically control the search
70 order by the way that you specify ROS_PACKAGE_PATH. The search order
71 within a given element of ROS_PACKAGE_PATH can be unpredictably affected by
72 the details of how files are laid out on disk.
73
74 \subsection efficiency Efficiency considerations
75 librospack re-parses the manifest files and rebuilds the dependency tree
76 on each execution. However, it maintains a cache of stackage directories in
77 ROS_HOME/rospack_cache (or ROS_HOME/rosstack_cache).
78 This cache is updated whenever there is a cache
79 miss, or when the cache is 60 seconds old. You can change this timeout by
80 setting the environment variable ROS_CACHE_TIMEOUT, in seconds. Set it to
81 0.0 to force a cache rebuild on every invocation of librospack.
82
83 librospack's performance can be adversely affected by the presence of very
84 broad and/or deep directory structures that don't contain manifest files.
85 If such directories are in librospack's search path, it can spend a lot of
86 time crawling them only to discover that there are no stackages to be found.
87 You can prevent this latency by creating a @b rospack_nosubdirs file in
88 such directories. If librospack seems to be running annoyingly slowly, you
89 can call profile(), which will print out the slowest trees
90 to crawl.
91
92 \subsection dependencies Minimal dependencies
93 Because librospack is the tool that determines dependencies, it must
94 have minimal dependencies. librospack contains a copy of the TinyXML library,
95 and depends on Boost. If gtest is available, then tests can be run.
96
97 \section codeapi Code API
98
99 librospack is used primarily in command-line tools (rospack and rosstack),
100 but can be used in other contexts. See the API documentation for Rospack
101 and Rosstack.
102 */
103
104 #ifndef ROSPACK_ROSPACK_H
105 #define ROSPACK_ROSPACK_H
106
107 #include <boost/tr1/unordered_set.hpp>
108 #include <boost/tr1/unordered_map.hpp>
109 #include <list>
110 #include <map>
111 #include <set>
112 #include <string>
113 #include <vector>
114 #include "macros.h"
115
116 //#ifdef ROSPACK_API_BACKCOMPAT_V1
117 #if 1 // def ROSPACK_API_BACKCOMPAT_V1
118 #include "rospack/rospack_backcompat.h"
119 #endif
120
121
122 namespace rospack
123 {
124
125 typedef enum
126 {
127 POSTORDER,
128 PREORDER
129 } traversal_order_t;
130
131 // Forward declarations
132 class Stackage;
133 class DirectoryCrawlRecord;
134
135 /**
136 * @brief The base class for package/stack ("stackage") crawlers. Users of the library should
137 * use the functionality provided here through one of the derived classes,
138 * Rosstack or Rospack.
139 */
140 class ROSPACK_DECL Rosstackage
141 {
142 private:
143 std::string manifest_name_;
144 std::string cache_name_;
145 bool crawled_;
146 std::string name_;
147 std::string tag_;
148 bool quiet_;
149 std::vector<std::string> search_paths_;
150 std::tr1::unordered_map<std::string, std::vector<std::string> > dups_;
151 std::tr1::unordered_map<std::string, Stackage*> stackages_;
152 Stackage* findWithRecrawl(const std::string& name);
153 void log(const std::string& level, const std::string& msg, bool append_errno);
154 void addStackage(const std::string& path);
155 void crawlDetail(const std::string& path,
156 bool force,
157 int depth,
158 bool collect_profile_data,
159 std::vector<DirectoryCrawlRecord*>& profile_data,
160 std::tr1::unordered_set<std::string>& profile_hash);
161 bool depsOnDetail(const std::string& name, bool direct,
162 std::vector<Stackage*>& deps, bool ignore_missing=false);
163 bool depsDetail(const std::string& name, bool direct,
164 std::vector<Stackage*>& deps);
165 bool isStackage(const std::string& path);
166 void loadManifest(Stackage* stackage);
167 void computeDeps(Stackage* stackage, bool ignore_errors=false, bool ignore_missing=false);
168 void computeDepsInternal(Stackage* stackage, bool ignore_errors, const std::string& depend_tag, bool ignore_missing=false);
169 bool isSysPackage(const std::string& pkgname);
170 void gatherDeps(Stackage* stackage, bool direct,
171 traversal_order_t order,
172 std::vector<Stackage*>& deps,
173 bool no_recursion_on_wet=false);
174 void gatherDepsFull(Stackage* stackage, bool direct,
175 traversal_order_t order, int depth,
176 std::tr1::unordered_set<Stackage*>& deps_hash,
177 std::vector<Stackage*>& deps,
178 bool get_indented_deps,
179 std::vector<std::string>& indented_deps,
180 bool no_recursion_on_wet=false);
181 std::string getCachePath();
182 bool readCache();
183 void writeCache();
184 FILE* validateCache();
185 bool expandExportString(Stackage* stackage,
186 const std::string& instring,
187 std::string& outstring);
188 void depsWhyDetail(Stackage* from,
189 Stackage* to,
190 std::list<std::list<Stackage*> >& acc_list);
191
192 void initPython();
193
194 protected:
195 /**
196 * @brief Constructor, only used by derived classes.
197 * @param manifest_name What the manifest is called (e.g., "manifest.xml or stack.xml")
198 * @param cache_name What the cache is called (e.g., "rospack_cache" or "rosstack_cache")
199 * @param name Name of the tool we're building (e.g., "rospack" or "rosstack")
200 * @param tag Name of the attribute we look for in a "depend" tag in a
201 * manifest (e.g., "package" or "stack")
202 */
203 Rosstackage(const std::string& manifest_name,
204 const std::string& cache_name,
205 const std::string& name,
206 const std::string& tag);
207
208 public:
209 /**
210 * @brief Destructor.
211 */
212 virtual ~Rosstackage();
213
214 /**
215 * @brief Usage string, to be overridden by derived classes.
216 * @return Command-line usage statement.
217 */
218 virtual const char* usage() { return ""; }
219 /**
220 * @brief Crawl the filesystem, accumulating a database of
221 * stackages. May read results from a cache file instead
222 * of crawling. This method should be called before any
223 * making any queries (find, list, etc.).
224 * @param search_path List of directories to crawl, in precenence
225 * order. Directories should be absolute paths.
226 * It's passed by value to allow callers (e.g.,
227 * find()) to safely pass in search_paths_.
228 * @param force If true, then crawl even if the cache looks valid
229 */
230 void crawl(std::vector<std::string> search_path, bool force);
231 /**
232 * @brief Is the current working directory a stackage?
233 * @param name If in a stackage, then the stackage's name is written here.
234 * @return True if the current working directory contains a manifest
235 * file
236 */
237 bool inStackage(std::string& name);
238 /**
239 * @brief Control warning and error console output.
240 * @param quiet If true, then don't output any warnings or errors to
241 * console. If false, then output warnings and errors to
242 * stderr (default behavior).
243 */
244 void setQuiet(bool quiet);
245 /**
246 * @brief Get the name of the tool that's in use (e.g., "rospack" or "rosstack")
247 * @return The name of the tool.
248 */
249 const std::string& getName() {return name_;}
250 /**
251 * @brief Helper method to construct a directory search path by looking
252 * at relevant environment variables. The value of ROS_ROOT goes
253 * first, followed by each element of a colon-separated
254 * ROS_PACKAGE_PATH.
255 * @param sp The computed search path is written here.
256 * @return True if a search path was computed, false otherwise (e.g., ROS_ROOT not set).
257 */
258 bool getSearchPathFromEnv(std::vector<std::string>& sp);
259 /**
260 * @brief Look for a stackage.
261 * @param name The stackage to look for.
262 * @param path If found, the absolute path to the stackage is written here.
263 * @return True if the stackage is found, false otherwise.
264 */
265 bool find(const std::string& name, std::string& path);
266 /**
267 * @brief Compute the packages that are contained in a stack.
268 * @param name The stack to work on.
269 * @param packages The stack's constituent packages are written here.
270 * @return True if the contents could be computed, false otherwise.
271 */
272 bool contents(const std::string& name, std::set<std::string>& packages);
273 /**
274 * @brief Find the stack that contains a package.
275 * @param name The package to work on.
276 * @param stack If the containing stack is found, its name is written here.
277 * @param path If the containing stack is found, its absolute path is written here.
278 * @return True if the containing stack could be found, false otherwise.
279 */
280 bool contains(const std::string& name,
281 std::string& stack,
282 std::string& path);
283
284 /**
285 * @brief List names and paths of all stackages.
286 * @param list Pairs of (name,path) are written here.
287 */
288 void list(std::set<std::pair<std::string, std::string> >& list);
289 /**
290 * @brief Identify duplicate stackages. Forces crawl.
291 * @param dups Names of stackages that are found more than once while
292 * crawling are written here.
293 */
294 void listDuplicates(std::vector<std::string>& dups);
295 /**
296 * @brief Identify duplicate stackages and provide their paths. Forces crawl.
297 * @param dups Names of stackages that are found more than once while
298 * crawling are mapped to the found paths of these packages.
299 */
300 void listDuplicatesWithPaths(std::map<std::string, std::vector<std::string> >& dups);
301 /**
302 * @brief Compute dependencies of a stackage (i.e., stackages that this
303 * stackages depends on).
304 * @param name The stackage to work on.
305 * @param direct If true, then compute only direct dependencies. If
306 * false, then compute full (including indirect)
307 * dependencies.
308 * @param deps If dependencies are computed, then they're written here.
309 * @return True if dependencies were computed, false otherwise.
310 */
311 bool deps(const std::string& name, bool direct, std::vector<std::string>& deps);
312 /**
313 * @brief Compute reverse dependencies of a stackage (i.e., stackages
314 * that depend on this stackage). Forces crawl.
315 * @param name The stackage to work on.
316 * @param direct If true, then compute only direct dependencies. If
317 * false, then compute full (including indirect)
318 * dependencies.
319 * @param deps If dependencies are computed, then they're written here.
320 * @return True if dependencies were computed, false otherwise.
321 */
322 bool depsOn(const std::string& name, bool direct,
323 std::vector<std::string>& deps);
324 /**
325 * @brief List the manifests of a stackage's dependencies. Used by
326 * rosbuild.
327 * @param name The stackage to work on.
328 * @param direct If true, then compute only direct dependencies. If
329 * false, then compute full (including indirect)
330 * dependencies.
331 * @param manifests The list of absolute paths to manifests of stackages
332 * that the given stackage depends on is written here.
333 * @return True if the manifest list was computed, false otherwise.
334 */
335 bool depsManifests(const std::string& name, bool direct,
336 std::vector<std::string>& manifests);
337 /**
338 * @brief List the marker files in a packages's dependencies that
339 * indicate that those packages contain auto-generated message
340 * and/or service code. Used by rosbuild.
341 * @param name The package to work on.
342 * @param direct If true, then compute only direct dependencies. If
343 * false, then compute full (including indirect)
344 * dependencies.
345 * @param gens The list of absolute paths to marker files (e.g.,
346 * "/opt/ros/electric/stacks/ros_comm/messages/std_msgs/msg_gen/generated")
347 * is written here.
348 * @return True if the list of files was generated, false otherwise.
349 */
350 bool depsMsgSrv(const std::string& name, bool direct,
351 std::vector<std::string>& gens);
352 /**
353 * @brief Generate indented list of a stackage's dependencies,
354 * including duplicates. Intended for visual debugging of dependency
355 * structures.
356 * @param name The stackage to work on.
357 * @param direct If true, then compute only direct dependencies. If
358 * false, then compute full (including indirect)
359 * dependencies.
360 * @param deps List of the stackage's dependencies, with leading spaces
361 * to indicate depth, is written here. Print this list to console,
362 * with newlines separating each element. Example output:
363 @verbatim
364 roscpp_traits
365 cpp_common
366 cpp_common
367 rostime
368 cpp_common
369 @endverbatim
370 * @return True if the indented dependencies were computed, false
371 * otherwise.
372 */
373 bool depsIndent(const std::string& name, bool direct,
374 std::vector<std::string>& deps);
375 /**
376 * @brief Compute all dependency chains from one stackage to another.
377 * Intended for visual debugging of dependency structures.
378 * @param from The stackage that depends on.
379 * @param to The stackage that is depended on.
380 * @param output A list of dependency chains. Print this list to console,
381 * with newlines separating each element. Example output:
382 @verbatim
383 Dependency chains from roscpp to roslib:
384 * roscpp -> roslib
385 * roscpp -> std_msgs -> roslib
386 * roscpp -> rosgraph_msgs -> std_msgs -> roslib
387 @endverbatim
388 * @return True if the dependency chains were computed, false
389 * otherwise.
390 */
391 bool depsWhy(const std::string& from,
392 const std::string& to,
393 std::string& output);
394 /**
395 * @brief Compute rosdep entries that are declared in manifest of a package
396 * and its dependencies. Used by rosmake.
397 * @param name The package to work on.
398 * @param direct If true, then compute only direct dependencies. If
399 * false, then compute full (including indirect)
400 * dependencies.
401 * @param rosdeps List of rosdep entries found in the package and its
402 * dependencies is written here.
403 * @return True if the rosdep list is computed, false otherwise.
404 */
405 bool rosdeps(const std::string& name, bool direct,
406 std::set<std::string>& rosdeps);
407 void _rosdeps(Stackage* stackage, std::set<std::string>& rosdeps, const char* tag_name);
408 /**
409 * @brief Compute vcs entries that are declared in manifest of a package
410 * and its dependencies. Was used by Hudson build scripts; might not
411 * be needed.
412 * @param name The package to work on.
413 * @param direct If true, then compute only direct dependencies. If
414 * false, then compute full (including indirect)
415 * dependencies.
416 * @param vcs List of vcs entries found in the package and its
417 * dependencies is written here.
418 * @return True if the vcs list is computed, false otherwise.
419 */
420 bool vcs(const std::string& name, bool direct,
421 std::vector<std::string>& vcs);
422 /**
423 * @brief Compute cpp exports declared in a package and its dependencies.
424 * Used by rosbuild.
425 * @param name The package to work on.
426 * @param type The option to pass to pkg-config for wet packages.
427 * @param attrib The value of the 'attrib' attribute to search for.
428 * @param deps_only If true, then only return information from the
429 * pacakge's dependencies; if false, then also include the package's
430 * own export information.
431 * @param flags The pairs of export flags and is-wet are written here.
432 * @return True if the flags were computed, false otherwise.
433 */
434 bool cpp_exports(const std::string& name, const std::string& type,
435 const std::string& attrib, bool deps_only,
436 std::vector<std::pair<std::string, bool> >& flags);
437 /**
438 * @brief Reorder the paths according to the workspace chaining.
439 * @param paths The paths.
440 * @param reordered The reordered paths are written here.
441 * @return True if the pathswere reordered, false otherwise.
442 */
443 bool reorder_paths(const std::string& paths, std::string& reordered);
444 /**
445 * @brief Compute exports declared in a package and its dependencies.
446 * Used by rosbuild.
447 * @param name The package to work on.
448 * @param lang The value of the 'lang' attribute to search for.
449 * @param attrib The value of the 'attrib' attribute to search for.
450 * @param deps_only If true, then only return information from the
451 * pacakge's dependencies; if false, then also include the package's
452 * own export information.
453 * @param flags The accumulated flags are written here.
454 * @return True if the flags were computed, false otherwise.
455 */
456 bool exports(const std::string& name, const std::string& lang,
457 const std::string& attrib, bool deps_only,
458 std::vector<std::string>& flags);
459 /**
460 * @brief Compute exports declared in a dry package.
461 * @param name The package to work on.
462 * @param lang The value of the 'lang' attribute to search for.
463 * @param attrib The value of the 'attrib' attribute to search for.
464 * @param flags The accumulated flags are written here.
465 * @return True if the flags were computed, false otherwise.
466 */
467 bool exports_dry_package(Stackage* stackage, const std::string& lang,
468 const std::string& attrib,
469 std::vector<std::string>& flags);
470 /**
471 * @brief Compute exported plugins declared in packages that depend
472 * on a package. Forces crawl. Used by rosbuild and roslib.
473 * @param name The package to work on.
474 * @param attrib The value of the 'attrib' attribute to search for.
475 * @param top If non-empty, then limit the reverse dependency search to
476 * packages that 'top' depends on. Otherwise, examine all packages
477 * that depend on 'name'.
478 * @param flags The accumulated flags are written here.
479 * @return True if the flags were computed, false otherwise.
480 */
481 bool plugins(const std::string& name, const std::string& attrib,
482 const std::string& top,
483 std::vector<std::string>& flags);
484 /**
485 * @brief Report on time taken to crawl for stackages. Intended for
486 * use in debugging misconfigured stackage trees. Forces crawl.
487 * @param search_path Directories to search; passed to crawl().
488 * @param zombie_only If false, then produce formatted output, with
489 * timing information. Example output:
490 @verbatim
491 Full tree crawl took 0.014954 seconds.
492 Directories marked with (*) contain no manifest. You may
493 want to delete these directories.
494 To get just of list of directories without manifests,
495 re-run the profile with --zombie-only
496 -------------------------------------------------------------
497 0.013423 /opt/ros/electric/stacks
498 0.002989 /opt/ros/electric/stacks/ros_comm
499 @endverbatim If true, then produce a list of absolute paths that contain no stackages ("zombies"); these directories can likely be safely deleted. Example output:
500 @verbatim
501 /opt/ros/electric/stacks/pr2_controllers/trajectory_msgs
502 /opt/ros/electric/stacks/pr2_controllers/trajectory_msgs/msg
503 @endverbatim
504 * @param length Limit on how many directories to include in report
505 * (ordered in decreasing order of time taken to crawl).
506 * @param dirs Profile output. Print this list to console,
507 * with newlines separating each element.
508 */
509 bool profile(const std::vector<std::string>& search_path,
510 bool zombie_only,
511 int length,
512 std::vector<std::string>& dirs);
513 /**
514 * @brief Log a warning (usually goes to stderr).
515 * @param msg The warning.
516 * @param append_errno If true, then append a colon, a space, and
517 * the return from 'sterror(errno)'.
518 */
519 void logWarn(const std::string& msg,
520 bool append_errno = false);
521 /**
522 * @brief Log a error (usually goes to stderr).
523 * @param msg The error.
524 * @param append_errno If true, then append a colon, a space, and
525 * the return from 'sterror(errno)'.
526 */
527 void logError(const std::string& msg,
528 bool append_errno = false);
529 };
530
531 /**
532 * @brief Package crawler. Create one of these to operate on a package
533 * tree. Call public methods inherited from Rosstackage.
534 */
535 class ROSPACK_DECL Rospack : public Rosstackage
536 {
537 public:
538 /**
539 * @brief Constructor
540 */
541 Rospack();
542 /**
543 * @brief Usage statement.
544 * @return Command-line usage for the rospack tool.
545 */
546 virtual const char* usage();
547 };
548
549 /**
550 * @brief Stack crawler. Create one of these to operate on a stack
551 * tree. Call public methods inherited from Rosstackage.
552 */
553 class ROSPACK_DECL Rosstack : public Rosstackage
554 {
555 public:
556 /**
557 * @brief Constructor
558 */
559 Rosstack();
560 /**
561 * @brief Usage statement.
562 * @return Command-line usage for the rosstack tool.
563 */
564 virtual const char* usage();
565 };
566
567 } // namespace rospack
568
569 #endif
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27
28 #ifndef ROSPACK_ROSPACK_BACKCOMPAT_H
29 #define ROSPACK_ROSPACK_BACKCOMPAT_H
30
31 #include <string>
32 #include "macros.h"
33
34 namespace rospack
35 {
36
37 /**
38 * @brief Backward compatibility API for librospack (DEPRECATED).
39 * @deprecated Used by roslib. Don't use in new code.
40 */
41 class ROSPACK_DECL ROSPack
42 {
43 private:
44 std::string output_;
45 public:
46 /**
47 * @brief Constructor
48 * @deprecated Used by roslib. Don't use in new code.
49 */
50 ROSPack() {}
51 /**
52 * @brief Run rospack with the given arguments. Call getOutput() to
53 * get the result.
54 * @param argc Number of arguments in argv.
55 * @param argv List of arguments.
56 * @return Zero if the call succeeded, non-zero otherwise.
57 * @deprecated Used by roslib. Don't use in new code.
58 */
59 int run(int argc, char** argv);
60 /**
61 * @brief Run rospack with the given arguments. Call getOutput() to
62 * get the result.
63 * @param cmd Space-separated list of arguments.
64 * @return Zero if the call succeeded, non-zero otherwise.
65 * @deprecated Used by roslib. Don't use in new code.
66 */
67 int run(const std::string& cmd);
68 /**
69 * @brief Get the output from the last successful run() call.
70 * @return The result string.
71 * @deprecated Used by roslib. Don't use in new code.
72 */
73 std::string getOutput() {return output_;}
74 /**
75 * @brief Are we operating in quiet mode?
76 * @return Always true.
77 * @deprecated Used by roslib. Don't use in new code.
78 */
79 bool is_quiet() {return true;}
80 };
81
82 } // namespace rospack
83
84
85 #endif
0 <package>
1 <name>rospack</name>
2 <version>2.1.25</version>
3 <description>ROS Package Tool</description>
4 <maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
5 <license>BSD</license>
6
7 <url type="website">http://wiki.ros.org/rospack</url>
8 <url type="bugtracker">https://github.com/ros/rospack/issues</url>
9 <url type="repository">https://github.com/ros/rospack</url>
10
11 <author>Brian Gerkey</author>
12 <author>Morgan Quigley</author>
13 <author>Dirk Thomas</author>
14
15 <buildtool_depend version_gte="0.5.68">catkin</buildtool_depend>
16
17 <build_depend>boost</build_depend>
18 <build_depend>gtest</build_depend>
19 <build_depend>pkg-config</build_depend>
20 <build_depend>python</build_depend>
21 <build_depend>tinyxml</build_depend>
22
23 <run_depend>boost</run_depend>
24 <run_depend>gtest</run_depend>
25 <run_depend>pkg-config</run_depend>
26 <run_depend>python</run_depend>
27 <run_depend>python-catkin-pkg</run_depend>
28 <run_depend>python-rosdep</run_depend>
29 <run_depend>tinyxml</run_depend>
30
31 <test_depend>python-coverage</test_depend>
32 </package>
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc., Morgan Quigley
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "rospack/rospack.h"
28 #include "utils.h"
29 #include "tinyxml.h"
30
31 #include <boost/filesystem.hpp>
32 #include <boost/algorithm/string.hpp>
33 #include <stdexcept>
34
35 #if defined(WIN32)
36 #include <windows.h>
37 #include <direct.h>
38 #include <fcntl.h> // for O_RDWR, O_EXCL, O_CREAT
39 // simple workaround - could have issues though. See
40 // http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
41 // for potentially better solutions. Similar probably applies for some of the others
42 #define snprintf _snprintf
43 #define pclose _pclose
44 #define popen _popen
45 #define PATH_MAX MAX_PATH
46 #if defined(__MINGW32__)
47 #include <libgen.h> // for dirname
48 #endif
49 #if defined(_MSC_VER)
50 #include <io.h> // _mktemp_s
51 #endif
52 #else //!defined(WIN32)
53 #include <sys/types.h>
54 #include <libgen.h>
55 #include <pwd.h>
56 #include <unistd.h>
57 #include <sys/time.h>
58 #endif
59
60 #include <algorithm>
61 #include <climits>
62
63 #include <sys/stat.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <time.h>
67 #include <string.h>
68 #include <errno.h>
69
70 #include <Python.h>
71
72 /* re-define some String functions for recent python (>= 3.0) */
73 #if PY_VERSION_HEX >= 0x03000000
74 #define PyString_AsString PyBytes_AsString
75 #define PyString_FromString PyBytes_FromString
76 #endif
77
78 // TODO:
79 // recrawl on:
80 // package not found in cache
81 // package found in cache, but no manifest.xml present in filesystem
82
83 namespace fs = boost::filesystem;
84
85 namespace rospack
86 {
87 static const char* MANIFEST_TAG_PACKAGE = "package";
88 static const char* MANIFEST_TAG_STACK = "stack";
89 static const char* ROSPACK_MANIFEST_NAME = "manifest.xml";
90 static const char* ROSPACKAGE_MANIFEST_NAME = "package.xml";
91 static const char* ROSSTACK_MANIFEST_NAME = "stack.xml";
92 static const char* ROSPACK_CACHE_NAME = "rospack_cache";
93 static const char* ROSSTACK_CACHE_NAME = "rosstack_cache";
94 static const char* ROSPACK_NOSUBDIRS = "rospack_nosubdirs";
95 static const char* CATKIN_IGNORE = "CATKIN_IGNORE";
96 static const char* DOTROS_NAME = ".ros";
97 static const char* MSG_GEN_GENERATED_DIR = "msg_gen";
98 static const char* MSG_GEN_GENERATED_FILE = "generated";
99 static const char* SRV_GEN_GENERATED_DIR = "srv_gen";
100 static const char* SRV_GEN_GENERATED_FILE = "generated";
101 static const char* MANIFEST_TAG_ROSDEP = "rosdep";
102 static const char* MANIFEST_TAG_VERSIONCONTROL = "versioncontrol";
103 static const char* MANIFEST_TAG_EXPORT = "export";
104 static const char* MANIFEST_ATTR_NAME = "name";
105 static const char* MANIFEST_ATTR_TYPE = "type";
106 static const char* MANIFEST_ATTR_URL = "url";
107 static const char* MANIFEST_PREFIX = "${prefix}";
108 static const int MAX_CRAWL_DEPTH = 1000;
109 static const int MAX_DEPENDENCY_DEPTH = 1000;
110 static const double DEFAULT_MAX_CACHE_AGE = 60.0;
111
112 TiXmlElement* get_manifest_root(Stackage* stackage);
113 double time_since_epoch();
114
115 #ifdef __APPLE__
116 static const std::string g_ros_os = "osx";
117 #else
118 #if defined(WIN32)
119 static const std::string g_ros_os = "win32";
120 #else
121 static const std::string g_ros_os = "linux";
122 #endif
123 #endif
124
125 class Exception : public std::runtime_error
126 {
127 public:
128 Exception(const std::string& what)
129 : std::runtime_error(what)
130 {}
131 };
132
133 class Stackage
134 {
135 public:
136 // \brief name of the stackage
137 std::string name_;
138 // \brief absolute path to the stackage
139 std::string path_;
140 // \brief absolute path to the stackage manifest
141 std::string manifest_path_;
142 // \brief filename of the stackage manifest
143 std::string manifest_name_;
144 // \brief have we already loaded the manifest?
145 bool manifest_loaded_;
146 // \brief TinyXML structure, filled in during parsing
147 TiXmlDocument manifest_;
148 std::vector<Stackage*> deps_;
149 bool deps_computed_;
150 bool is_wet_package_;
151 bool is_metapackage_;
152
153 Stackage(const std::string& name,
154 const std::string& path,
155 const std::string& manifest_path,
156 const std::string& manifest_name) :
157 name_(name),
158 path_(path),
159 manifest_path_(manifest_path),
160 manifest_name_(manifest_name),
161 manifest_loaded_(false),
162 deps_computed_(false),
163 is_metapackage_(false)
164 {
165 is_wet_package_ = manifest_name_ == ROSPACKAGE_MANIFEST_NAME;
166 }
167
168 void update_wet_information()
169 {
170 assert(is_wet_package_);
171 assert(manifest_loaded_);
172 // get name from package.xml instead of folder name
173 TiXmlElement* root = get_manifest_root(this);
174 for(TiXmlElement* el = root->FirstChildElement("name"); el; el = el->NextSiblingElement("name"))
175 {
176 name_ = el->GetText();
177 break;
178 }
179 // check if package is a metapackage
180 for(TiXmlElement* el = root->FirstChildElement("export"); el; el = el->NextSiblingElement("export"))
181 {
182 if(el->FirstChildElement("metapackage"))
183 {
184 is_metapackage_ = true;
185 break;
186 }
187 }
188 }
189
190 bool isStack() const
191 {
192 return manifest_name_ == MANIFEST_TAG_STACK || (is_wet_package_ && is_metapackage_);
193 }
194
195 bool isPackage() const
196 {
197 return manifest_name_ == MANIFEST_TAG_PACKAGE || (is_wet_package_ && !is_metapackage_);
198 }
199
200 };
201
202 class DirectoryCrawlRecord
203 {
204 public:
205 std::string path_;
206 bool zombie_;
207 double start_time_;
208 double crawl_time_;
209 size_t start_num_pkgs_;
210 DirectoryCrawlRecord(std::string path,
211 double start_time,
212 size_t start_num_pkgs) :
213 path_(path),
214 zombie_(false),
215 start_time_(start_time),
216 crawl_time_(0.0),
217 start_num_pkgs_(start_num_pkgs) {}
218 };
219 bool cmpDirectoryCrawlRecord(DirectoryCrawlRecord* i,
220 DirectoryCrawlRecord* j)
221 {
222 return (i->crawl_time_ < j->crawl_time_);
223 }
224
225 /////////////////////////////////////////////////////////////
226 // Rosstackage methods (public/protected)
227 /////////////////////////////////////////////////////////////
228 Rosstackage::Rosstackage(const std::string& manifest_name,
229 const std::string& cache_name,
230 const std::string& name,
231 const std::string& tag) :
232 manifest_name_(manifest_name),
233 cache_name_(cache_name),
234 crawled_(false),
235 name_(name),
236 tag_(tag)
237 {
238 }
239
240 void
241 Rosstackage::logWarn(const std::string& msg,
242 bool append_errno)
243 {
244 log("Warning", msg, append_errno);
245 }
246 void
247 Rosstackage::logError(const std::string& msg,
248 bool append_errno)
249 {
250 log("Error", msg, append_errno);
251 }
252
253 bool
254 Rosstackage::getSearchPathFromEnv(std::vector<std::string>& sp)
255 {
256 char* rr = getenv("ROS_ROOT");
257 char* rpp = getenv("ROS_PACKAGE_PATH");
258
259 // ROS_ROOT is optional, and will be phased out
260 if(rr)
261 {
262 try
263 {
264 if(fs::is_directory(rr))
265 sp.push_back(rr);
266 else
267 logWarn(std::string("ROS_ROOT=") + rr + " is not a directory");
268 }
269 catch(fs::filesystem_error& e)
270 {
271 logWarn(std::string("error while looking at ROS_ROOT ") + rr + ": " + e.what());
272 }
273 }
274 if(rpp)
275 {
276 // I can't see that boost filesystem has an elegant cross platform
277 // representation for this anywhere like qt/python have.
278 #if defined(WIN32)
279 const char *path_delim = ";";
280 #else //!defined(WIN32)
281 const char *path_delim = ":";
282 #endif
283
284 std::vector<std::string> rpp_strings;
285 boost::split(rpp_strings, rpp,
286 boost::is_any_of(path_delim),
287 boost::token_compress_on);
288 for(std::vector<std::string>::const_iterator it = rpp_strings.begin();
289 it != rpp_strings.end();
290 ++it)
291 {
292 sp.push_back(*it);
293 }
294 }
295 return true;
296 }
297
298 void
299 Rosstackage::setQuiet(bool quiet)
300 {
301 quiet_ = quiet;
302 }
303
304 bool
305 Rosstackage::isStackage(const std::string& path)
306 {
307 try
308 {
309 if(!fs::is_directory(path))
310 return false;
311 }
312 catch(fs::filesystem_error& e)
313 {
314 logWarn(std::string("error while looking at ") + path + ": " + e.what());
315 return false;
316 }
317
318 try
319 {
320 for(fs::directory_iterator dit = fs::directory_iterator(path);
321 dit != fs::directory_iterator();
322 ++dit)
323 {
324 if(!fs::is_regular_file(dit->path()))
325 continue;
326
327 if(dit->path().filename() == manifest_name_)
328 return true;
329
330 // finding a package.xml is acceptable
331 if(dit->path().filename() == ROSPACKAGE_MANIFEST_NAME)
332 return true;
333 }
334 }
335 catch(fs::filesystem_error& e)
336 {
337 logWarn(std::string("error while crawling ") + path + ": " + e.what());
338 }
339 return false;
340 }
341
342 void
343 Rosstackage::crawl(std::vector<std::string> search_path,
344 bool force)
345 {
346 if(!force)
347 {
348 if(readCache())
349 {
350 // If the cache was valid, then the paths in the cache match the ones
351 // we've been asked to crawl. Store them, so that later, methods
352 // like find() can refer to them when recrawling.
353 search_paths_.clear();
354 for(std::vector<std::string>::const_iterator it = search_path.begin();
355 it != search_path.end();
356 ++it)
357 search_paths_.push_back(*it);
358 return;
359 }
360
361 if(crawled_)
362 {
363 bool same_paths = true;
364 if(search_paths_.size() != search_path.size())
365 same_paths = false;
366 else
367 {
368 for(unsigned int i=0; i<search_paths_.size(); i++)
369 {
370 if(search_paths_[i] != search_path[i])
371 {
372 same_paths = false;
373 break;
374 }
375 }
376 }
377
378 if(same_paths)
379 return;
380 }
381 }
382
383
384 std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
385 while(it != stackages_.end())
386 {
387 delete it->second;
388 it = stackages_.erase(it);
389 }
390 dups_.clear();
391 search_paths_.clear();
392 for(std::vector<std::string>::const_iterator it = search_path.begin();
393 it != search_path.end();
394 ++it)
395 search_paths_.push_back(*it);
396
397 std::vector<DirectoryCrawlRecord*> dummy;
398 std::tr1::unordered_set<std::string> dummy2;
399 for(std::vector<std::string>::const_iterator p = search_paths_.begin();
400 p != search_paths_.end();
401 ++p)
402 crawlDetail(*p, force, 1, false, dummy, dummy2);
403
404 crawled_ = true;
405
406 writeCache();
407 }
408
409 bool
410 Rosstackage::inStackage(std::string& name)
411 {
412 fs::path path;
413 try
414 {
415 // Search upward, as suggested in #3697. This method is only used when
416 // a package is not given on the command-line, and so should not have
417 // performance impact in common usage.
418 for(fs::path path = fs::current_path();
419 !path.empty();
420 path = path.parent_path())
421 {
422 if(Rosstackage::isStackage(path.string()))
423 {
424 #if !defined(BOOST_FILESYSTEM_VERSION) || (BOOST_FILESYSTEM_VERSION == 2)
425 name = fs::path(path).filename();
426 #else
427 // in boostfs3, filename() returns a path, which needs to be stringified
428 name = fs::path(path).filename().string();
429 #endif
430 return true;
431 }
432 }
433 // Search failed.
434 return false;
435 }
436 catch(fs::filesystem_error& e)
437 {
438 // can't determine current directory, or encountered trouble while
439 // searching upward; too bad
440 return false;
441 }
442 }
443
444
445 bool
446 Rosstackage::find(const std::string& name, std::string& path)
447 {
448 Stackage* s = findWithRecrawl(name);
449 if(s)
450 {
451 path = s->path_;
452 return true;
453 }
454 else
455 return false;
456 }
457
458 bool
459 Rosstackage::contents(const std::string& name,
460 std::set<std::string>& packages)
461 {
462 Rospack rp2;
463 std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.find(name);
464 if(it != stackages_.end())
465 {
466 std::vector<std::string> search_paths;
467 search_paths.push_back(it->second->path_);
468 rp2.crawl(search_paths, true);
469 std::set<std::pair<std::string, std::string> > names_paths;
470 rp2.list(names_paths);
471 for(std::set<std::pair<std::string, std::string> >::const_iterator iit = names_paths.begin();
472 iit != names_paths.end();
473 ++iit)
474 packages.insert(iit->first);
475 return true;
476 }
477 else
478 {
479 logError(std::string("stack ") + name + " not found");
480 return false;
481 }
482 }
483
484 bool
485 Rosstackage::contains(const std::string& name,
486 std::string& stack,
487 std::string& path)
488 {
489 Rospack rp2;
490 for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
491 it != stackages_.end();
492 ++it)
493 {
494 std::vector<std::string> search_paths;
495 search_paths.push_back(it->second->path_);
496 rp2.crawl(search_paths, true);
497 std::set<std::pair<std::string, std::string> > names_paths;
498 rp2.list(names_paths);
499 for(std::set<std::pair<std::string, std::string> >::const_iterator iit = names_paths.begin();
500 iit != names_paths.end();
501 ++iit)
502 {
503 if(iit->first == name)
504 {
505 stack = it->first;
506 path = it->second->path_;
507 return true;
508 }
509 }
510 }
511
512 logError(std::string("stack containing package ") + name + " not found");
513 return false;
514 }
515
516 void
517 Rosstackage::list(std::set<std::pair<std::string, std::string> >& list)
518 {
519 for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
520 it != stackages_.end();
521 ++it)
522 {
523 std::pair<std::string, std::string> item;
524 item.first = it->first;
525 item.second = it->second->path_;
526 list.insert(item);
527 }
528 }
529
530 void
531 Rosstackage::listDuplicates(std::vector<std::string>& dups)
532 {
533 dups.resize(dups_.size());
534 int i = 0;
535 for(std::tr1::unordered_map<std::string, std::vector<std::string> >::const_iterator it = dups_.begin();
536 it != dups_.end();
537 ++it)
538 {
539 dups[i] = it->first;
540 i++;
541 }
542 }
543
544 void
545 Rosstackage::listDuplicatesWithPaths(std::map<std::string, std::vector<std::string> >& dups)
546 {
547 dups.clear();
548 for(std::tr1::unordered_map<std::string, std::vector<std::string> >::const_iterator it = dups_.begin();
549 it != dups_.end();
550 ++it)
551 {
552 dups[it->first].resize(it->second.size());
553 int j = 0;
554 for(std::vector<std::string>::const_iterator jt = it->second.begin();
555 jt != it->second.end();
556 ++jt)
557 {
558 dups[it->first][j] = *jt;
559 j++;
560 }
561 }
562 }
563
564 bool
565 Rosstackage::deps(const std::string& name, bool direct,
566 std::vector<std::string>& deps)
567 {
568 std::vector<Stackage*> stackages;
569 // Disable errors for the first try
570 bool old_quiet = quiet_;
571 setQuiet(true);
572 if(!depsDetail(name, direct, stackages))
573 {
574 // Recrawl
575 crawl(search_paths_, true);
576 stackages.clear();
577 setQuiet(old_quiet);
578 if(!depsDetail(name, direct, stackages))
579 return false;
580 }
581 setQuiet(old_quiet);
582 for(std::vector<Stackage*>::const_iterator it = stackages.begin();
583 it != stackages.end();
584 ++it)
585 deps.push_back((*it)->name_);
586 return true;
587 }
588
589 bool
590 Rosstackage::depsOn(const std::string& name, bool direct,
591 std::vector<std::string>& deps)
592 {
593 std::vector<Stackage*> stackages;
594 if(!depsOnDetail(name, direct, stackages))
595 return false;
596 for(std::vector<Stackage*>::const_iterator it = stackages.begin();
597 it != stackages.end();
598 ++it)
599 deps.push_back((*it)->name_);
600 return true;
601 }
602
603 bool
604 Rosstackage::depsIndent(const std::string& name, bool direct,
605 std::vector<std::string>& deps)
606 {
607 Stackage* stackage = findWithRecrawl(name);
608 if(!stackage)
609 return false;
610 try
611 {
612 computeDeps(stackage);
613 std::vector<Stackage*> deps_vec;
614 std::tr1::unordered_set<Stackage*> deps_hash;
615 std::vector<std::string> indented_deps;
616 gatherDepsFull(stackage, direct, POSTORDER, 0, deps_hash, deps_vec, true, indented_deps);
617 for(std::vector<std::string>::const_iterator it = indented_deps.begin();
618 it != indented_deps.end();
619 ++it)
620 deps.push_back(*it);
621 }
622 catch(Exception& e)
623 {
624 logError(e.what());
625 return false;
626 }
627 return true;
628 }
629
630 bool
631 Rosstackage::depsWhy(const std::string& from,
632 const std::string& to,
633 std::string& output)
634 {
635 Stackage* from_s = findWithRecrawl(from);
636 if(!from_s)
637 return false;
638 Stackage* to_s = findWithRecrawl(to);
639 if(!to_s)
640 return false;
641
642 std::list<std::list<Stackage*> > acc_list;
643 try
644 {
645 depsWhyDetail(from_s, to_s, acc_list);
646 }
647 catch(Exception& e)
648 {
649 logError(e.what());
650 return true;
651 }
652 output.append(std::string("Dependency chains from ") +
653 from + " to " + to + ":\n");
654 for(std::list<std::list<Stackage*> >::const_iterator it = acc_list.begin();
655 it != acc_list.end();
656 ++it)
657 {
658 output.append("* ");
659 for(std::list<Stackage*>::const_iterator iit = it->begin();
660 iit != it->end();
661 ++iit)
662 {
663 if(iit != it->begin())
664 output.append("-> ");
665 output.append((*iit)->name_ + " ");
666 }
667 output.append("\n");
668 }
669 return true;
670 }
671
672 bool
673 Rosstackage::depsManifests(const std::string& name, bool direct,
674 std::vector<std::string>& manifests)
675 {
676 Stackage* stackage = findWithRecrawl(name);
677 if(!stackage)
678 return false;
679 try
680 {
681 computeDeps(stackage);
682 std::vector<Stackage*> deps_vec;
683 gatherDeps(stackage, direct, POSTORDER, deps_vec);
684 for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
685 it != deps_vec.end();
686 ++it)
687 manifests.push_back((*it)->manifest_path_);
688 }
689 catch(Exception& e)
690 {
691 logError(e.what());
692 return false;
693 }
694 return true;
695 }
696
697 bool
698 Rosstackage::rosdeps(const std::string& name, bool direct,
699 std::set<std::string>& rosdeps)
700 {
701 Stackage* stackage = findWithRecrawl(name);
702 if(!stackage)
703 return false;
704 try
705 {
706 computeDeps(stackage);
707 std::vector<Stackage*> deps_vec;
708 // rosdeps include the current package
709 deps_vec.push_back(stackage);
710 if(!direct)
711 gatherDeps(stackage, direct, POSTORDER, deps_vec);
712 for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
713 it != deps_vec.end();
714 ++it)
715 {
716 if (!stackage->is_wet_package_)
717 {
718 _rosdeps(*it, rosdeps, MANIFEST_TAG_ROSDEP);
719 }
720 else
721 {
722 // package format 1 tags
723 _rosdeps(*it, rosdeps, "build_depend");
724 _rosdeps(*it, rosdeps, "buildtool_depend");
725 _rosdeps(*it, rosdeps, "run_depend");
726 // package format 2 tags
727 _rosdeps(*it, rosdeps, "build_export_depend");
728 _rosdeps(*it, rosdeps, "buildtool_export_depend");
729 _rosdeps(*it, rosdeps, "exec_depend");
730 _rosdeps(*it, rosdeps, "depend");
731 _rosdeps(*it, rosdeps, "doc_depend");
732 _rosdeps(*it, rosdeps, "test_depend");
733 }
734 }
735 }
736 catch(Exception& e)
737 {
738 logError(e.what());
739 return false;
740 }
741 return true;
742 }
743
744 void
745 Rosstackage::_rosdeps(Stackage* stackage, std::set<std::string>& rosdeps, const char* tag_name)
746 {
747 TiXmlElement* root = get_manifest_root(stackage);
748 for(TiXmlElement* ele = root->FirstChildElement(tag_name);
749 ele;
750 ele = ele->NextSiblingElement(tag_name))
751 {
752 if(!stackage->is_wet_package_)
753 {
754 const char *att_str;
755 if((att_str = ele->Attribute(MANIFEST_ATTR_NAME)))
756 {
757 rosdeps.insert(std::string("name: ") + att_str);
758 }
759 }
760 else
761 {
762 const char* dep_pkgname = ele->GetText();
763 if(isSysPackage(dep_pkgname))
764 {
765 rosdeps.insert(std::string("name: ") + dep_pkgname);
766 }
767 }
768 }
769 }
770
771 bool
772 Rosstackage::vcs(const std::string& name, bool direct,
773 std::vector<std::string>& vcs)
774 {
775 Stackage* stackage = findWithRecrawl(name);
776 if(!stackage)
777 return false;
778 try
779 {
780 computeDeps(stackage);
781 std::vector<Stackage*> deps_vec;
782 // vcs include the current package
783 deps_vec.push_back(stackage);
784 if(!direct)
785 gatherDeps(stackage, direct, POSTORDER, deps_vec);
786 for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
787 it != deps_vec.end();
788 ++it)
789 {
790 TiXmlElement* root = get_manifest_root(*it);
791 for(TiXmlElement* ele = root->FirstChildElement(MANIFEST_TAG_VERSIONCONTROL);
792 ele;
793 ele = ele->NextSiblingElement(MANIFEST_TAG_VERSIONCONTROL))
794 {
795 std::string result;
796 const char *att_str;
797 if((att_str = ele->Attribute(MANIFEST_ATTR_TYPE)))
798 {
799 result.append("type: ");
800 result.append(att_str);
801 }
802 if((att_str = ele->Attribute(MANIFEST_ATTR_URL)))
803 {
804 result.append("\turl: ");
805 result.append(att_str);
806 }
807 vcs.push_back(result);
808 }
809 }
810 }
811 catch(Exception& e)
812 {
813 logError(e.what());
814 return false;
815 }
816 return true;
817 }
818
819 bool
820 Rosstackage::cpp_exports(const std::string& name, const std::string& type,
821 const std::string& attrib, bool deps_only,
822 std::vector<std::pair<std::string, bool> >& flags)
823 {
824 Stackage* stackage = findWithRecrawl(name);
825 if(!stackage)
826 return false;
827
828 static bool init_py = false;
829 static PyObject* pName;
830 static PyObject* pModule;
831 static PyObject* pDict;
832 static PyObject* pFunc;
833
834 try
835 {
836 computeDeps(stackage);
837 std::vector<Stackage*> deps_vec;
838 if(!deps_only)
839 deps_vec.push_back(stackage);
840 gatherDeps(stackage, false, PREORDER, deps_vec, true);
841 for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
842 it != deps_vec.end();
843 ++it)
844 {
845 if(!(*it)->is_wet_package_)
846 {
847 std::vector<std::string> dry_flags;
848 if(!exports_dry_package(*it, "cpp", attrib, dry_flags))
849 {
850 return false;
851 }
852 for(std::vector<std::string>::const_iterator it = dry_flags.begin(); it != dry_flags.end(); ++it)
853 {
854 flags.push_back(std::pair<std::string, bool>(*it, false));
855 }
856 }
857 else
858 {
859 initPython();
860 PyGILState_STATE gstate = PyGILState_Ensure();
861
862 if(!init_py)
863 {
864 init_py = true;
865 pName = PyString_FromString("rosdep2.rospack");
866 pModule = PyImport_Import(pName);
867 if(!pModule)
868 {
869 PyErr_Print();
870 PyGILState_Release(gstate);
871 std::string errmsg = "could not find python module 'rosdep2.rospack'. is rosdep up-to-date (at least 0.10.4)?";
872 throw Exception(errmsg);
873 }
874 pDict = PyModule_GetDict(pModule);
875 pFunc = PyDict_GetItemString(pDict, "call_pkg_config");
876 }
877
878 if(!PyCallable_Check(pFunc))
879 {
880 PyErr_Print();
881 PyGILState_Release(gstate);
882 std::string errmsg = "could not find python function 'rosdep2.rospack.call_pkg_config'. is rosdep up-to-date (at least 0.10.7)?";
883 throw Exception(errmsg);
884 }
885
886 PyObject* pArgs = PyTuple_New(2);
887 PyObject* pOpt = PyString_FromString(type.c_str());
888 PyTuple_SetItem(pArgs, 0, pOpt);
889 PyObject* pPkg = PyString_FromString((*it)->name_.c_str());
890 PyTuple_SetItem(pArgs, 1, pPkg);
891 PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
892 Py_DECREF(pArgs);
893
894 if(!pValue)
895 {
896 PyErr_Print();
897 PyGILState_Release(gstate);
898 std::string errmsg = "could not call python function 'rosdep2.rospack.call_pkg_config'";
899 throw Exception(errmsg);
900 }
901 if(pValue == Py_None)
902 {
903 Py_DECREF(pValue);
904 std::string errmsg = "python function 'rosdep2.rospack.call_pkg_config' could not call 'pkg-config " + type + " " + (*it)->name_ + "' without errors";
905 throw Exception(errmsg);
906 }
907
908 flags.push_back(std::pair<std::string, bool>(PyString_AsString(pValue), true));
909 Py_DECREF(pValue);
910
911 // we want to keep the static objects alive for repeated access
912 // so skip all garbage collection until process ends
913 //Py_DECREF(pFunc);
914 //Py_DECREF(pModule);
915 //Py_DECREF(pName);
916 //Py_Finalize();
917
918 PyGILState_Release(gstate);
919 }
920 }
921 }
922 catch(Exception& e)
923 {
924 logError(e.what());
925 return false;
926 }
927 return true;
928 }
929
930 bool
931 Rosstackage::reorder_paths(const std::string& paths, std::string& reordered)
932 {
933 static bool init_py = false;
934 static PyObject* pName;
935 static PyObject* pModule;
936 static PyObject* pFunc;
937
938 initPython();
939 PyGILState_STATE gstate = PyGILState_Ensure();
940
941 if(!init_py)
942 {
943 init_py = true;
944 pName = PyString_FromString("catkin_pkg.rospack");
945 pModule = PyImport_Import(pName);
946 if(!pModule)
947 {
948 PyErr_Print();
949 PyGILState_Release(gstate);
950 std::string errmsg = "could not find python module 'catkin_pkg.rospack'. is catkin_pkg up-to-date (at least 0.1.8)?";
951 throw Exception(errmsg);
952 }
953 PyObject* pDict = PyModule_GetDict(pModule);
954 pFunc = PyDict_GetItemString(pDict, "reorder_paths");
955 }
956
957 if(!PyCallable_Check(pFunc))
958 {
959 PyErr_Print();
960 PyGILState_Release(gstate);
961 std::string errmsg = "could not find python function 'catkin_pkg.rospack.reorder_paths'. is catkin_pkg up-to-date (at least 0.1.8)?";
962 throw Exception(errmsg);
963 }
964
965
966 PyObject* pArgs = PyTuple_New(1);
967 PyTuple_SetItem(pArgs, 0, PyString_FromString(paths.c_str()));
968 PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
969 Py_DECREF(pArgs);
970
971 if(!pValue)
972 {
973 PyErr_Print();
974 PyGILState_Release(gstate);
975 std::string errmsg = "could not call python function 'catkin_pkg.rospack.reorder_paths'";
976 throw Exception(errmsg);
977 }
978
979 reordered = PyString_AsString(pValue);
980 Py_DECREF(pValue);
981
982 // we want to keep the static objects alive for repeated access
983 // so skip all garbage collection until process ends
984 //Py_DECREF(pFunc);
985 //Py_DECREF(pModule);
986 //Py_DECREF(pName);
987 //Py_Finalize();
988
989 PyGILState_Release(gstate);
990
991 return true;
992 }
993
994 bool
995 Rosstackage::exports(const std::string& name, const std::string& lang,
996 const std::string& attrib, bool deps_only,
997 std::vector<std::string>& flags)
998 {
999 Stackage* stackage = findWithRecrawl(name);
1000 if(!stackage)
1001 return false;
1002 try
1003 {
1004 computeDeps(stackage);
1005 std::vector<Stackage*> deps_vec;
1006 if(!deps_only)
1007 deps_vec.push_back(stackage);
1008 gatherDeps(stackage, false, PREORDER, deps_vec);
1009 for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
1010 it != deps_vec.end();
1011 ++it)
1012 {
1013 if (!exports_dry_package(*it, lang, attrib, flags))
1014 {
1015 return false;
1016 }
1017 }
1018 }
1019 catch(Exception& e)
1020 {
1021 logError(e.what());
1022 return false;
1023 }
1024 return true;
1025 }
1026
1027 bool
1028 Rosstackage::exports_dry_package(Stackage* stackage, const std::string& lang,
1029 const std::string& attrib,
1030 std::vector<std::string>& flags)
1031 {
1032 TiXmlElement* root = get_manifest_root(stackage);
1033 for(TiXmlElement* ele = root->FirstChildElement(MANIFEST_TAG_EXPORT);
1034 ele;
1035 ele = ele->NextSiblingElement(MANIFEST_TAG_EXPORT))
1036 {
1037 bool os_match = false;
1038 const char *best_match = NULL;
1039 for(TiXmlElement* ele2 = ele->FirstChildElement(lang);
1040 ele2;
1041 ele2 = ele2->NextSiblingElement(lang))
1042 {
1043 const char *os_str;
1044 if ((os_str = ele2->Attribute("os")))
1045 {
1046 if(g_ros_os == std::string(os_str))
1047 {
1048 if(os_match)
1049 logWarn(std::string("ignoring duplicate ") + lang + " tag with os=" + os_str + " in export block");
1050 else
1051 {
1052 best_match = ele2->Attribute(attrib.c_str());
1053 os_match = true;
1054 }
1055 }
1056 }
1057 if(!os_match)
1058 {
1059 if(!best_match)
1060 best_match = ele2->Attribute(attrib.c_str());
1061 else
1062 logWarn(std::string("ignoring duplicate ") + lang + " tag in export block");
1063 }
1064
1065 }
1066 if(best_match)
1067 {
1068 std::string expanded_str;
1069 if(!expandExportString(stackage, best_match, expanded_str))
1070 return false;
1071 flags.push_back(expanded_str);
1072 }
1073 }
1074 // We automatically point to msg_gen and msg_srv directories if
1075 // certain files are present.
1076 // But only if we're looking for cpp/cflags, #3884.
1077 if((lang == "cpp") && (attrib == "cflags"))
1078 {
1079 fs::path msg_gen = fs::path(stackage->path_) / MSG_GEN_GENERATED_DIR;
1080 fs::path srv_gen = fs::path(stackage->path_) / SRV_GEN_GENERATED_DIR;
1081 if(fs::is_regular_file(msg_gen / MSG_GEN_GENERATED_FILE))
1082 {
1083 msg_gen /= fs::path("cpp") / "include";
1084 flags.push_back(std::string("-I" + msg_gen.string()));
1085 }
1086 if(fs::is_regular_file(srv_gen / SRV_GEN_GENERATED_FILE))
1087 {
1088 srv_gen /= fs::path("cpp") / "include";
1089 flags.push_back(std::string("-I" + srv_gen.string()));
1090 }
1091 }
1092 return true;
1093 }
1094
1095 bool
1096 Rosstackage::plugins(const std::string& name, const std::string& attrib,
1097 const std::string& top,
1098 std::vector<std::string>& flags)
1099 {
1100 // Find everybody who depends directly on the package in question
1101 std::vector<Stackage*> stackages;
1102 if(!depsOnDetail(name, true, stackages, true))
1103 return false;
1104 // Also look in the package itself
1105 std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.find(name);
1106 if(it != stackages_.end())
1107 {
1108 // don't warn here; it was done in depsOnDetail()
1109 stackages.push_back(it->second);
1110 }
1111 // If top was given, filter to include only those package on which top
1112 // depends.
1113 if(top.size())
1114 {
1115 std::vector<Stackage*> top_deps;
1116 if(!depsDetail(top, false, top_deps))
1117 return false;
1118 std::tr1::unordered_set<Stackage*> top_deps_set;
1119 for(std::vector<Stackage*>::iterator it = top_deps.begin();
1120 it != top_deps.end();
1121 ++it)
1122 top_deps_set.insert(*it);
1123 std::vector<Stackage*>::iterator it = stackages.begin();
1124 while(it != stackages.end())
1125 {
1126 if((*it)->name_ != top &&
1127 (top_deps_set.find(*it) == top_deps_set.end()))
1128 it = stackages.erase(it);
1129 else
1130 ++it;
1131 }
1132 }
1133 // Now go looking for the manifest data
1134 for(std::vector<Stackage*>::const_iterator it = stackages.begin();
1135 it != stackages.end();
1136 ++it)
1137 {
1138 TiXmlElement* root = get_manifest_root(*it);
1139 for(TiXmlElement* ele = root->FirstChildElement(MANIFEST_TAG_EXPORT);
1140 ele;
1141 ele = ele->NextSiblingElement(MANIFEST_TAG_EXPORT))
1142 {
1143 for(TiXmlElement* ele2 = ele->FirstChildElement(name);
1144 ele2;
1145 ele2 = ele2->NextSiblingElement(name))
1146 {
1147 const char *att_str;
1148 if((att_str = ele2->Attribute(attrib.c_str())))
1149 {
1150 std::string expanded_str;
1151 if(!expandExportString(*it, att_str, expanded_str))
1152 return false;
1153 flags.push_back((*it)->name_ + " " + expanded_str);
1154 }
1155 }
1156 }
1157 }
1158 return true;
1159 }
1160
1161 bool
1162 Rosstackage::depsMsgSrv(const std::string& name, bool direct,
1163 std::vector<std::string>& gens)
1164 {
1165 Stackage* stackage = findWithRecrawl(name);
1166 if(!stackage)
1167 return false;
1168 try
1169 {
1170 computeDeps(stackage);
1171 std::vector<Stackage*> deps_vec;
1172 gatherDeps(stackage, direct, POSTORDER, deps_vec);
1173 for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
1174 it != deps_vec.end();
1175 ++it)
1176 {
1177 fs::path msg_gen = fs::path((*it)->path_) /
1178 MSG_GEN_GENERATED_DIR /
1179 MSG_GEN_GENERATED_FILE;
1180 fs::path srv_gen = fs::path((*it)->path_) /
1181 SRV_GEN_GENERATED_DIR /
1182 SRV_GEN_GENERATED_FILE;
1183 if(fs::is_regular_file(msg_gen))
1184 gens.push_back(msg_gen.string());
1185 if(fs::is_regular_file(srv_gen))
1186 gens.push_back(srv_gen.string());
1187 }
1188 }
1189 catch(Exception& e)
1190 {
1191 logError(e.what());
1192 return false;
1193 }
1194 return true;
1195 }
1196
1197 /////////////////////////////////////////////////////////////
1198 // Rosstackage methods (private)
1199 /////////////////////////////////////////////////////////////
1200
1201 void
1202 Rosstackage::log(const std::string& level,
1203 const std::string& msg,
1204 bool append_errno)
1205 {
1206 if(quiet_)
1207 return;
1208 fprintf(stderr, "[%s] %s: %s",
1209 name_.c_str(), level.c_str(), msg.c_str());
1210 if(append_errno)
1211 fprintf(stderr, ": %s", strerror(errno));
1212 fprintf(stderr, "\n");
1213 }
1214
1215
1216 Stackage*
1217 Rosstackage::findWithRecrawl(const std::string& name)
1218 {
1219 if(stackages_.count(name))
1220 return stackages_[name];
1221 else
1222 {
1223 // Try to recrawl, in case we loaded from cache
1224 crawl(search_paths_, true);
1225 if(stackages_.count(name))
1226 return stackages_[name];
1227 }
1228
1229 logError(std::string("stack/package ") + name + " not found");
1230 return NULL;
1231 }
1232
1233 bool
1234 Rosstackage::depsDetail(const std::string& name, bool direct,
1235 std::vector<Stackage*>& deps)
1236 {
1237 // No recrawl here, because we're in a recursive function. Rely on the
1238 // top level to do it.
1239 if(!stackages_.count(name))
1240 {
1241 logError(std::string("no such package ") + name);
1242 return false;
1243 }
1244 Stackage* stackage = stackages_[name];
1245 try
1246 {
1247 computeDeps(stackage);
1248 std::vector<Stackage*> deps_vec;
1249 gatherDeps(stackage, direct, POSTORDER, deps_vec);
1250 for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
1251 it != deps_vec.end();
1252 ++it)
1253 deps.push_back(*it);
1254 }
1255 catch(Exception& e)
1256 {
1257 logError(e.what());
1258 return false;
1259 }
1260 return true;
1261 }
1262
1263 void
1264 Rosstackage::depsWhyDetail(Stackage* from,
1265 Stackage* to,
1266 std::list<std::list<Stackage*> >& acc_list)
1267 {
1268 computeDeps(from);
1269 for(std::vector<Stackage*>::const_iterator it = from->deps_.begin();
1270 it != from->deps_.end();
1271 ++it)
1272 {
1273 if((*it)->name_ == to->name_)
1274 {
1275 std::list<Stackage*> acc;
1276 acc.push_back(from);
1277 acc.push_back(to);
1278 acc_list.push_back(acc);
1279 }
1280 else
1281 {
1282 std::list<std::list<Stackage*> > l;
1283 depsWhyDetail(*it, to, l);
1284 for(std::list<std::list<Stackage*> >::iterator iit = l.begin();
1285 iit != l.end();
1286 ++iit)
1287 {
1288 iit->push_front(from);
1289 acc_list.push_back(*iit);
1290 }
1291 }
1292 }
1293 }
1294
1295 bool
1296 Rosstackage::depsOnDetail(const std::string& name, bool direct,
1297 std::vector<Stackage*>& deps, bool ignore_missing)
1298 {
1299 // No recrawl here, because depends-on always forces a crawl at the
1300 // start.
1301 if(!stackages_.count(name))
1302 logWarn(std::string("no such package ") + name);
1303 try
1304 {
1305 for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
1306 it != stackages_.end();
1307 ++it)
1308 {
1309 computeDeps(it->second, true, ignore_missing);
1310 std::vector<Stackage*> deps_vec;
1311 gatherDeps(it->second, direct, POSTORDER, deps_vec);
1312 for(std::vector<Stackage*>::const_iterator iit = deps_vec.begin();
1313 iit != deps_vec.end();
1314 ++iit)
1315 {
1316 if((*iit)->name_ == name)
1317 {
1318 deps.push_back(it->second);
1319 break;
1320 }
1321 }
1322 }
1323 }
1324 catch(Exception& e)
1325 {
1326 logError(e.what());
1327 return false;
1328 }
1329 return true;
1330 }
1331
1332 bool
1333 Rosstackage::profile(const std::vector<std::string>& search_path,
1334 bool zombie_only,
1335 int length,
1336 std::vector<std::string>& dirs)
1337 {
1338 double start = time_since_epoch();
1339 std::vector<DirectoryCrawlRecord*> dcrs;
1340 std::tr1::unordered_set<std::string> dcrs_hash;
1341 for(std::vector<std::string>::const_iterator p = search_path.begin();
1342 p != search_path.end();
1343 ++p)
1344 {
1345 crawlDetail(*p, true, 1, true, dcrs, dcrs_hash);
1346 }
1347 if(!zombie_only)
1348 {
1349 double total = time_since_epoch() - start;
1350 char buf[16];
1351 snprintf(buf, sizeof(buf), "%.6f", total);
1352 dirs.push_back(std::string("Full tree crawl took ") + buf + " seconds.");
1353 dirs.push_back("Directories marked with (*) contain no manifest. You may");
1354 dirs.push_back("want to delete these directories.");
1355 dirs.push_back("To get just of list of directories without manifests,");
1356 dirs.push_back("re-run the profile with --zombie-only");
1357 dirs.push_back("-------------------------------------------------------------");
1358 }
1359 std::sort(dcrs.begin(), dcrs.end(), cmpDirectoryCrawlRecord);
1360 std::reverse(dcrs.begin(), dcrs.end());
1361 int i=0;
1362 for(std::vector<DirectoryCrawlRecord*>::const_iterator it = dcrs.begin();
1363 it != dcrs.end();
1364 ++it)
1365 {
1366 if(zombie_only)
1367 {
1368 if((*it)->zombie_)
1369 {
1370 if(length < 0 || i < length)
1371 dirs.push_back((*it)->path_);
1372 i++;
1373 }
1374 }
1375 else
1376 {
1377 char buf[16];
1378 snprintf(buf, sizeof(buf), "%.6f", (*it)->crawl_time_);
1379 if(length < 0 || i < length)
1380 dirs.push_back(std::string(buf) + " " +
1381 ((*it)->zombie_ ? "* " : " ") +
1382 (*it)->path_);
1383 i++;
1384 }
1385 delete *it;
1386 }
1387
1388 writeCache();
1389 return 0;
1390 }
1391
1392 void
1393 Rosstackage::addStackage(const std::string& path)
1394 {
1395 #if !defined(BOOST_FILESYSTEM_VERSION) || (BOOST_FILESYSTEM_VERSION == 2)
1396 std::string name = fs::path(path).filename();
1397 #else
1398 // in boostfs3, filename() returns a path, which needs to be stringified
1399 std::string name = fs::path(path).filename().string();
1400 #endif
1401
1402 Stackage* stackage = 0;
1403 fs::path dry_manifest_path = fs::path(path) / manifest_name_;
1404 fs::path wet_manifest_path = fs::path(path) / ROSPACKAGE_MANIFEST_NAME;
1405 if(fs::is_regular_file(dry_manifest_path))
1406 {
1407 stackage = new Stackage(name, path, dry_manifest_path.string(), manifest_name_);
1408 }
1409 else if(fs::is_regular_file(wet_manifest_path))
1410 {
1411 stackage = new Stackage(name, path, wet_manifest_path.string(), ROSPACKAGE_MANIFEST_NAME);
1412 loadManifest(stackage);
1413 stackage->update_wet_information();
1414 }
1415 else
1416 {
1417 return;
1418 }
1419
1420 // skip the stackage if it is not of correct type
1421 if((manifest_name_ == ROSSTACK_MANIFEST_NAME && stackage->isPackage()) ||
1422 (manifest_name_ == ROSPACK_MANIFEST_NAME && stackage->isStack()))
1423 {
1424 return;
1425 }
1426
1427 if(stackages_.find(stackage->name_) != stackages_.end())
1428 {
1429 if (dups_.find(stackage->name_) == dups_.end())
1430 {
1431 std::vector<std::string> dups;
1432 dups.push_back(stackages_[stackage->name_]->path_);
1433 dups_[stackage->name_] = dups;
1434 }
1435 dups_[stackage->name_].push_back(stackage->path_);
1436 return;
1437 }
1438
1439 stackages_[stackage->name_] = stackage;
1440 }
1441
1442 void
1443 Rosstackage::crawlDetail(const std::string& path,
1444 bool force,
1445 int depth,
1446 bool collect_profile_data,
1447 std::vector<DirectoryCrawlRecord*>& profile_data,
1448 std::tr1::unordered_set<std::string>& profile_hash)
1449 {
1450 if(depth > MAX_CRAWL_DEPTH)
1451 throw Exception("maximum depth exceeded during crawl");
1452
1453 try
1454 {
1455 if(!fs::is_directory(path))
1456 return;
1457 }
1458 catch(fs::filesystem_error& e)
1459 {
1460 logWarn(std::string("error while looking at ") + path + ": " + e.what());
1461 return;
1462 }
1463
1464 fs::path catkin_ignore = fs::path(path) / CATKIN_IGNORE;
1465 try
1466 {
1467 if(fs::is_regular_file(catkin_ignore))
1468 return;
1469 }
1470 catch(fs::filesystem_error& e)
1471 {
1472 logWarn(std::string("error while looking for ") + catkin_ignore.string() + ": " + e.what());
1473 }
1474
1475 if(isStackage(path))
1476 {
1477 addStackage(path);
1478 return;
1479 }
1480
1481 fs::path nosubdirs = fs::path(path) / ROSPACK_NOSUBDIRS;
1482 try
1483 {
1484 if(fs::is_regular_file(nosubdirs))
1485 return;
1486 }
1487 catch(fs::filesystem_error& e)
1488 {
1489 logWarn(std::string("error while looking for ") + nosubdirs.string() + ": " + e.what());
1490 }
1491
1492 // We've already checked above whether CWD contains the kind of manifest
1493 // we're looking for. Don't recurse if we encounter a rospack manifest,
1494 // to avoid having rosstack finding stacks inside packages, #3816.
1495 fs::path rospack_manifest = fs::path(path) / ROSPACK_MANIFEST_NAME;
1496 try
1497 {
1498 if(fs::is_regular_file(rospack_manifest))
1499 return;
1500 }
1501 catch(fs::filesystem_error& e)
1502 {
1503 logWarn(std::string("error while looking for ") + rospack_manifest.string() + ": " + e.what());
1504 }
1505
1506 DirectoryCrawlRecord* dcr = NULL;
1507 if(collect_profile_data)
1508 {
1509 if(profile_hash.find(path) == profile_hash.end())
1510 {
1511 dcr = new DirectoryCrawlRecord(path,
1512 time_since_epoch(),
1513 stackages_.size());
1514 profile_data.push_back(dcr);
1515 profile_hash.insert(path);
1516 }
1517 }
1518
1519 try
1520 {
1521 for(fs::directory_iterator dit = fs::directory_iterator(path);
1522 dit != fs::directory_iterator();
1523 ++dit)
1524 {
1525 if(fs::is_directory(dit->path()))
1526 {
1527 #if !defined(BOOST_FILESYSTEM_VERSION) || (BOOST_FILESYSTEM_VERSION == 2)
1528 std::string name = dit->path().filename();
1529 #else
1530 // in boostfs3, filename() returns a path, which needs to be stringified
1531 std::string name = dit->path().filename().string();
1532 #endif
1533 // Ignore directories starting with '.'
1534 if(name.size() == 0 || name[0] == '.')
1535 continue;
1536
1537 crawlDetail(dit->path().string(), force, depth+1,
1538 collect_profile_data, profile_data, profile_hash);
1539 }
1540 }
1541 }
1542 catch(fs::filesystem_error& e)
1543 {
1544 logWarn(std::string("error while crawling ") + path + ": " + e.what());
1545 }
1546
1547 if(collect_profile_data && dcr != NULL)
1548 {
1549 // Measure the elapsed time
1550 dcr->crawl_time_ = time_since_epoch() - dcr->start_time_;
1551 // If the number of packages didn't change while crawling,
1552 // then this directory is a zombie
1553 if(stackages_.size() == dcr->start_num_pkgs_)
1554 dcr->zombie_ = true;
1555 }
1556 }
1557
1558 void
1559 Rosstackage::loadManifest(Stackage* stackage)
1560 {
1561 if(stackage->manifest_loaded_)
1562 return;
1563
1564 if(!stackage->manifest_.LoadFile(stackage->manifest_path_))
1565 {
1566 std::string errmsg = std::string("error parsing manifest of package ") +
1567 stackage->name_ + " at " + stackage->manifest_path_;
1568 throw Exception(errmsg);
1569 }
1570 stackage->manifest_loaded_ = true;
1571 }
1572
1573 void
1574 Rosstackage::computeDeps(Stackage* stackage, bool ignore_errors, bool ignore_missing)
1575 {
1576 if(stackage->deps_computed_)
1577 return;
1578
1579 stackage->deps_computed_ = true;
1580
1581 try
1582 {
1583 loadManifest(stackage);
1584 get_manifest_root(stackage);
1585 }
1586 catch(Exception& e)
1587 {
1588 if(ignore_errors)
1589 return;
1590 else
1591 throw e;
1592 }
1593 if (!stackage->is_wet_package_)
1594 {
1595 computeDepsInternal(stackage, ignore_errors, "depend", ignore_missing);
1596 }
1597 else
1598 {
1599 // package format 1 tags
1600 computeDepsInternal(stackage, ignore_errors, "run_depend", ignore_missing);
1601 // package format 2 tags
1602 computeDepsInternal(stackage, ignore_errors, "exec_depend", ignore_missing);
1603 computeDepsInternal(stackage, ignore_errors, "depend", ignore_missing);
1604 }
1605 }
1606
1607 void
1608 Rosstackage::computeDepsInternal(Stackage* stackage, bool ignore_errors, const std::string& depend_tag, bool ignore_missing)
1609 {
1610 TiXmlElement* root;
1611 root = get_manifest_root(stackage);
1612
1613 TiXmlNode *dep_node = NULL;
1614 const char* dep_pkgname;
1615 while((dep_node = root->IterateChildren(depend_tag, dep_node)))
1616 {
1617 TiXmlElement *dep_ele = dep_node->ToElement();
1618 if (!stackage->is_wet_package_)
1619 {
1620 dep_pkgname = dep_ele->Attribute(tag_.c_str());
1621 }
1622 else
1623 {
1624 dep_pkgname = dep_ele->GetText();
1625 }
1626 if(!dep_pkgname)
1627 {
1628 if(!ignore_errors)
1629 {
1630 std::string errmsg = std::string("bad depend syntax (no 'package/stack' attribute) in manifest ") + stackage->name_ + " at " + stackage->manifest_path_;
1631 throw Exception(errmsg);
1632 }
1633 }
1634 else if(dep_pkgname == stackage->name_)
1635 {
1636 if(!ignore_errors)
1637 {
1638 std::string errmsg = std::string("package/stack ") + stackage->name_ + " depends on itself";
1639 throw Exception(errmsg);
1640 }
1641 }
1642 else if(!stackages_.count(dep_pkgname))
1643 {
1644 if (stackage->is_wet_package_ && (ignore_missing || isSysPackage(dep_pkgname)))
1645 {
1646 continue;
1647 }
1648 if(ignore_errors)
1649 {
1650 Stackage* dep = new Stackage(dep_pkgname, "", "", "");
1651 stackage->deps_.push_back(dep);
1652 }
1653 else
1654 {
1655 std::string errmsg = std::string("package/stack '") + stackage->name_ + "' depends on non-existent package '" + dep_pkgname + "' and rosdep claims that it is not a system dependency. Check the ROS_PACKAGE_PATH or try calling 'rosdep update'";
1656 throw Exception(errmsg);
1657 }
1658 }
1659 else
1660 {
1661 Stackage* dep = stackages_[dep_pkgname];
1662 if (std::find(stackage->deps_.begin(), stackage->deps_.end(), dep) == stackage->deps_.end())
1663 {
1664 stackage->deps_.push_back(dep);
1665 computeDeps(dep, ignore_errors, ignore_missing);
1666 }
1667 }
1668 }
1669 }
1670
1671 void
1672 Rosstackage::initPython()
1673 {
1674 static bool initialized = false;
1675 if(!initialized)
1676 {
1677 initialized = true;
1678 Py_InitializeEx(0);
1679 }
1680 }
1681
1682 bool
1683 Rosstackage::isSysPackage(const std::string& pkgname)
1684 {
1685 static std::map<std::string, bool> cache;
1686 if(cache.find(pkgname) != cache.end())
1687 {
1688 return cache.find(pkgname)->second;
1689 }
1690
1691 initPython();
1692 PyGILState_STATE gstate = PyGILState_Ensure();
1693
1694 static PyObject* pModule = 0;
1695 static PyObject* pDict = 0;
1696 if(!pModule)
1697 {
1698 PyObject* pName = PyString_FromString("rosdep2.rospack");
1699 pModule = PyImport_Import(pName);
1700 Py_DECREF(pName);
1701 if(!pModule)
1702 {
1703 PyErr_Print();
1704 PyGILState_Release(gstate);
1705 std::string errmsg = "could not find python module 'rosdep2.rospack'. is rosdep up-to-date (at least 0.10.4)?";
1706 throw Exception(errmsg);
1707 }
1708 pDict = PyModule_GetDict(pModule);
1709 }
1710
1711 static PyObject* pView = 0;
1712 if(!pView)
1713 {
1714 PyObject* pFunc = PyDict_GetItemString(pDict, "init_rospack_interface");
1715 if(!PyCallable_Check(pFunc))
1716 {
1717 PyErr_Print();
1718 PyGILState_Release(gstate);
1719 std::string errmsg = "could not find python function 'rosdep2.rospack.init_rospack_interface'. is rosdep up-to-date (at least 0.10.4)?";
1720 throw Exception(errmsg);
1721 }
1722 pView = PyObject_CallObject(pFunc, NULL);
1723 if(!pView)
1724 {
1725 PyErr_Print();
1726 PyGILState_Release(gstate);
1727 std::string errmsg = "could not call python function 'rosdep2.rospack.init_rospack_interface'";
1728 throw Exception(errmsg);
1729 }
1730 }
1731 static bool rospack_view_not_empty = false;
1732 if(!rospack_view_not_empty)
1733 {
1734 PyObject* pFunc = PyDict_GetItemString(pDict, "is_view_empty");
1735 if(!PyCallable_Check(pFunc))
1736 {
1737 PyErr_Print();
1738 PyGILState_Release(gstate);
1739 std::string errmsg = "could not find python function 'rosdep2.rospack.is_view_empty'. is rosdep up-to-date (at least 0.10.8)?";
1740 throw Exception(errmsg);
1741 }
1742 PyObject* pArgs = PyTuple_New(1);
1743 PyTuple_SetItem(pArgs, 0, pView);
1744 PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
1745 Py_INCREF(pView); // in order to keep the view when garbaging pArgs
1746 Py_DECREF(pArgs);
1747 if(PyObject_IsTrue(pValue))
1748 {
1749 PyErr_Print();
1750 PyGILState_Release(gstate);
1751 std::string errmsg = "the rosdep view is empty: call 'sudo rosdep init' and 'rosdep update'";
1752 throw Exception(errmsg);
1753 }
1754 rospack_view_not_empty = true;
1755 }
1756
1757 PyObject* pFunc = PyDict_GetItemString(pDict, "is_system_dependency");
1758 if(!PyCallable_Check(pFunc))
1759 {
1760 PyErr_Print();
1761 PyGILState_Release(gstate);
1762 std::string errmsg = "could not call python function 'rosdep2.rospack.is_system_dependency'. is rosdep up-to-date (at least 0.10.4)?";
1763 throw Exception(errmsg);
1764 }
1765
1766 PyObject* pArgs = PyTuple_New(2);
1767 PyTuple_SetItem(pArgs, 0, pView);
1768 PyObject* pDep = PyString_FromString(pkgname.c_str());
1769 PyTuple_SetItem(pArgs, 1, pDep);
1770 PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
1771 Py_INCREF(pView); // in order to keep the view when garbaging pArgs
1772 Py_DECREF(pArgs);
1773
1774 bool value = PyObject_IsTrue(pValue);
1775 Py_DECREF(pValue);
1776
1777 // we want to keep the static objects alive for repeated access
1778 // so skip all garbage collection until process ends
1779 //Py_DECREF(pView);
1780 //Py_DECREF(pDict);
1781 //Py_DECREF(pModule);
1782 //Py_Finalize();
1783
1784 PyGILState_Release(gstate);
1785
1786 cache[pkgname] = value;
1787
1788 return value;
1789 }
1790
1791 void
1792 Rosstackage::gatherDeps(Stackage* stackage, bool direct,
1793 traversal_order_t order,
1794 std::vector<Stackage*>& deps,
1795 bool no_recursion_on_wet)
1796 {
1797 std::tr1::unordered_set<Stackage*> deps_hash;
1798 std::vector<std::string> indented_deps;
1799 gatherDepsFull(stackage, direct, order, 0,
1800 deps_hash, deps, false, indented_deps, no_recursion_on_wet);
1801 }
1802
1803 void
1804 _gatherDepsFull(Stackage* stackage, bool direct,
1805 traversal_order_t order, int depth,
1806 std::tr1::unordered_set<Stackage*>& deps_hash,
1807 std::vector<Stackage*>& deps,
1808 bool get_indented_deps,
1809 std::vector<std::string>& indented_deps,
1810 bool no_recursion_on_wet,
1811 std::vector<std::string>& dep_chain)
1812 {
1813 if(stackage->is_wet_package_ && no_recursion_on_wet)
1814 {
1815 return;
1816 }
1817
1818 if(direct && (stackage->is_wet_package_ || !no_recursion_on_wet))
1819 {
1820 for(std::vector<Stackage*>::const_iterator it = stackage->deps_.begin();
1821 it != stackage->deps_.end();
1822 ++it)
1823 deps.push_back(*it);
1824 return;
1825 }
1826
1827 if(depth > MAX_DEPENDENCY_DEPTH) {
1828 std::string cycle;
1829 for(std::vector<std::string>::const_iterator it = dep_chain.begin();
1830 it != dep_chain.end();
1831 ++it)
1832 {
1833 std::vector<std::string>::const_iterator begin = dep_chain.begin();
1834 std::vector<std::string>::const_iterator cycle_begin = std::find(begin, it, *it);
1835 if(cycle_begin != it) {
1836 cycle = ": ";
1837 for(std::vector<std::string>::const_iterator jt = cycle_begin; jt != it; ++jt) {
1838 if(jt != cycle_begin) cycle += ", ";
1839 cycle += *jt;
1840 }
1841 break;
1842 }
1843 }
1844 throw Exception(std::string("maximum dependency depth exceeded (likely circular dependency") + cycle + ")");
1845 }
1846
1847 for(std::vector<Stackage*>::const_iterator it = stackage->deps_.begin();
1848 it != stackage->deps_.end();
1849 ++it)
1850 {
1851 if(get_indented_deps)
1852 {
1853 std::string indented_dep;
1854 for(int i=0; i<depth; i++)
1855 indented_dep.append(" ");
1856 indented_dep.append((*it)->name_);
1857 indented_deps.push_back(indented_dep);
1858 }
1859
1860 bool first = (deps_hash.find(*it) == deps_hash.end());
1861 if(first)
1862 {
1863 deps_hash.insert(*it);
1864 // We maintain the vector because the original rospack guaranteed
1865 // ordering in dep reporting.
1866 if(order == PREORDER)
1867 deps.push_back(*it);
1868 }
1869 if(!(*it)->is_wet_package_ || !no_recursion_on_wet)
1870 {
1871 // We always descend, even if we're encountering this stackage for the
1872 // nth time, so that we'll throw an error on recursive dependencies
1873 // (detected via max stack depth being exceeded).
1874 dep_chain.push_back((*it)->name_);
1875 _gatherDepsFull(*it, direct, order, depth+1, deps_hash, deps,
1876 get_indented_deps, indented_deps,
1877 no_recursion_on_wet, dep_chain);
1878 dep_chain.pop_back();
1879 }
1880 if(first)
1881 {
1882 if(order == POSTORDER)
1883 deps.push_back(*it);
1884 }
1885 }
1886 }
1887
1888 // Pre-condition: computeDeps(stackage) succeeded
1889 void
1890 Rosstackage::gatherDepsFull(Stackage* stackage, bool direct,
1891 traversal_order_t order, int depth,
1892 std::tr1::unordered_set<Stackage*>& deps_hash,
1893 std::vector<Stackage*>& deps,
1894 bool get_indented_deps,
1895 std::vector<std::string>& indented_deps,
1896 bool no_recursion_on_wet)
1897 {
1898 std::vector<std::string> dep_chain;
1899 dep_chain.push_back(stackage->name_);
1900 _gatherDepsFull(stackage, direct,
1901 order, depth,
1902 deps_hash,
1903 deps,
1904 get_indented_deps,
1905 indented_deps,
1906 no_recursion_on_wet,
1907 dep_chain);
1908 }
1909
1910 std::string
1911 Rosstackage::getCachePath()
1912 {
1913 fs::path cache_path;
1914
1915 char* ros_home = getenv("ROS_HOME");
1916 if(ros_home)
1917 cache_path = ros_home;
1918 else
1919 {
1920 // Get the user's home directory by looking up the password entry based
1921 // on UID. If that doesn't work, we fall back on examining $HOME,
1922 // knowing that that can cause trouble when mixed with sudo (#2884).
1923 #if defined(WIN32)
1924 char* home_drive = getenv("HOMEDRIVE");
1925 char* home_path = getenv("HOMEPATH");
1926 if(home_drive && home_path)
1927 cache_path = fs::path(home_drive) / fs::path(home_path) / fs::path(DOTROS_NAME);
1928 #else // UNIX
1929 char* home_path;
1930 struct passwd* passwd_ent;
1931 // Look up based on effective UID, just in case we got here by set-uid
1932 if((passwd_ent = getpwuid(geteuid())))
1933 home_path = passwd_ent->pw_dir;
1934 else
1935 home_path = getenv("HOME");
1936 if(home_path)
1937 cache_path = fs::path(home_path) / fs::path(DOTROS_NAME);
1938 #endif
1939 }
1940
1941 // If it doesn't exist, create the directory that will hold the cache
1942 try
1943 {
1944 if(!fs::is_directory(cache_path))
1945 {
1946 fs::create_directory(cache_path);
1947 }
1948 }
1949 catch(fs::filesystem_error& e)
1950 {
1951 logWarn(std::string("cannot create rospack cache directory ") +
1952 cache_path.string() + ": " + e.what());
1953 }
1954 cache_path /= cache_name_;
1955 return cache_path.string();
1956 }
1957
1958 bool
1959 Rosstackage::readCache()
1960 {
1961 FILE* cache = validateCache();
1962 if(cache)
1963 {
1964 char linebuf[30000];
1965 for(;;)
1966 {
1967 if (!fgets(linebuf, sizeof(linebuf), cache))
1968 break; // error in read operation
1969 if (linebuf[0] == '#')
1970 continue;
1971 char* newline_pos = strchr(linebuf, '\n');
1972 if(newline_pos)
1973 *newline_pos = 0;
1974 addStackage(linebuf);
1975 }
1976 fclose(cache);
1977 return true;
1978 }
1979 else
1980 return false;
1981 }
1982
1983 // TODO: replace the contents of the method with some fancy cross-platform
1984 // boost thing.
1985 void
1986 Rosstackage::writeCache()
1987 {
1988 // Write the results of this crawl to the cache file. At each step, give
1989 // up on error, printing a warning to stderr.
1990 std::string cache_path = getCachePath();
1991 if(!cache_path.size())
1992 {
1993 logWarn("no location available to write cache file. Try setting ROS_HOME or HOME.");
1994 }
1995 else
1996 {
1997 char tmp_cache_dir[PATH_MAX];
1998 char tmp_cache_path[PATH_MAX];
1999 strncpy(tmp_cache_dir, cache_path.c_str(), sizeof(tmp_cache_dir));
2000 #if defined(_MSC_VER)
2001 // No dirname on Windows; use _splitpath_s instead
2002 char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
2003 _splitpath_s(tmp_cache_dir, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
2004 ext, _MAX_EXT);
2005 char full_dir[_MAX_DRIVE + _MAX_DIR];
2006 _makepath_s(full_dir, _MAX_DRIVE + _MAX_DIR, drive, dir, NULL, NULL);
2007 snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s\\.rospack_cache.XXXXXX", full_dir);
2008 #elif defined(__MINGW32__)
2009 char* temp_name = tempnam(dirname(tmp_cache_dir),".rospack_cache.");
2010 snprintf(tmp_cache_path, sizeof(tmp_cache_path), temp_name);
2011 delete temp_name;
2012 #else
2013 snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s/.rospack_cache.XXXXXX", dirname(tmp_cache_dir));
2014 #endif
2015 #if defined(__MINGW32__)
2016 // There is no equivalent of mkstemp or _mktemp_s on mingw, so we resort to a slightly problematic
2017 // tempnam (above) and mktemp method. This has the miniscule chance of a race condition.
2018 int fd = open(tmp_cache_path, O_RDWR | O_EXCL | _O_CREAT, 0644);
2019 if (fd < 0)
2020 {
2021 logWarn(std::string("unable to create temporary cache file ") +
2022 tmp_cache_path, true);
2023 }
2024 else
2025 {
2026 FILE *cache = fdopen(fd, "w");
2027 #elif defined(WIN32)
2028 if (_mktemp_s(tmp_cache_path, PATH_MAX) != 0)
2029 {
2030 fprintf(stderr,
2031 "[rospack] Unable to generate temporary cache file name: %u",
2032 GetLastError());
2033 }
2034 else
2035 {
2036 FILE *cache = fopen(tmp_cache_path, "w");
2037 #else
2038 int fd = mkstemp(tmp_cache_path);
2039 if (fd < 0)
2040 {
2041 fprintf(stderr, "[rospack] Unable to create temporary cache file %s: %s\n",
2042 tmp_cache_path, strerror(errno));
2043 }
2044 else
2045 {
2046 FILE *cache = fdopen(fd, "w");
2047 #endif
2048 if (!cache)
2049 {
2050 fprintf(stderr, "[rospack] Unable open cache file %s: %s\n",
2051 tmp_cache_path, strerror(errno));
2052 }
2053 else
2054 {
2055 // TODO: remove writing of ROS_ROOT
2056 char *rr = getenv("ROS_ROOT");
2057 fprintf(cache, "#ROS_ROOT=%s\n", (rr ? rr : ""));
2058
2059 char *rpp = getenv("ROS_PACKAGE_PATH");
2060 fprintf(cache, "#ROS_PACKAGE_PATH=%s\n", (rpp ? rpp : ""));
2061 for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
2062 it != stackages_.end();
2063 ++it)
2064 fprintf(cache, "%s\n", it->second->path_.c_str());
2065 fclose(cache);
2066 if(fs::exists(cache_path))
2067 remove(cache_path.c_str());
2068 if(rename(tmp_cache_path, cache_path.c_str()) < 0)
2069 {
2070 fprintf(stderr, "[rospack] Error: failed to rename cache file %s to %s: %s\n",
2071 tmp_cache_path, cache_path.c_str(), strerror(errno));
2072 }
2073 }
2074 }
2075 }
2076 }
2077
2078 FILE*
2079 Rosstackage::validateCache()
2080 {
2081 std::string cache_path = getCachePath();
2082 // first see if it's new enough
2083 double cache_max_age = DEFAULT_MAX_CACHE_AGE;
2084 const char *user_cache_time_str = getenv("ROS_CACHE_TIMEOUT");
2085 if(user_cache_time_str)
2086 cache_max_age = atof(user_cache_time_str);
2087 if(cache_max_age == 0.0)
2088 return NULL;
2089 struct stat s;
2090 if(stat(cache_path.c_str(), &s) == 0)
2091 {
2092 double dt = difftime(time(NULL), s.st_mtime);
2093 // Negative cache_max_age means it's always new enough. It's dangerous
2094 // for the user to set this, but rosbash uses it.
2095 if ((cache_max_age > 0.0) && (dt > cache_max_age))
2096 return NULL;
2097 }
2098 // try to open it
2099 FILE* cache = fopen(cache_path.c_str(), "r");
2100 if(!cache)
2101 return NULL; // it's not readable by us. sad.
2102
2103 // see if ROS_PACKAGE_PATH matches
2104 char linebuf[30000];
2105 bool ros_root_ok = false;
2106 bool ros_package_path_ok = false;
2107 // TODO: remove ROS_ROOT
2108 const char* ros_root = getenv("ROS_ROOT");
2109 const char* ros_package_path = getenv("ROS_PACKAGE_PATH");
2110 for(;;)
2111 {
2112 if(!fgets(linebuf, sizeof(linebuf), cache))
2113 break;
2114 linebuf[strlen(linebuf)-1] = 0; // get rid of trailing newline
2115 if (linebuf[0] == '#')
2116 {
2117 if (!strncmp("#ROS_ROOT=", linebuf, 10))
2118 {
2119 if(!ros_root)
2120 {
2121 if(!strlen(linebuf+10))
2122 ros_root_ok = true;
2123 }
2124 else if (!strcmp(linebuf+10, ros_root))
2125 ros_root_ok = true;
2126 }
2127 else if(!strncmp("#ROS_PACKAGE_PATH=", linebuf, 18))
2128 {
2129 if(!ros_package_path)
2130 {
2131 if(!strlen(linebuf+18))
2132 ros_package_path_ok = true;
2133 }
2134 else if(!strcmp(linebuf+18, ros_package_path))
2135 ros_package_path_ok = true;
2136 }
2137 }
2138 else
2139 break; // we're out of the header. nothing more matters to this check.
2140 }
2141 if(ros_root_ok && ros_package_path_ok)
2142 {
2143 // seek to the beginning and pass back the stream (instead of closing
2144 // and later reopening, which is a race condition, #1666)
2145 fseek(cache, 0, SEEK_SET);
2146 return cache;
2147 }
2148 else
2149 {
2150 fclose(cache);
2151 return NULL;
2152 }
2153 }
2154
2155 bool
2156 Rosstackage::expandExportString(Stackage* stackage,
2157 const std::string& instring,
2158 std::string& outstring)
2159 {
2160 outstring = instring;
2161 for(std::string::size_type i = outstring.find(MANIFEST_PREFIX);
2162 i != std::string::npos;
2163 i = outstring.find(MANIFEST_PREFIX))
2164 {
2165 outstring.replace(i, std::string(MANIFEST_PREFIX).length(),
2166 stackage->path_);
2167 }
2168
2169 if (outstring.find_first_of("$`") == std::string::npos)
2170 {
2171 return true;
2172 }
2173
2174 // Do backquote substitution. E.g., if we find this string:
2175 // `pkg-config --cflags gdk-pixbuf-2.0`
2176 // We replace it with the result of executing the command
2177 // contained within the backquotes (reading from its stdout), which
2178 // might be something like:
2179 // -I/usr/include/gtk-2.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
2180
2181 // Construct and execute the string
2182 // We do the assignment first to ensure that if backquote expansion (or
2183 // anything else) fails, we'll get a non-zero exit status from pclose().
2184 std::string cmd = std::string("ret=\"") + outstring + "\" && echo $ret";
2185
2186 // Remove embedded newlines
2187 std::string token("\n");
2188 for (std::string::size_type s = cmd.find(token);
2189 s != std::string::npos;
2190 s = cmd.find(token, s))
2191 cmd.replace(s,token.length(),std::string(" "));
2192
2193 FILE* p;
2194 if(!(p = popen(cmd.c_str(), "r")))
2195 {
2196 std::string errmsg =
2197 std::string("failed to execute backquote expression ") +
2198 cmd + " in " +
2199 stackage->manifest_path_;
2200 logWarn(errmsg, true);
2201 return false;
2202 }
2203 else
2204 {
2205 char buf[8192];
2206 memset(buf,0,sizeof(buf));
2207 // Read the command's output
2208 do
2209 {
2210 clearerr(p);
2211 while(fgets(buf + strlen(buf),sizeof(buf)-strlen(buf)-1,p));
2212 } while(ferror(p) && errno == EINTR);
2213 // Close the subprocess, checking exit status
2214 if(pclose(p) != 0)
2215 {
2216 std::string errmsg =
2217 std::string("got non-zero exit status from executing backquote expression ") +
2218 cmd + " in " +
2219 stackage->manifest_path_;
2220 return false;
2221 }
2222 else
2223 {
2224 // Strip trailing newline, which was added by our call to echo
2225 buf[strlen(buf)-1] = '\0';
2226 // Replace the backquote expression with the new text
2227 outstring = buf;
2228 }
2229 }
2230
2231 return true;
2232 }
2233
2234 /////////////////////////////////////////////////////////////
2235 // Rospack methods
2236 /////////////////////////////////////////////////////////////
2237 Rospack::Rospack() :
2238 Rosstackage(ROSPACK_MANIFEST_NAME,
2239 ROSPACK_CACHE_NAME,
2240 ROSPACK_NAME,
2241 MANIFEST_TAG_PACKAGE)
2242 {
2243 }
2244
2245 Rosstackage::~Rosstackage()
2246 {
2247 for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
2248 it != stackages_.end();
2249 ++it)
2250 {
2251 delete it->second;
2252 }
2253 }
2254
2255 const char*
2256 Rospack::usage()
2257 {
2258 return "USAGE: rospack <command> [options] [package]\n"
2259 " Allowed commands:\n"
2260 " help\n"
2261 " cflags-only-I [--deps-only] [package]\n"
2262 " cflags-only-other [--deps-only] [package]\n"
2263 " depends [package] (alias: deps)\n"
2264 " depends-indent [package] (alias: deps-indent)\n"
2265 " depends-manifests [package] (alias: deps-manifests)\n"
2266 " depends-msgsrv [package] (alias: deps-msgsrv)\n"
2267 " depends-on [package]\n"
2268 " depends-on1 [package]\n"
2269 " depends-why --target=<target> [package] (alias: deps-why)\n"
2270 " depends1 [package] (alias: deps1)\n"
2271 " export [--deps-only] --lang=<lang> --attrib=<attrib> [package]\n"
2272 " find [package]\n"
2273 " langs\n"
2274 " libs-only-L [--deps-only] [package]\n"
2275 " libs-only-l [--deps-only] [package]\n"
2276 " libs-only-other [--deps-only] [package]\n"
2277 " list\n"
2278 " list-duplicates\n"
2279 " list-names\n"
2280 " plugins --attrib=<attrib> [--top=<toppkg>] [package]\n"
2281 " profile [--length=<length>] [--zombie-only]\n"
2282 " rosdep [package] (alias: rosdeps)\n"
2283 " rosdep0 [package] (alias: rosdeps0)\n"
2284 " vcs [package]\n"
2285 " vcs0 [package]\n"
2286 " Extra options:\n"
2287 " -q Quiets error reports.\n\n"
2288 " If [package] is omitted, the current working directory\n"
2289 " is used (if it contains a manifest.xml).\n\n";
2290 }
2291
2292 /////////////////////////////////////////////////////////////
2293 // Rosstack methods
2294 /////////////////////////////////////////////////////////////
2295 Rosstack::Rosstack() :
2296 Rosstackage(ROSSTACK_MANIFEST_NAME,
2297 ROSSTACK_CACHE_NAME,
2298 ROSSTACK_NAME,
2299 MANIFEST_TAG_STACK)
2300 {
2301 }
2302
2303 const char*
2304 Rosstack::usage()
2305 {
2306 return "USAGE: rosstack [options] <command> [stack]\n"
2307 " Allowed commands:\n"
2308 " help\n"
2309 " find [stack]\n"
2310 " contents [stack]\n"
2311 " list\n"
2312 " list-names\n"
2313 " depends [stack] (alias: deps)\n"
2314 " depends-manifests [stack] (alias: deps-manifests)\n"
2315 " depends1 [stack] (alias: deps1)\n"
2316 " depends-indent [stack] (alias: deps-indent)\n"
2317 " depends-why --target=<target> [stack] (alias: deps-why)\n"
2318 " depends-on [stack]\n"
2319 " depends-on1 [stack]\n"
2320 " contains [package]\n"
2321 " contains-path [package]\n"
2322 " profile [--length=<length>] \n\n"
2323 " If [stack] is omitted, the current working directory\n"
2324 " is used (if it contains a stack.xml).\n\n";
2325 }
2326
2327 TiXmlElement*
2328 get_manifest_root(Stackage* stackage)
2329 {
2330 TiXmlElement* ele = stackage->manifest_.RootElement();
2331 if(!ele)
2332 {
2333 std::string errmsg = std::string("error parsing manifest of package ") +
2334 stackage->name_ + " at " + stackage->manifest_path_;
2335 throw Exception(errmsg);
2336 }
2337 return ele;
2338 }
2339
2340 double
2341 time_since_epoch()
2342 {
2343 #if defined(WIN32)
2344 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
2345 #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
2346 #else
2347 #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
2348 #endif
2349 FILETIME ft;
2350 unsigned __int64 tmpres = 0;
2351
2352 GetSystemTimeAsFileTime(&ft);
2353 tmpres |= ft.dwHighDateTime;
2354 tmpres <<= 32;
2355 tmpres |= ft.dwLowDateTime;
2356 tmpres /= 10;
2357 tmpres -= DELTA_EPOCH_IN_MICROSECS;
2358 return static_cast<double>(tmpres) / 1e6;
2359 #else
2360 struct timeval tod;
2361 gettimeofday(&tod, NULL);
2362 return tod.tv_sec + 1e-6 * tod.tv_usec;
2363 #endif
2364 }
2365
2366
2367
2368 } // namespace rospack
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc., Morgan Quigley
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "rospack/rospack_backcompat.h"
28 #include "rospack/rospack.h"
29 #include "rospack_cmdline.h"
30
31 #include <boost/algorithm/string.hpp>
32 #include <string.h>
33 #include <stdio.h>
34 #include <errno.h>
35
36 namespace rospack
37 {
38
39 int
40 ROSPack::run(int argc, char** argv)
41 {
42 rospack::Rospack rp;
43 output_.clear();
44 bool success = rospack::rospack_run(argc, argv, rp, output_);
45 if(!success)
46 {
47 fprintf(stderr, "[librospack]: error while executing command\n");
48 return 1;
49 }
50 return 0;
51 }
52
53 int
54 ROSPack::run(const std::string& cmd)
55 {
56 // Callers of this method don't make 'rospack' argv[0].
57 std::string full_cmd = std::string("rospack ") + cmd;
58
59 int argc;
60 char** argv;
61 std::vector<std::string> full_cmd_split;
62 boost::split(full_cmd_split, full_cmd,
63 boost::is_any_of(" "),
64 boost::token_compress_on);
65 argc = full_cmd_split.size();
66 argv = new char*[argc];
67 int i = 0;
68 for(std::vector<std::string>::const_iterator it = full_cmd_split.begin();
69 it != full_cmd_split.end();
70 ++it)
71 {
72 argv[i] = new char[it->size()+1];
73 memset(argv[i], 0, it->size()+1);
74 memcpy(argv[i], it->c_str(), it->size());
75 i++;
76 }
77
78 int ret = run(argc, argv);
79
80 for(int i=0; i<argc; i++)
81 delete[] argv[i];
82 delete[] argv;
83
84 return ret;
85 }
86
87 } // namespace rospack
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "rospack/rospack.h"
28 #include "utils.h"
29 #include "rospack_cmdline.h"
30
31 #include <boost/program_options.hpp>
32 #include <boost/algorithm/string.hpp>
33 #include <algorithm>
34 #include <iostream>
35 #include <stdlib.h>
36 #include <stdio.h>
37
38 namespace po = boost::program_options;
39
40 namespace rospack
41 {
42
43 bool parse_args(int argc, char** argv,
44 rospack::Rosstackage& rp,
45 po::variables_map& vm);
46
47 bool
48 rospack_run(int argc, char** argv, rospack::Rosstackage& rp, std::string& output)
49 {
50 po::variables_map vm;
51
52 if(!parse_args(argc, argv, rp, vm))
53 return false;
54
55 bool quiet = (vm.count("quiet")==1);
56 rp.setQuiet(quiet);
57
58 std::string command;
59 std::string package;
60 bool package_given = false;
61 bool deps_only = false;
62 std::string lang;
63 std::string attrib;
64 std::string top;
65 std::string target;
66 bool zombie_only = false;
67 std::string length_str;
68 int length;
69 if(vm.count("command"))
70 command = vm["command"].as<std::string>();
71
72 if(vm.count("-h") && command.empty())
73 command = "help";
74
75 if(!command.size())
76 {
77 rp.logError( std::string("no command given. Try '") + rp.getName() + " help'");
78 return true;
79 }
80 // For some commands, we force a crawl. Definitely anything that does a
81 // depends-on calculation.
82 bool force = false;
83 if((command == "profile") ||
84 (command == "depends-on") ||
85 (command == "depends-on1") ||
86 (command == "langs") ||
87 (command == "list-duplicates"))
88 force = true;
89
90 if(vm.count("package"))
91 {
92 package = vm["package"].as<std::string>();
93 package_given = true;
94 }
95 else
96 {
97 // try to determine package from directory context
98 rp.inStackage(package);
99 }
100 if(vm.count("deps-only"))
101 deps_only = true;
102 if(vm.count("lang"))
103 lang = vm["lang"].as<std::string>();
104 if(vm.count("attrib"))
105 attrib = vm["attrib"].as<std::string>();
106 if(vm.count("top"))
107 top = vm["top"].as<std::string>();
108 if(vm.count("target"))
109 target = vm["target"].as<std::string>();
110 if(vm.count("zombie-only"))
111 zombie_only = true;
112 if(vm.count("length"))
113 {
114 length_str = vm["length"].as<std::string>();
115 length = atoi(length_str.c_str());
116 }
117 else
118 {
119 if(zombie_only)
120 length = -1;
121 else
122 length = 20;
123 }
124
125 // COMMAND: help
126 if(command == "help" || vm.count("help"))
127 {
128 if(package_given || (command != "help" && vm.count("help"))) {
129 if (command == "help") {
130 command = vm["package"].as<std::string>();
131 }
132 output.append("Usage: rospack ");
133 output.append(command);
134 if(command == "help")
135 output.append("[command]\n\nPrint help message.");
136 else if(command == "find")
137 output.append("\n\nPrint absolute path to the package");
138 else if(command == "list")
139 output.append("\n\nPrint newline-separated list <package-name> <package-dir> for all packages.");
140 else if(command == "list-names")
141 output.append("\n\nPrint newline-separated list of packages names for all packages.");
142 else if(command == "list-duplicates")
143 output.append("\n\nPrint newline-separated list of names of packages that are found more than once during the search.");
144 else if(command == "langs")
145 output.append("\n\nPrint space-separated list of available language-specific client libraries.");
146 else if(command == "depends" || command == "deps")
147 output.append("[package]\n\nPrint newline-separated, ordered list of all dependencies of the package.");
148 else if(command == "depends1" || command == "deps1")
149 output.append("[package]\n\nPrint newline-separated, ordered list of immediate dependencies of the package.");
150 else if(command == "depends-manifest" || command == "deps-manifest")
151 output.append("[package]\n\nPrint space-separated, ordered list of manifest.xml files for all dependencies of the package. Used internally by rosbuild.");
152 else if(command == "depends-indent" || command == "deps-indent")
153 output.append("[package]\n\nPrint newline-separated, indented list of the entire dependency chain for the package.");
154 else if(command == "depends-why" || command == "deps-why")
155 output.append("--target=TARGET [package]\n\nPrint newline-separated presentation of all dependency chains from the package to TARGET. ");
156 else if(command == "depends-msgsrv" || command == "deps-msgsrv")
157 output.append("[package]\n\nPrint space-separated list of message-generation marker files for all dependencies of the package. Used internally by rosbuild.");
158 else if(command == "rosdep" || command == "rosdeps")
159 output.append("[package]\n\nPrint newline-separated list of all [rosdep] tags from the manifest.xml of the package and all of its dependencies.");
160 else if(command == "rosdep0" || command == "rosdeps0")
161 output.append("[package]\n\nPrint newline-separated list of all [rosdep] tags from the manifest.xml of just the package itself.");
162 else if(command == "vcs")
163 output.append("[package]\n\nPrint newline-separated list of all [versioncontrol] tags from the manifest.xml of the package and all of its dependencies.");
164 else if(command == "vcs0")
165 output.append("[package]\n\nPrint newline-separated list of all [versioncontrol] tags from the manifest.xml of just the package itself.");
166 else if(command == "depends-on")
167 output.append("[package]\n\nPrint newline-separated list of all packages that depend on the package. ");
168 else if(command == "depends-on1")
169 output.append("[package]\n\nPrint newline-separated list of all packages that directly depend on the package.");
170 else if(command == "export")
171 output.append("[--deps-only] --lang=<lang> --attrib=<attrib> [package]\n\nPrint Space-separated list of [export][LANGUAGE ATTRIBUTE=\"\"/][/export] values from the manifest.xml of the package and its dependencies.\n\nIf --deps-only is provided, then the package itself is excluded.");
172 else if(command == "plugins")
173 output.append("--attrib=<attrib> [--top=<toppkg>] [package]\n\nExamine packages that depend directly on the given package, giving name and the exported attribute with the name <attrib>\n\nIf --top=<toppkg> is given, then in addition to depending directly on the given package, to be scanned for exports, a package must also be a dependency of <toppkg>, or be <toppkg> itself.");
174 else if(command == "cflags-only-I")
175 output.append("[--deps-only] [package]\n\nPrint Space-separated list of [export][LANGUAGE ATTRIBUTE=\"\"/][/export] values from the manifest.xml of the package and its dependencies.\n\nIf --deps-only is provided, then the package itself is excluded.");
176 else if(command == "cflags-only-other")
177 output.append("[--deps-only] [package]\n\nPrint space-separated list of export/cpp/cflags that don't start with -I.\n\nIf --deps-only is provided, then the package itself is excluded.");
178 else if(command == "libs-only-L")
179 output.append("[--deps-only] [package]\n\nPrint space-separated list of export/cpp/libs that start with -L.\n\nIf --deps-only is provided, then the package itself is excluded.");
180 else if(command == "libs-only-l")
181 output.append("[--deps-only] [package]\n\nPrint space-separated list of export/cpp/libs that start with -l.\n\nIf --deps-only is provided, then the package itself is excluded.");
182 else if(command == "libs-only-other")
183 output.append("[--deps-only] [package]\n\nPrint space-separated list of export/cpp/libs that don't start with -l or -L.\n\nIf --deps-only is provided, then the package itself is excluded.");
184 else if(command == "profile")
185 output.append("[--length=<length>] [--zombie-only]\n\nForce a full crawl of package directories and report the directories that took the longest time to crawl.\n\n--length=N how many directories to display\n\n--zombie-only Only print directories that do not have any manifests.");
186 output.append("\n");
187 } else {
188 output.append(rp.usage());
189 }
190 return true;
191 }
192
193 std::vector<std::string> search_path;
194 if(!rp.getSearchPathFromEnv(search_path))
195 return false;
196
197 // COMMAND: profile
198 if(command == "profile")
199 {
200 if(package_given || target.size() || top.size() ||
201 deps_only || lang.size() || attrib.size())
202 {
203 rp.logError( "invalid option(s) given");
204 return false;
205 }
206 std::vector<std::string> dirs;
207 if(rp.profile(search_path, zombie_only, length, dirs))
208 return false;
209 for(std::vector<std::string>::const_iterator it = dirs.begin();
210 it != dirs.end();
211 ++it)
212 output.append((*it) + "\n");
213 return true;
214 }
215
216 // We crawl here because profile (above) does its own special crawl.
217 rp.crawl(search_path, force);
218
219 // COMMAND: find [package]
220 if(command == "find")
221 {
222 if(!package.size())
223 {
224 rp.logError( "no package/stack given");
225 return false;
226 }
227 if(target.size() || top.size() || length_str.size() ||
228 zombie_only || deps_only || lang.size() || attrib.size())
229 {
230 rp.logError( "invalid option(s) given");
231 return false;
232 }
233 std::string path;
234 if(!rp.find(package, path))
235 return false;
236 output.append(path + "\n");
237 return true;
238 }
239 // COMMAND: list
240 else if(command == "list")
241 {
242 if(package_given || target.size() || top.size() || length_str.size() ||
243 zombie_only || deps_only || lang.size() || attrib.size())
244 {
245 rp.logError( "invalid option(s) given");
246 return false;
247 }
248 std::set<std::pair<std::string, std::string> > list;
249 rp.list(list);
250 for(std::set<std::pair<std::string, std::string> >::const_iterator it = list.begin();
251 it != list.end();
252 ++it)
253 {
254 output.append(it->first + " " + it->second + "\n");
255 }
256 return true;
257 }
258 // COMMAND: list-names
259 else if(command == "list-names")
260 {
261 if(package_given || target.size() || top.size() || length_str.size() ||
262 zombie_only || deps_only || lang.size() || attrib.size())
263 {
264 rp.logError( "invalid option(s) given");
265 return false;
266 }
267 std::set<std::pair<std::string, std::string> > list;
268 rp.list(list);
269 for(std::set<std::pair<std::string, std::string> >::const_iterator it = list.begin();
270 it != list.end();
271 ++it)
272 {
273 output.append(it->first + "\n");
274 }
275 return true;
276 }
277 // COMMAND: list-duplicates
278 else if(command == "list-duplicates")
279 {
280 if(package_given || target.size() || top.size() || length_str.size() ||
281 zombie_only || deps_only || lang.size() || attrib.size())
282 {
283 rp.logError( "invalid option(s) given");
284 return false;
285 }
286 std::map<std::string, std::vector<std::string> > dups;
287 rp.listDuplicatesWithPaths(dups);
288 // if there are dups, list-duplicates prints them and returns non-zero
289 for(std::map<std::string, std::vector<std::string> >::const_iterator it = dups.begin();
290 it != dups.end();
291 ++it)
292 {
293 output.append(it->first + "\n");
294 for(std::vector<std::string>::const_iterator jt = it->second.begin();
295 jt != it->second.end();
296 ++jt)
297 {
298 output.append("- " + *jt + "\n");
299 }
300 }
301 return true;
302 }
303 // COMMAND: langs
304 else if(rp.getName() == ROSPACK_NAME && command == "langs")
305 {
306 if(package_given || target.size() || top.size() || length_str.size() ||
307 zombie_only || deps_only || lang.size() || attrib.size())
308 {
309 rp.logError( "invalid option(s) given");
310 return false;
311 }
312 std::vector<std::string> deps;
313 if(!rp.depsOn("roslang", true, deps))
314 return false;
315 const char* ros_lang_disable;
316 if((ros_lang_disable = getenv("ROS_LANG_DISABLE")))
317 {
318 std::vector<std::string> disable_langs;
319 // I can't see that boost filesystem has an elegant cross platform
320 // representation for this anywhere like qt/python have.
321 #if defined(WIN32)
322 const char *path_delim = ";";
323 #else //!defined(WIN32)
324 const char *path_delim = ":";
325 #endif
326 boost::split(disable_langs, ros_lang_disable,
327 boost::is_any_of(path_delim),
328 boost::token_compress_on);
329 std::vector<std::string>::iterator it = deps.begin();
330 while(it != deps.end())
331 {
332 if(std::find(disable_langs.begin(), disable_langs.end(), *it) !=
333 disable_langs.end())
334 it = deps.erase(it);
335 else
336 ++it;
337 }
338 }
339 for(std::vector<std::string>::const_iterator it = deps.begin();
340 it != deps.end();
341 ++it)
342 {
343 if(it != deps.begin())
344 output.append(" ");
345 output.append(*it);
346 }
347 output.append("\n");
348 return true;
349 }
350 // COMMAND: depends [package] (alias: deps)
351 else if(command == "depends" || command == "deps" ||
352 command == "depends1" || command == "deps1")
353 {
354 if(!package.size())
355 {
356 rp.logError( "no package/stack given");
357 return false;
358 }
359 if(target.size() || top.size() || length_str.size() ||
360 zombie_only || deps_only || lang.size() || attrib.size())
361 {
362 rp.logError( "invalid option(s) given");
363 return false;
364 }
365 std::vector<std::string> deps;
366 if(!rp.deps(package, (command == "depends1" || command == "deps1"), deps))
367 return false;
368 for(std::vector<std::string>::const_iterator it = deps.begin();
369 it != deps.end();
370 ++it)
371 output.append(*it + "\n");
372 return true;
373 }
374 // COMMAND: depends-manifests [package] (alias: deps-manifests)
375 else if(command == "depends-manifests" || command == "deps-manifests")
376 {
377 if(!package.size())
378 {
379 rp.logError( "no package/stack given");
380 return false;
381 }
382 if(target.size() || top.size() || length_str.size() ||
383 zombie_only || deps_only || lang.size() || attrib.size())
384 {
385 rp.logError( "invalid option(s) given");
386 return false;
387 }
388 std::vector<std::string> manifests;
389 if(!rp.depsManifests(package, false, manifests))
390 return false;
391 for(std::vector<std::string>::const_iterator it = manifests.begin();
392 it != manifests.end();
393 ++it)
394 {
395 if(it != manifests.begin())
396 output.append(" ");
397 output.append(*it);
398 }
399 output.append("\n");
400 return true;
401 }
402 // COMMAND: depends-msgsrv [package] (alias: deps-msgsrv)
403 else if(rp.getName() == ROSPACK_NAME &&
404 (command == "depends-msgsrv" || command == "deps-msgsrv"))
405 {
406 if(!package.size())
407 {
408 rp.logError( "no package given");
409 return false;
410 }
411 if(target.size() || top.size() || length_str.size() ||
412 zombie_only || deps_only || lang.size() || attrib.size())
413 {
414 rp.logError( "invalid option(s) given");
415 return false;
416 }
417 std::vector<std::string> gens;
418 if(!rp.depsMsgSrv(package, false, gens))
419 return false;
420 for(std::vector<std::string>::const_iterator it = gens.begin();
421 it != gens.end();
422 ++it)
423 {
424 if(it != gens.begin())
425 output.append(" ");
426 output.append(*it);
427 }
428 output.append("\n");
429 return true;
430 }
431 // COMMAND: depends-indent [package] (alias: deps-indent)
432 else if(command == "depends-indent" || command == "deps-indent")
433 {
434 if(!package.size())
435 {
436 rp.logError( "no package/stack given");
437 return false;
438 }
439 if(target.size() || top.size() || length_str.size() ||
440 zombie_only || deps_only || lang.size() || attrib.size())
441 {
442 rp.logError( "invalid option(s) given");
443 return false;
444 }
445 std::vector<std::string> deps;
446 if(!rp.depsIndent(package, false, deps))
447 return false;
448 for(std::vector<std::string>::const_iterator it = deps.begin();
449 it != deps.end();
450 ++it)
451 output.append(*it + "\n");
452 return true;
453 }
454 // COMMAND: depends-why [package] (alias: deps-why)
455 else if(command == "depends-why" || command == "deps-why")
456 {
457 if(!package.size() || !target.size())
458 {
459 rp.logError( "no package/stack or target given");
460 return false;
461 }
462 if(top.size() || length_str.size() ||
463 zombie_only || deps_only || lang.size() || attrib.size())
464 {
465 rp.logError( "invalid option(s) given");
466 return false;
467 }
468 std::string why_output;
469 if(!rp.depsWhy(package, target, why_output))
470 return false;
471 output.append(why_output);
472 return true;
473 }
474 // COMMAND: rosdep [package] (alias: rosdeps)
475 // COMMAND: rosdep0 [package] (alias: rosdeps0)
476 else if(rp.getName() == ROSPACK_NAME &&
477 (command == "rosdep" || command == "rosdeps" ||
478 command == "rosdep0" || command == "rosdeps0"))
479 {
480 if(!package.size())
481 {
482 rp.logError( "no package given");
483 return false;
484 }
485 if(target.size() || top.size() || length_str.size() ||
486 zombie_only || deps_only || lang.size() || attrib.size())
487 {
488 rp.logError( "invalid option(s) given");
489 return false;
490 }
491 std::set<std::string> rosdeps;
492 if(!rp.rosdeps(package, (command == "rosdep0" || command == "rosdeps0"), rosdeps))
493 return false;
494 for(std::set<std::string>::const_iterator it = rosdeps.begin();
495 it != rosdeps.end();
496 ++it)
497 output.append(*it + "\n");
498 return true;
499 }
500 // COMMAND: vcs [package]
501 // COMMAND: vcs0 [package]
502 else if(rp.getName() == ROSPACK_NAME &&
503 (command == "vcs" || command == "vcs0"))
504 {
505 if(!package.size())
506 {
507 rp.logError( "no package given");
508 return false;
509 }
510 if(target.size() || top.size() || length_str.size() ||
511 zombie_only || deps_only || lang.size() || attrib.size())
512 {
513 rp.logError( "invalid option(s) given");
514 return false;
515 }
516 std::vector<std::string> vcs;
517 if(!rp.vcs(package, (command == "vcs0"), vcs))
518 return false;
519 for(std::vector<std::string>::const_iterator it = vcs.begin();
520 it != vcs.end();
521 ++it)
522 output.append(*it + "\n");
523 return true;
524 }
525 // COMMAND: depends-on [package]
526 // COMMAND: depends-on1 [package]
527 else if(command == "depends-on" || command == "depends-on1")
528 {
529 if(!package.size())
530 {
531 rp.logError( "no package/stack given");
532 return false;
533 }
534 if(target.size() || top.size() || length_str.size() ||
535 zombie_only || deps_only || lang.size() || attrib.size())
536 {
537 rp.logError( "invalid option(s) given");
538 return false;
539 }
540 std::vector<std::string> deps;
541 if(!rp.depsOn(package, (command == "depends-on1"), deps))
542 return false;
543 for(std::vector<std::string>::const_iterator it = deps.begin();
544 it != deps.end();
545 ++it)
546 output.append(*it + "\n");
547 return true;
548 }
549 // COMMAND: export [--deps-only] --lang=<lang> --attrib=<attrib> [package]
550 else if(rp.getName() == ROSPACK_NAME && command == "export")
551 {
552 if(!package.size() || !lang.size() || !attrib.size())
553 {
554 rp.logError( "no package / lang / attrib given");
555 return false;
556 }
557 if(target.size() || top.size() || length_str.size() || zombie_only)
558 {
559 rp.logError( "invalid option(s) given");
560 return false;
561 }
562 std::vector<std::string> flags;
563 if(!rp.exports(package, lang, attrib, deps_only, flags))
564 return false;
565 for(std::vector<std::string>::const_iterator it = flags.begin();
566 it != flags.end();
567 ++it)
568 {
569 if(it != flags.begin())
570 output.append(" ");
571 output.append(*it);
572 }
573 output.append("\n");
574 return true;
575 }
576 // COMMAND: plugins --attrib=<attrib> [--top=<toppkg>] [package]
577 else if(rp.getName() == ROSPACK_NAME && command == "plugins")
578 {
579 if(!package.size() || !attrib.size())
580 {
581 rp.logError( "no package / attrib given");
582 return false;
583 }
584 if(target.size() || length_str.size() || zombie_only)
585 {
586 rp.logError( "invalid option(s) given");
587 return false;
588 }
589 std::vector<std::string> flags;
590 if(!rp.plugins(package, attrib, top, flags))
591 return false;
592 for(std::vector<std::string>::const_iterator it = flags.begin();
593 it != flags.end();
594 ++it)
595 output.append(*it + "\n");
596 return true;
597 }
598 // COMMAND: cflags-only-I [--deps-only] [package]
599 else if(rp.getName() == ROSPACK_NAME && command == "cflags-only-I")
600 {
601 if(!package.size())
602 {
603 rp.logError( "no package given");
604 return false;
605 }
606 if(target.size() || top.size() || length_str.size() || zombie_only)
607 {
608 rp.logError( "invalid option(s) given");
609 return false;
610 }
611 std::vector<std::pair<std::string, bool> > flags;
612 if(!rp.cpp_exports(package, "--cflags-only-I", "cflags", deps_only, flags))
613 return false;
614
615 std::string dry_combined;
616 std::string wet_combined;
617 for(std::vector<std::pair<std::string, bool> >::const_iterator it = flags.begin();
618 it != flags.end();
619 ++it)
620 {
621 std::string& combined = it->second ? wet_combined : dry_combined;
622 if(!combined.empty())
623 combined.append(" ");
624 combined.append(it->first);
625 }
626
627 std::string dry_result;
628 parse_compiler_flags(dry_combined, "-I", true, false, dry_result);
629 output.append(dry_result);
630
631 std::string wet_result;
632 parse_compiler_flags(wet_combined, "-I", true, false, wet_result);
633 if(!dry_result.empty() && !wet_result.empty())
634 output.append(" ");
635 if(!rp.reorder_paths(wet_result, wet_result))
636 return false;
637 output.append(wet_result + "\n");
638 return true;
639 }
640 // COMMAND: cflags-only-other [--deps-only] [package]
641 else if(rp.getName() == ROSPACK_NAME && command == "cflags-only-other")
642 {
643 if(!package.size())
644 {
645 rp.logError( "no package given");
646 return false;
647 }
648 if(target.size() || top.size() || length_str.size() || zombie_only)
649 {
650 rp.logError( "invalid option(s) given");
651 return false;
652 }
653 std::vector<std::pair<std::string, bool> > flags;
654 if(!rp.cpp_exports(package, "--cflags-only-other", "cflags", deps_only, flags))
655 return false;
656 std::string combined;
657 for(std::vector<std::pair<std::string, bool> >::const_iterator it = flags.begin();
658 it != flags.end();
659 ++it)
660 {
661 if(it != flags.begin())
662 combined.append(" ");
663 combined.append(it->first);
664 }
665 std::string result;
666 parse_compiler_flags(combined, "-I", false, false, result);
667 output.append(result + "\n");
668 return true;
669 }
670 // COMMAND: libs-only-L [--deps-only] [package]
671 else if(rp.getName() == ROSPACK_NAME && command == "libs-only-L")
672 {
673 if(!package.size())
674 {
675 rp.logError( "no package given");
676 return false;
677 }
678 if(target.size() || top.size() || length_str.size() || zombie_only)
679 {
680 rp.logError( "invalid option(s) given");
681 return false;
682 }
683 std::vector<std::pair<std::string, bool> > flags;
684 if(!rp.cpp_exports(package, "--libs-only-L", "lflags", deps_only, flags))
685 return false;
686
687 std::string dry_combined;
688 std::string wet_combined;
689 for(std::vector<std::pair<std::string, bool> >::const_iterator it = flags.begin();
690 it != flags.end();
691 ++it)
692 {
693 std::string& combined = it->second ? wet_combined : dry_combined;
694 if(!combined.empty())
695 combined.append(" ");
696 combined.append(it->first);
697 }
698
699 std::string dry_result;
700 parse_compiler_flags(dry_combined, "-L", true, false, dry_result);
701 output.append(dry_result);
702
703 std::string wet_result;
704 parse_compiler_flags(wet_combined, "-L", true, false, wet_result);
705 if(!dry_result.empty() && !wet_result.empty())
706 output.append(" ");
707 if(!rp.reorder_paths(wet_result, wet_result))
708 return false;
709 output.append(wet_result + "\n");
710 return true;
711 }
712 // COMMAND: libs-only-l [--deps-only] [package]
713 else if(rp.getName() == ROSPACK_NAME && command == "libs-only-l")
714 {
715 if(!package.size())
716 {
717 rp.logError( "no package given");
718 return false;
719 }
720 if(target.size() || top.size() || length_str.size() || zombie_only)
721 {
722 rp.logError( "invalid option(s) given");
723 return false;
724 }
725 std::vector<std::pair<std::string, bool> > flags;
726 if(!rp.cpp_exports(package, "--libs-only-l", "lflags", deps_only, flags))
727 return false;
728 std::string combined;
729 for(std::vector<std::pair<std::string, bool> >::const_iterator it = flags.begin();
730 it != flags.end();
731 ++it)
732 {
733 if(it != flags.begin())
734 combined.append(" ");
735 combined.append(it->first);
736 }
737 std::string result;
738 parse_compiler_flags(combined, "-l", true, true, result);
739 output.append(result + "\n");
740 return true;
741 }
742 // COMMAND: libs-only-other [--deps-only] [package]
743 else if(rp.getName() == ROSPACK_NAME && command == "libs-only-other")
744 {
745 if(!package.size())
746 {
747 rp.logError( "no package given");
748 return false;
749 }
750 if(target.size() || top.size() || length_str.size() || zombie_only)
751 {
752 rp.logError( "invalid option(s) given");
753 return false;
754 }
755 std::vector<std::pair<std::string, bool> > flags;
756 if(!rp.cpp_exports(package, "--libs-only-other", "lflags", deps_only, flags))
757 return false;
758 std::string combined;
759 for(std::vector<std::pair<std::string, bool> >::const_iterator it = flags.begin();
760 it != flags.end();
761 ++it)
762 {
763 if(it != flags.begin())
764 combined.append(" ");
765 combined.append(it->first);
766 }
767 std::string intermediate;
768 parse_compiler_flags(combined, "-L", false, false, intermediate);
769 std::string result;
770 parse_compiler_flags(intermediate, "-l", false, false, result);
771 output.append(result + "\n");
772 return true;
773 }
774 // COMMAND: contents [stack]
775 else if(rp.getName() == ROSSTACK_NAME && command == "contents")
776 {
777 if(!package.size())
778 {
779 rp.logError( "no stack given");
780 return false;
781 }
782 if(target.size() || top.size() || length_str.size() ||
783 zombie_only || deps_only || lang.size() || attrib.size())
784 {
785 rp.logError( "invalid option(s) given");
786 return false;
787 }
788
789 std::set<std::string> packages;
790 rp.contents(package, packages);
791 for(std::set<std::string>::const_iterator it = packages.begin();
792 it != packages.end();
793 ++it)
794 output.append(*it + "\n");
795 return true;
796 }
797 // COMMAND: contains [package]
798 else if(rp.getName() == ROSSTACK_NAME &&
799 ((command == "contains") || (command == "contains-path")))
800 {
801 if(!package.size())
802 {
803 rp.logError( "no package given");
804 return false;
805 }
806 if(target.size() || top.size() || length_str.size() ||
807 zombie_only || deps_only || lang.size() || attrib.size())
808 {
809 rp.logError( "invalid option(s) given");
810 return false;
811 }
812 std::string name, path;
813 if(!rp.contains(package, name, path))
814 return false;
815 if(command == "contains")
816 output.append(name + "\n");
817 else // command == "contains-path"
818 output.append(path + "\n");
819 return true;
820 }
821 else
822 {
823 rp.logError(std::string("command ") + command + " not implemented");
824 return false;
825 }
826 }
827
828 bool
829 parse_args(int argc, char** argv,
830 rospack::Rosstackage& rp, po::variables_map& vm)
831 {
832 po::options_description desc("Allowed options");
833 desc.add_options()
834 ("command", po::value<std::string>(), "command")
835 ("package", po::value<std::string>(), "package")
836 ("target", po::value<std::string>(), "target")
837 ("deps-only", "deps-only")
838 ("lang", po::value<std::string>(), "lang")
839 ("attrib", po::value<std::string>(), "attrib")
840 ("top", po::value<std::string>(), "top")
841 ("length", po::value<std::string>(), "length")
842 ("zombie-only", "zombie-only")
843 ("help", "help")
844 ("-h", "help")
845 ("quiet,q", "quiet");
846
847 po::positional_options_description pd;
848 pd.add("command", 1).add("package", 1);
849 try
850 {
851 po::store(po::command_line_parser(argc, argv).options(desc).positional(pd).run(), vm);
852 }
853 catch(boost::program_options::error e)
854 {
855 rp.logError( std::string("failed to parse command-line options: ") + e.what());
856 return false;
857 }
858 po::notify(vm);
859
860 return true;
861 }
862
863 }
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #ifndef ROSPACK_ROSPACK_CMDLINE_H
28 #define ROSPACK_ROSPACK_CMDLINE_H
29
30 #include "rospack/macros.h"
31 #include "rospack/rospack.h"
32
33 namespace rospack
34 {
35
36 ROSPACK_DECL bool rospack_run(int argc, char** argv,
37 rospack::Rosstackage& rp,
38 std::string& output);
39
40 }
41
42 #endif
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "rospack_cmdline.h"
28 #include <stdio.h>
29
30 int
31 main(int argc, char** argv)
32 {
33 rospack::Rospack rp;
34 std::string output;
35 if(!rospack::rospack_run(argc, argv, rp, output))
36 return 1;
37 else
38 {
39 printf("%s", output.c_str());
40 return 0;
41 }
42 }
43
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "rospack_cmdline.h"
28 #include <stdio.h>
29
30 int
31 main(int argc, char** argv)
32 {
33 rospack::Rosstack rs;
34 std::string output;
35 if(!rospack::rospack_run(argc, argv, rs, output))
36 return 1;
37 else
38 {
39 printf("%s", output.c_str());
40 return 0;
41 }
42 }
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <string>
28 #include <vector>
29 #include <boost/algorithm/string.hpp>
30 #include <boost/tr1/unordered_set.hpp>
31
32 #include "utils.h"
33
34 namespace rospack
35 {
36
37 void
38 deduplicate_tokens(const std::string& instring,
39 bool last,
40 std::string& outstring)
41 {
42 std::vector<std::string> vec;
43 std::tr1::unordered_set<std::string> set;
44 boost::split(vec, instring,
45 boost::is_any_of("\t "),
46 boost::token_compress_on);
47 if(last)
48 std::reverse(vec.begin(), vec.end());
49 std::vector<std::string> vec_out;
50 for(std::vector<std::string>::const_iterator it = vec.begin();
51 it != vec.end();
52 ++it)
53 {
54 if(set.find(*it) == set.end())
55 {
56 vec_out.push_back(*it);
57 set.insert(*it);
58 }
59 }
60 if(last)
61 std::reverse(vec_out.begin(), vec_out.end());
62 for(std::vector<std::string>::const_iterator it = vec_out.begin();
63 it != vec_out.end();
64 ++it)
65 {
66 if(it == vec_out.begin())
67 outstring.append(*it);
68 else
69 outstring.append(std::string(" ") + *it);
70 }
71 }
72
73 void
74 parse_compiler_flags(const std::string& instring,
75 const std::string& token,
76 bool select,
77 bool last,
78 std::string& outstring)
79 {
80 std::string intermediate;
81 std::vector<std::string> result_vec;
82 boost::split(result_vec, instring,
83 boost::is_any_of("\t "),
84 boost::token_compress_on);
85 for(std::vector<std::string>::const_iterator it = result_vec.begin();
86 it != result_vec.end();
87 ++it)
88 {
89 // Combined into one arg
90 if(it->size() > token.size() && it->substr(0,token.size()) == token)
91 {
92 if(select)
93 {
94 if(intermediate.size())
95 intermediate.append(" ");
96 intermediate.append(it->substr(token.size()));
97 }
98 }
99 // Space-separated
100 else if((*it) == token)
101 {
102 std::vector<std::string>::const_iterator iit = it;
103 if(++iit != result_vec.end())
104 {
105 if(it->size() >= token.size() && it->substr(0,token.size()) == token)
106 {
107 // skip it
108 }
109 else
110 {
111 if(select)
112 {
113 if(intermediate.size())
114 intermediate.append(" ");
115 intermediate.append((*iit));
116 }
117 it = iit;
118 }
119 }
120 }
121 // Special case: if we're told to look for -l, then also find *.a
122 else if(it->size() > 2 &&
123 (*it)[0] == '/' &&
124 it->substr(it->size()-2) == ".a")
125 {
126 if(select)
127 {
128 if(intermediate.size())
129 intermediate.append(" ");
130 intermediate.append((*it));
131 }
132 }
133 else if(!select)
134 {
135 if(intermediate.size())
136 intermediate.append(" ");
137 intermediate.append((*it));
138 }
139 }
140 if(select)
141 deduplicate_tokens(intermediate, last, outstring);
142 else
143 outstring = intermediate;
144 }
145
146 }
147
0 /*
1 * Copyright (C) 2008, Willow Garage, Inc.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright notice,
6 * this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #ifndef ROSPACK_UTILS_H
28 #define ROSPACK_UTILS_H
29
30 #include <string>
31 #include "rospack/macros.h"
32
33 namespace rospack
34 {
35
36 // Strings that are used in multiple translation units within librospack
37 static const char* ROSPACK_NAME = "rospack";
38 static const char* ROSSTACK_NAME = "rosstack";
39
40 ROSPACK_DECL void deduplicate_tokens(const std::string& instring,
41 bool last,
42 std::string& outstring);
43
44 ROSPACK_DECL void parse_compiler_flags(const std::string& instring,
45 const std::string& token,
46 bool select,
47 bool last,
48 std::string& outstring);
49
50 }
51
52 #endif
53
0 include_directories(${PROJECT_SOURCE_DIR}/src)
1 catkin_add_gtest(${PROJECT_NAME}-utest
2 test/utest.cpp ${PROJECT_SOURCE_DIR}/src/rospack_cmdline.cpp ${PROJECT_SOURCE_DIR}/src/utils.cpp
3 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
4 if(TARGET ${PROJECT_NAME}-utest)
5 target_link_libraries(${PROJECT_NAME}-utest rospack)
6 endif()
7
8 # Prepare to run the tests. This could be cleaner.
9 add_custom_target(${PROJECT_NAME}-prepare_test
10 COMMAND cmake -E copy_directory ${PROJECT_SOURCE_DIR}/test ${CMAKE_CURRENT_BINARY_DIR}
11 COMMAND cmake -E chdir test/deep python deep.py)
12 if(TARGET ${PROJECT_NAME}-utest)
13 add_dependencies(${PROJECT_NAME}-utest ${PROJECT_NAME}-prepare_test)
14 endif()
15
16 configure_file(test/utest.py.in
17 ${CMAKE_CURRENT_BINARY_DIR}/test/utest.py)
18 configure_file(test/utest_rosstack.py.in
19 ${CMAKE_CURRENT_BINARY_DIR}/test/utest_rosstack.py)
20 catkin_add_nosetests(${CMAKE_CURRENT_BINARY_DIR}/test
21 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test)
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn"
5 url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
6 </package>
7
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn"
5 url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
6 </package>
7
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 /Users/gerkey/code/personalrobots/exec_trex
1 /Users/gerkey/code/personalrobots/pr2_msgs
2 /Users/gerkey/code/personalrobots/pr2_srvs
3 /Users/gerkey/code/personalrobots/robot_msgs
4 /Users/gerkey/code/personalrobots/robot_srvs
5 /Users/gerkey/code/personalrobots/3rdparty/boost
6 /Users/gerkey/code/personalrobots/3rdparty/Cg
7 /Users/gerkey/code/personalrobots/3rdparty/drawstuff
8 /Users/gerkey/code/personalrobots/3rdparty/eml
9 /Users/gerkey/code/personalrobots/3rdparty/estar
10 /Users/gerkey/code/personalrobots/3rdparty/ffmpeg
11 /Users/gerkey/code/personalrobots/3rdparty/freeimage
12 /Users/gerkey/code/personalrobots/3rdparty/gazebo
13 /Users/gerkey/code/personalrobots/3rdparty/gmapping
14 /Users/gerkey/code/personalrobots/3rdparty/irrlicht
15 /Users/gerkey/code/personalrobots/3rdparty/kdl
16 /Users/gerkey/code/personalrobots/3rdparty/kni-3.9.2
17 /Users/gerkey/code/personalrobots/3rdparty/libdc1394v2
18 /Users/gerkey/code/personalrobots/3rdparty/libsunflower
19 /Users/gerkey/code/personalrobots/3rdparty/loki
20 /Users/gerkey/code/personalrobots/3rdparty/newmat10
21 /Users/gerkey/code/personalrobots/3rdparty/ogre
22 /Users/gerkey/code/personalrobots/3rdparty/ois
23 /Users/gerkey/code/personalrobots/3rdparty/opencv
24 /Users/gerkey/code/personalrobots/3rdparty/opencv_latest
25 /Users/gerkey/code/personalrobots/3rdparty/opende
26 /Users/gerkey/code/personalrobots/3rdparty/pil
27 /Users/gerkey/code/personalrobots/3rdparty/player
28 /Users/gerkey/code/personalrobots/3rdparty/plplot
29 /Users/gerkey/code/personalrobots/3rdparty/rtnet
30 /Users/gerkey/code/personalrobots/3rdparty/sdl
31 /Users/gerkey/code/personalrobots/3rdparty/sdl_image
32 /Users/gerkey/code/personalrobots/3rdparty/sicktoolbox
33 /Users/gerkey/code/personalrobots/3rdparty/stage
34 /Users/gerkey/code/personalrobots/3rdparty/trex
35 /Users/gerkey/code/personalrobots/3rdparty/velodyne-driver
36 /Users/gerkey/code/personalrobots/3rdparty/VTK
37 /Users/gerkey/code/personalrobots/3rdparty/wxmpl
38 /Users/gerkey/code/personalrobots/3rdparty/wxpropgrid
39 /Users/gerkey/code/personalrobots/3rdparty/xenomai
40 /Users/gerkey/code/personalrobots/controllers/generic_controllers
41 /Users/gerkey/code/personalrobots/controllers/pr2_controllers
42 /Users/gerkey/code/personalrobots/demos/2dnav-gazebo
43 /Users/gerkey/code/personalrobots/demos/2dnav-stage
44 /Users/gerkey/code/personalrobots/demos/stair1-demos
45 /Users/gerkey/code/personalrobots/deprecated/etherdrive_old
46 /Users/gerkey/code/personalrobots/deprecated/genericControllers
47 /Users/gerkey/code/personalrobots/deprecated/hw_interface
48 /Users/gerkey/code/personalrobots/deprecated/libKDL
49 /Users/gerkey/code/personalrobots/deprecated/old_mechanism_control
50 /Users/gerkey/code/personalrobots/deprecated/pr2Controllers
51 /Users/gerkey/code/personalrobots/deprecated/robot_mechanism_model
52 /Users/gerkey/code/personalrobots/deprecated/rosControllers
53 /Users/gerkey/code/personalrobots/deprecated/testControllers
54 /Users/gerkey/code/personalrobots/deprecated/unstable_msgs
55 /Users/gerkey/code/personalrobots/grasp_module/grasp_learner
56 /Users/gerkey/code/personalrobots/grasp_module/grasp_module
57 /Users/gerkey/code/personalrobots/grasp_module/grasp_module_node
58 /Users/gerkey/code/personalrobots/grasp_module/grasp_planner
59 /Users/gerkey/code/personalrobots/grasp_module/object_detector
60 /Users/gerkey/code/personalrobots/hardware_test/cmdline_selftest
61 /Users/gerkey/code/personalrobots/hardware_test/motor_qualification
62 /Users/gerkey/code/personalrobots/hardware_test/runtime_monitor
63 /Users/gerkey/code/personalrobots/hardware_test/self_test
64 /Users/gerkey/code/personalrobots/highlevel/rubot
65 /Users/gerkey/code/personalrobots/manip/arm_trajectory
66 /Users/gerkey/code/personalrobots/manip/overhead_grasp_behavior
67 /Users/gerkey/code/personalrobots/manip/pr2_kinematic_controllers
68 /Users/gerkey/code/personalrobots/manip/teleop_arm_keyboard
69 /Users/gerkey/code/personalrobots/mechanism/hardware_interface
70 /Users/gerkey/code/personalrobots/mechanism/mechanism_control
71 /Users/gerkey/code/personalrobots/mechanism/mechanism_model
72 /Users/gerkey/code/personalrobots/motion_planning/collision_space
73 /Users/gerkey/code/personalrobots/motion_planning/issue_kinematic_plan
74 /Users/gerkey/code/personalrobots/motion_planning/kinematic_planning
75 /Users/gerkey/code/personalrobots/motion_planning/ompl
76 /Users/gerkey/code/personalrobots/motion_planning/plan_kinematic_path
77 /Users/gerkey/code/personalrobots/motion_planning/planning_models
78 /Users/gerkey/code/personalrobots/motion_planning/planning_node_util
79 /Users/gerkey/code/personalrobots/motion_planning/planning_world_viewer
80 /Users/gerkey/code/personalrobots/motion_planning/sbpl
81 /Users/gerkey/code/personalrobots/motion_planning/test_collision_space
82 /Users/gerkey/code/personalrobots/nav/amcl_player
83 /Users/gerkey/code/personalrobots/nav/deadreckon
84 /Users/gerkey/code/personalrobots/nav/fake_localization
85 /Users/gerkey/code/personalrobots/nav/laser_pose_interpolator
86 /Users/gerkey/code/personalrobots/nav/nav_view
87 /Users/gerkey/code/personalrobots/nav/sbpl_2dnav
88 /Users/gerkey/code/personalrobots/nav/slam_gmapping
89 /Users/gerkey/code/personalrobots/nav/teleop_base
90 /Users/gerkey/code/personalrobots/nav/teleop_base_keyboard
91 /Users/gerkey/code/personalrobots/nav/teleop_base_overhead_cam
92 /Users/gerkey/code/personalrobots/nav/wavefront_player
93 /Users/gerkey/code/personalrobots/robot_control_loops/pr2_etherCAT
94 /Users/gerkey/code/personalrobots/robot_control_loops/pr2_etherDrive
95 /Users/gerkey/code/personalrobots/robot_control_loops/pr2_gazebo
96 /Users/gerkey/code/personalrobots/robot_descriptions/gazebo_robot_description
97 /Users/gerkey/code/personalrobots/robot_descriptions/wg_robot_description
98 /Users/gerkey/code/personalrobots/robot_descriptions/wg_robot_description_parser
99 /Users/gerkey/code/personalrobots/simulators/flatland
100 /Users/gerkey/code/personalrobots/simulators/gazebo_map_extruder
101 /Users/gerkey/code/personalrobots/simulators/nepumuk
102 /Users/gerkey/code/personalrobots/simulators/rosgazebo
103 /Users/gerkey/code/personalrobots/simulators/rosstage
104 /Users/gerkey/code/personalrobots/util/display_ode_spaces
105 /Users/gerkey/code/personalrobots/util/image_utils
106 /Users/gerkey/code/personalrobots/util/laser_scan_utils
107 /Users/gerkey/code/personalrobots/util/libTF
108 /Users/gerkey/code/personalrobots/util/logging
109 /Users/gerkey/code/personalrobots/util/logsetta
110 /Users/gerkey/code/personalrobots/util/math_utils
111 /Users/gerkey/code/personalrobots/util/megamaid
112 /Users/gerkey/code/personalrobots/util/misc_utils
113 /Users/gerkey/code/personalrobots/util/mux
114 /Users/gerkey/code/personalrobots/util/profiling_utils
115 /Users/gerkey/code/personalrobots/util/pyrob
116 /Users/gerkey/code/personalrobots/util/random_utils
117 /Users/gerkey/code/personalrobots/util/rosTF
118 /Users/gerkey/code/personalrobots/util/sdlgl
119 /Users/gerkey/code/personalrobots/util/simple_options
120 /Users/gerkey/code/personalrobots/util/stl_utils
121 /Users/gerkey/code/personalrobots/util/string_utils
122 /Users/gerkey/code/personalrobots/vision/borg_tuner
123 /Users/gerkey/code/personalrobots/vision/calib_converter
124 /Users/gerkey/code/personalrobots/vision/cam_viewer
125 /Users/gerkey/code/personalrobots/vision/cam_viewer_py
126 /Users/gerkey/code/personalrobots/vision/cv_blob_tracker
127 /Users/gerkey/code/personalrobots/vision/cv_cam_calib
128 /Users/gerkey/code/personalrobots/vision/cv_movie_streamer
129 /Users/gerkey/code/personalrobots/vision/cv_view
130 /Users/gerkey/code/personalrobots/vision/dorylus
131 /Users/gerkey/code/personalrobots/vision/gmmseg
132 /Users/gerkey/code/personalrobots/vision/laser_interface
133 /Users/gerkey/code/personalrobots/vision/lasiklite
134 /Users/gerkey/code/personalrobots/vision/scan_utils
135 /Users/gerkey/code/personalrobots/vision/spectacles
136 /Users/gerkey/code/personalrobots/vision/urg_ipdcmot
137 /Users/gerkey/code/personalrobots/visualization/cloud_viewer
138 /Users/gerkey/code/personalrobots/visualization/irrlicht_viewer
139 /Users/gerkey/code/personalrobots/visualization/log_gui
140 /Users/gerkey/code/personalrobots/visualization/ogre_tools
141 /Users/gerkey/code/personalrobots/visualization/ogre_visualizer
142 /Users/gerkey/code/personalrobots/visualization/pr2_gui
143 /Users/gerkey/code/personalrobots/visualization/scene_labeler
144 /Users/gerkey/code/personalrobots/visualization/wx_camera_panel
145 /Users/gerkey/code/personalrobots/visualization/wx_roserr
146 /Users/gerkey/code/personalrobots/visualization/wx_topic_display
147 /Users/gerkey/code/personalrobots/visualization/wxpy_ros
148 /Users/gerkey/code/personalrobots/world_models/costmap_2d
149 /Users/gerkey/code/personalrobots/world_models/fake_world_3d_map
150 /Users/gerkey/code/personalrobots/world_models/map_server
151 /Users/gerkey/code/personalrobots/world_models/world_3d_map
152 /Users/gerkey/code/personalrobots/drivers/cam/axis_cam
153 /Users/gerkey/code/personalrobots/drivers/cam/bumblebee_bridge
154 /Users/gerkey/code/personalrobots/drivers/cam/cv_cam
155 /Users/gerkey/code/personalrobots/drivers/cam/dc1394_cam
156 /Users/gerkey/code/personalrobots/drivers/cam/dc1394_cam_server
157 /Users/gerkey/code/personalrobots/drivers/cam/elphel_cam
158 /Users/gerkey/code/personalrobots/drivers/cam/flea2
159 /Users/gerkey/code/personalrobots/drivers/cam/videre_cam
160 /Users/gerkey/code/personalrobots/drivers/generic/serial_port
161 /Users/gerkey/code/personalrobots/drivers/imu/3dmgx2_driver
162 /Users/gerkey/code/personalrobots/drivers/imu/imu_node
163 /Users/gerkey/code/personalrobots/drivers/imu/simple_estimator
164 /Users/gerkey/code/personalrobots/drivers/input/joy
165 /Users/gerkey/code/personalrobots/drivers/input/joy_view
166 /Users/gerkey/code/personalrobots/drivers/laser/hokuyourg_player
167 /Users/gerkey/code/personalrobots/drivers/laser/laser_view
168 /Users/gerkey/code/personalrobots/drivers/laser/sicktoolbox_wrapper
169 /Users/gerkey/code/personalrobots/drivers/laser/urg_driver
170 /Users/gerkey/code/personalrobots/drivers/motor/ethercat_hardware
171 /Users/gerkey/code/personalrobots/drivers/motor/etherdrive
172 /Users/gerkey/code/personalrobots/drivers/motor/etherdrive_hardware
173 /Users/gerkey/code/personalrobots/drivers/robot/erratic_player
174 /Users/gerkey/code/personalrobots/drivers/robot/ipdcmot
175 /Users/gerkey/code/personalrobots/drivers/robot/katana
176 /Users/gerkey/code/personalrobots/drivers/robot/segway_apox
177 /Users/gerkey/code/personalrobots/drivers/robot/sensor_cart
178 /Users/gerkey/code/personalrobots/drivers/simulator/gazebo_hardware
179 /Users/gerkey/code/personalrobots/drivers/simulator/gazebo_plugin
180 /Users/gerkey/code/personalrobots/drivers/simulator/gazebo_sensors
181 /Users/gerkey/code/personalrobots/hardware_test/sensors/hokuyo_tester
182 /Users/gerkey/code/personalrobots/nav/controllers/libtrajectory_rollout
183 /Users/gerkey/code/personalrobots/util/kinematics/libKinematics
184 /Users/gerkey/code/personalrobots/util/kinematics/robot_kinematics
185 /Users/gerkey/code/personalrobots/vision/camera_grab_samples/bumblebee_grab_sample
186 /Users/gerkey/code/personalrobots/drivers/robot/pr2/libpr2API
187 /Users/gerkey/code/personalrobots/drivers/robot/pr2/libpr2HW
188 /Users/gerkey/code/personalrobots/drivers/robot/pr2/pr2Core
189 /Users/gerkey/code/personalrobots/drivers/robot/pr2/tilting_laser
0 <package>
1 <export>
2 <!-- really try to stress backquote interpretation here, including making sure ${prefix} has precedence -->
3 <cpp lflags="-lloki `echo -lfoo` `echo -Lodin \`cat ${prefix}/backquote-l\``" cflags="-Iblah `cat ${prefix}/backquote-I`"/>
4 </export>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="`/foo/bar/gobbledygook`"/>
3 </export>
4 </package>
0 <package>
1 <export>
2 <cpp lflags="-lfoo" cflags="-Isomething"/>
3 <roslang cmake="something.cmake"/>
4 </export>
5 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
6 <rosdep name="foo"/>
7 </package>
0 <package>
1 <export>
2 <cpp lflags="-lbar"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/branches"/>
5 </package>
6
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2009, Willow Garage, Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials provided
14 # with the distribution.
15 # * Neither the name of Willow Garage, Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31 import sys
32 import os
33
34 template = """
35 <package>
36 <export>
37 <cpp lflags="-llib%s"/>
38 </export>
39 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
40 <depend package="depth-%s" />
41 </package>
42 """
43
44 final_template = """
45 <package>
46 <export>
47 <cpp lflags="-llib%s"/>
48 </export>
49 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
50 </package>
51 """
52
53 package = "depth-%s"
54
55 def write_manifest(dir, manifest):
56 if not os.path.exists(dir):
57 os.mkdir(dir)
58 file = os.path.join(dir, 'manifest.xml')
59 try:
60 f = open(file, 'w')
61 print("generating %s"%file)
62 f.write(manifest)
63 finally:
64 f.close()
65
66 for i in range(1, 101):
67 packagename = package%(i-1)
68 manifest = template%(i-1, i)
69 write_manifest(packagename, manifest)
70
71 write_manifest('depth-100', final_template%100)
72
0 <package>
1 <export>
2 <cpp lflags="-lloki"/>
3 <base cmake="foo.cmake"/>
4 <base_two cmake="bar.cmake"/>
5 </export>
6 <depend package="base"/>
7 <depend package="base_two"/>
8 <rosdep name="bar"/>
9 <rosdep nombre="baz"/>
10 </package>
0 <package>
1 <export>
2 <cpp lflags="-lloki"/>
3 </export>
4 <depend package="base"/>
5 <depend package="base_two"/>
6 <depend package="deps_higher"/>
7 </package>
0 <package>
1 <description>Fake description</description>
2 <author>No One</author>
3 <license>Fake License</license>
4 <export>
5 <cpp lflags="" cflags=""/>
6 </export>
7 <versioncontrol type="svn" url=""/>
8 <depend package="empty"/>
9 </package>
0 <package>
1 <export>
2 <cpp lflags="-lthor"/>
3 </export>
4 <depend package="deps"/>
5 </package>
6
0 <package>
1 <export>
2 <cpp lflags="-lloki"/>
3 </export>
4 <depend package="no_such_package"/>
5 </package>
6
0 <package>
1 <depend package="nonexistentpackage"/>
2 </package>
0 <package>
1 <description>Fake description</description>
2 <author>No One</author>
3 <license>Fake License</license>
4 <export>
5 <cpp lflags="" cflags=""/>
6 </export>
7 <versioncontrol type="" url=""/>
8 </package>
0 <package>
1 <export>
2 </export>
0 <package>
1 <export>
2 <cpp lflags="/usr/lib/libfoo.a /a/bad/flag"/>
3 </export>
4 </package>
0 <package>
1 <export>
2 <cpp lflags="-lfoo"/>
3 </export>
4 </package>
0 <package>
1 <export>
2 <foo bar="bat"/>
3 </export>
4 </package>
0 <package>
1 <depend pkg="foo"/>
2 </package>
0 <package>
1 <export>
2 <cpp os="other" cflags="-DOTHER"/>
3 <cpp os="linux" cflags="-DLINUX"/>
4 <cpp os="osx" cflags="-DAPPLE"/>
5 <cpp os="win32" cflags="-DWINDOWS"/>
6 </export>
7 </package>
0 <package>
1 <export>
2 <base cmake="bat.cmake"/>
3 </export>
4 <depend package="base"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-ltest"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <!-- Indicated that this package is a client library -->
2 <depend package="roslang"/>
3 </package>
0 <package>
1 <!-- Indicated that this package is a client library -->
2 <depend package="roslang"/>
3 </package>
0 <package>
1 </package>
0 [nosetests]
1 with-xunit=1
2 with-coverage=1
3 tests=utest.py, utest_rosstack.py
4
5
0 /*
1 * Copyright (c) 2008, Willow Garage, Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Willow Garage, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /* Author: Brian Gerkey */
30
31 #include <stdexcept> // for std::runtime_error
32 #include <string>
33 #include <vector>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <stdio.h>
37 #include <time.h>
38 #include <gtest/gtest.h>
39 #include <boost/algorithm/string.hpp>
40 #include <boost/filesystem.hpp>
41 #include "rospack/rospack.h"
42 #include "utils.h"
43
44
45 TEST(rospack, reentrant)
46 {
47 rospack::ROSPack rp;
48 std::string output;
49 int ret = rp.run(std::string("plugins --attrib=foo --top=precedence1 roslang"));
50 ASSERT_EQ(ret, 0);
51 ret = rp.run(std::string("find roslang"));
52 ASSERT_EQ(ret, 0);
53 ret = rp.run(std::string("list-names"));
54 ASSERT_EQ(ret, 0);
55 std::vector<std::string> output_list;
56 output = rp.getOutput();
57 boost::trim(output);
58 boost::split(output_list, output, boost::is_any_of("\n"));
59 ASSERT_EQ((int)output_list.size(), 4);
60 ret = rp.run(std::string("list"));
61 ASSERT_EQ(ret, 0);
62 output = rp.getOutput();
63 boost::trim(output);
64 boost::split(output_list, output, boost::is_any_of("\n"));
65 ASSERT_EQ((int)output_list.size(), 4);
66 std::vector<std::string> path_name;
67 boost::split(path_name, output_list[0], boost::is_any_of(" "));
68 ASSERT_EQ((int)path_name.size(), 2);
69 }
70
71 TEST(rospack, multiple_rospack_objects)
72 {
73 rospack::ROSPack rp;
74 std::string output;
75 int ret = rp.run(std::string("plugins --attrib=foo --top=precedence1 roslang"));
76 ASSERT_EQ(ret, 0);
77 ret = rp.run(std::string("find roslang"));
78 ASSERT_EQ(ret, 0);
79 ret = rp.run(std::string("list-names"));
80 ASSERT_EQ(ret, 0);
81 std::vector<std::string> output_list;
82 output = rp.getOutput();
83 boost::trim(output);
84 boost::split(output_list, output, boost::is_any_of("\n"));
85 ASSERT_EQ((int)output_list.size(), 4);
86 ret = rp.run(std::string("list"));
87 ASSERT_EQ(ret, 0);
88 output = rp.getOutput();
89 boost::trim(output);
90 boost::split(output_list, output, boost::is_any_of("\n"));
91 ASSERT_EQ((int)output_list.size(), 4);
92 std::vector<std::string> path_name;
93 boost::split(path_name, output_list[0], boost::is_any_of(" "));
94 ASSERT_EQ((int)path_name.size(), 2);
95
96 rospack::ROSPack rp2;
97 ret = rp2.run(std::string("plugins --attrib=foo --top=precedence1 roslang"));
98 ASSERT_EQ(ret, 0);
99 ret = rp2.run(std::string("find roslang"));
100 ASSERT_EQ(ret, 0);
101 ret = rp2.run(std::string("list-names"));
102 ASSERT_EQ(ret, 0);
103 output_list.clear();
104 output = rp2.getOutput();
105 boost::trim(output);
106 boost::split(output_list, output, boost::is_any_of("\n"));
107 ASSERT_EQ((int)output_list.size(), 4);
108 ret = rp2.run(std::string("list"));
109 ASSERT_EQ(ret, 0);
110 output = rp2.getOutput();
111 boost::trim(output);
112 boost::split(output_list, output, boost::is_any_of("\n"));
113 ASSERT_EQ((int)output_list.size(), 4);
114 path_name.clear();
115 boost::split(path_name, output_list[0], boost::is_any_of(" "));
116 ASSERT_EQ((int)path_name.size(), 2);
117 }
118
119 TEST(rospack, deduplicate_tokens)
120 {
121 std::string input = "foo foo bar bat bar foobar batbar";
122 std::string truth = "foo bar bat foobar batbar";
123 std::string output;
124 rospack::deduplicate_tokens(input, false, output);
125 ASSERT_EQ(truth, output);
126 }
127
128 int main(int argc, char **argv)
129 {
130 // Quiet some warnings
131 (void)rospack::ROSPACK_NAME;
132 (void)rospack::ROSSTACK_NAME;
133
134 char buf[1024];
135 std::string rr = std::string(getcwd(buf, sizeof(buf))) + "/test2";
136 setenv("ROS_ROOT", rr.c_str(), 1);
137 unsetenv("ROS_PACKAGE_PATH");
138 char path[PATH_MAX];
139 if(getcwd(path,sizeof(path)))
140 {
141 boost::filesystem::path p(path);
142 p = p.parent_path();
143 std::string newpath = p.string();
144 char* oldpath = getenv("PATH");
145 if(oldpath)
146 newpath += std::string(":") + oldpath;
147 setenv("PATH", newpath.c_str(), 1);
148 }
149
150 testing::InitGoogleTest(&argc, argv);
151 return RUN_ALL_TESTS();
152 }
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2008, Willow Garage, Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials provided
14 # with the distribution.
15 # * Neither the name of Willow Garage, Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31 #
32 # Author: Brian Gerkey/Ken Conley
33
34 import os
35 import unittest
36 import tempfile
37 import shutil
38 import sys
39 import platform
40 from subprocess import Popen, PIPE
41
42 ROS_ROOT = 'ROS_ROOT'
43 ROS_PACKAGE_PATH = 'ROS_PACKAGE_PATH'
44 ROS_LANG_DISABLE = 'ROS_LANG_DISABLE'
45 ROSPACK_PATH = "@CATKIN_DEVEL_PREFIX@/bin/rospack"
46
47 # Set the initial CWD, so that we can set it back later.
48 # Go up one directory. Have to do this because nosetests doesn't seem to
49 # run properly from the parent directory, even with --where= passed in.
50 initial_cwd = os.path.dirname(os.path.dirname(__file__))
51 os.chdir(initial_cwd)
52
53 _structure_test_p = os.path.abspath('structure_test')
54 # expected layout of the structure_test directory, used for rospack find and list tests
55 structure_test = {
56 'package1': 'package1',
57 'package2': 'package2',
58 'package3': 'subdir1/package3',
59 'package4': 'subdir1/subdir1_1/package4',
60 'package5': 'subdir1/subdir1_1/package5',
61 'package6': 'subdir3/package6',
62 'package7': 'subdir3/package7',
63 }
64 # canonicalize
65 for k in structure_test.keys():
66 structure_test[k] = os.path.abspath(os.path.join(_structure_test_p, structure_test[k]))
67
68
69 aliases = {
70 'deps': 'depends',
71 'deps1': 'depends1',
72 'deps-manifests': 'depends-manifests',
73 'deps-indent': 'depends-indent',
74 'rosdep': 'rosdeps',
75 'rosdep0': 'rosdeps0'
76 }
77
78 ## Process-level tests of rospack executable
79 class RospackTestCase(unittest.TestCase):
80
81 def setUp(self):
82 # Some tests change CWD
83 os.chdir(initial_cwd)
84
85 ## runs rospack with ROS_ROOT set to ./test and ROS_PACKAGE_PATH unset
86 ## @return int, str: return code, stdout
87 def _run_rospack(self, ros_root, ros_package_path, pkgname, command):
88 env = os.environ.copy()
89 if ros_root is not None:
90 env[ROS_ROOT] = ros_root
91 else:
92 if ROS_ROOT in env:
93 del env[ROS_ROOT]
94 if ros_package_path is not None:
95 env[ROS_PACKAGE_PATH] = ros_package_path
96 elif ROS_PACKAGE_PATH in env:
97 del env[ROS_PACKAGE_PATH]
98 # Must split up the command string into its whitespace separated
99 # components; otherwise you get multiple words as one element of
100 # argv.
101 #args = ["rospack", command, pkgname]
102 args = [ROSPACK_PATH]
103 if command:
104 for s in command.split():
105 args.append(s)
106 if pkgname is not None:
107 args.append(pkgname)
108 p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
109 stdout, stderr = p.communicate()
110
111 # Also test command aliases, verifying that they give the same
112 # return code and console output
113 if command:
114 cmd = command.split()[-1]
115 if cmd in aliases:
116 args[-2] = aliases[cmd]
117 alias_p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
118 alias_stdout, alias_stderr = alias_p.communicate()
119 self.assertEquals(p.returncode, alias_p.returncode)
120 self.assertEquals(stdout, alias_stdout)
121 #self.assertEquals(stderr, alias_stderr)
122
123 # rospack should only yield non-negative return codes. A negative
124 # return code indicates a crash (e.g., SIGSEGV, SIGABORT), which is
125 # never ok.
126 if p.returncode < 0:
127 self.fail('rospack returned non-zero exit code (%d), indicating a crash'%(p.returncode))
128
129 return p.returncode, stdout.strip().decode('ascii'), stderr.decode('ascii')
130
131 ################################################################################
132 # HELPER ROUTINES
133 # NOTE: helpers with the 'e' prefix take in environment parameters
134
135 ## @return str: stdout
136 def run_rospack(self, pkgname, command):
137 ros_root = os.path.abspath('test')
138 return self._run_rospack(ros_root, None, pkgname, command)[1]
139
140 ## @return str: stdout
141 def erun_rospack(self, ros_root, ros_package_path, pkgname, command):
142 return self._run_rospack(ros_root, ros_package_path, pkgname, command)[1]
143
144 ## runs rospack with ROS_ROOT set to ./test and ROS_PACKAGE_PATH unset
145 ## @return int: status code
146 def run_rospack_status(self, pkgname, command):
147 ros_root = os.path.abspath('test')
148 return self._run_rospack(ros_root, None, pkgname, command)[0]
149
150 ## @return int: status code
151 def erun_rospack_status(self, ros_root, ros_package_path, pkgname, command):
152 return self._run_rospack(ros_root, ros_package_path, pkgname, command)[0]
153
154 ## assert that rospack fails on the specified args
155 def rospack_fail(self, package, command):
156 ros_root = os.path.abspath('test')
157 code, stdout, stderr = self._run_rospack(ros_root, None, package, command)
158 self.assertNotEquals(0, code, "rospack [%s %s] should have failed. \n\nstdout[%s] \n\nstderr[%s]"%(command, package, stdout, stderr))
159
160 ## assert that rospack fails on the specified args. includes ROS_ROOT and ROS_PACKAGE_PATH
161 def erospack_fail(self, ros_root, ros_package_path, package, command):
162 code, stdout, stderr = self._run_rospack(ros_root, ros_package_path, package, command)
163 self.assertNotEquals(0, code, "rospack [%s %s] should have failed instead of returning status code 0. \n\nstdout[%s] \n\nstderr[%s]"%(command, package, stdout, stderr))
164
165 ## assert that rospack succeeds on the specified args
166 def rospack_succeed(self, package, command):
167 ros_root = os.path.abspath('test')
168 status_code, stdout, stderr = self._run_rospack(ros_root, None, package, command)
169 self.assertEquals(0, status_code, '"rospack %s %s" failed with status code [%s] instead of succeeding with [0]. \n\nstdout[%s] \n\nstderr[%s]'%(command, package, status_code, stdout, stderr))
170
171 ## assert that rospack succeeds on the specified args
172 def erospack_succeed(self, ros_root, ros_package_path, package, command):
173 status_code, stdout, stderr = self._run_rospack(ros_root, ros_package_path, package, command)
174 self.assertEquals(0, status_code, "rospack [%s %s, env ROS_ROOT=%s ROS_PACKAGE_PATH=%s] failed with status code [%s] instead of succeeding with [0]. \n\nstdout[%s] \n\nstderr[%s]"%(command, package, ros_root, ros_package_path, status_code, stdout, stderr))
175
176 # helper routine that does return value validation where the return value from
177 # rospack is an unordered, line-separated list
178 def check_ordered_list(self, command, tests):
179 for retlist, package in tests:
180 expected = set(retlist)
181 self.rospack_succeed(package, command)
182 retval = self.strip_opt_ros(self.run_rospack(package, command))
183 retactual = [v for v in retval.split('\n') if v]
184 self.failIf(set(retlist) ^ set(retactual), "rospack %s %s failed: [%s] vs [%s]"%(command, package, retlist, retactual))
185 self.assertEquals('\n'.join(retlist), '\n'.join(retactual))
186
187 # variant of check_ordered_list that allows specifying ros_root and ros_package_path.
188 # helper routine that does return value validation where the return value from
189 # rospack is an unordered, line-separated list
190 def echeck_ordered_list(self, command, tests):
191 for retlist, ros_root, ros_package_path, package in tests:
192 expected = set(retlist)
193 self.erospack_succeed(ros_root, ros_package_path, package, command)
194 retval = self.erun_rospack(ros_root, ros_package_path, package, command)
195 retactual = [v for v in retval.split('\n') if v]
196 self.failIf(set(retlist) ^ set(retactual), "[env %s %s] rospack %s %s failed: [%s] vs [%s]"%(ros_root, ros_package_path, command, package, retlist, retactual))
197
198 # variant that does not require ordering among the return values
199 def check_unordered_list(self, command, tests):
200 for retlist, package in tests:
201 expected = set(retlist)
202 self.rospack_succeed(package, command)
203 retval = self.run_rospack(package, command)
204 retactual = [v for v in retval.split('\n') if v]
205 self.failIf(set(retlist) ^ set(retactual), "rospack %s %s failed: [%s] vs [%s]"%(command, package, retlist, retactual))
206 #self.assertEquals('\n'.join(retlist), '\n'.join(retactual))
207
208 # variant that does not require ordering among the return values
209 def echeck_unordered_list(self, command, tests):
210 for retlist, ros_root, ros_package_path, package in tests:
211 expected = set(retlist)
212 self.erospack_succeed(ros_root, ros_package_path, package, command)
213 retval = self.erun_rospack(ros_root, ros_package_path, package, command)
214 retactual = [v for v in retval.split('\n') if v]
215 self.failIf(set(retlist) ^ set(retactual), "rospack %s %s failed: [%s] vs [%s]"%(command, package, retlist, retactual))
216 #self.assertEquals('\n'.join(retlist), '\n'.join(retactual))
217
218 ################################################################################
219 ## ARG PARSING
220
221 def test_no_option(self):
222 self.rospack_succeed(None, None)
223
224 def test_fake_option(self):
225 self.rospack_fail("deps", "--fake deps")
226
227 def test_invalid_option(self):
228 self.rospack_fail("deps", "deps --lang=cpp --attrib=flags")
229 self.rospack_fail("deps", "deps --lang=cpp")
230 self.rospack_fail("deps", "deps --attrib=lflags")
231 self.rospack_fail("base", "export --lang=cpp --attrib=cflags --top=")
232 self.rospack_fail(None, "profile --length=")
233 self.rospack_fail(None, "deps --length=10")
234 self.rospack_fail(None, "deps --zombie-only")
235 self.rospack_fail(None, "profile --deps-only")
236
237 def test_ros_cache_timeout(self):
238 env = os.environ.copy()
239 os.environ['ROS_CACHE_TIMEOUT'] = '0'
240 self.rospack_succeed(None, "profile")
241 os.environ['ROS_CACHE_TIMEOUT'] = '-1'
242 self.rospack_succeed(None, "profile")
243 import time
244 time.sleep(0.1)
245 os.environ['ROS_CACHE_TIMEOUT'] = '.001'
246 self.rospack_succeed(None, "profile")
247 os.environ = env
248
249 def test_profile(self):
250 # TODO: test that the output is correct
251 self.rospack_succeed(None, "profile --zombie-only")
252 # TODO: test that the output is correct
253 self.rospack_succeed(None, "profile --length=10")
254
255 def test_ros_home(self):
256 env = os.environ.copy()
257
258 # Make sure we write to ROS_HOME, #2812.
259 d = tempfile.mkdtemp()
260 os.environ['ROS_HOME'] = d
261 cache_path = os.path.join(d,'rospack_cache')
262 self.rospack_succeed(None, "profile")
263 self.assertEquals(True, os.path.exists(cache_path))
264 # Make sure we auto-create ROS_HOME
265 shutil.rmtree(d)
266 self.rospack_succeed(None, "profile")
267 self.assertEquals(True, os.path.exists(cache_path))
268 # Test with a corrupted cache
269 f = open(cache_path, 'w')
270 f.write('#SOMETHING\n')
271 f.close()
272 self.rospack_succeed(None, "list")
273 # Make sure we proceed when we can't write to ROS_HOME
274 os.chmod(d, 0000)
275 self.rospack_succeed(None, "profile")
276 # Delete the .ros directory, just in case this test is being run as
277 # root, in which case the above call will cause .ros to be created,
278 # despite the restrictive permissions that were set.
279 if os.path.exists(d):
280 os.chmod(d, 0o700)
281 shutil.rmtree(d)
282 # Make sure we proceed when we HOME/.ros isn't a directory
283 f = open(d, 'w')
284 f.close()
285 os.chmod(d, 0o700)
286 self.rospack_succeed(None, "profile")
287 # Make sure we proceed when neither HOME nor ROS_HOME is set
288 del os.environ['ROS_HOME']
289 del os.environ['HOME']
290 self.rospack_succeed(None, "profile")
291
292 # Clean up
293 os.unlink(d)
294 os.environ = env
295
296 def test_no_package_allowed(self):
297 self.rospack_succeed(None, "help")
298 self.rospack_succeed(None, "profile")
299 self.rospack_succeed(None, "list")
300 self.rospack_succeed(None, "list-names")
301 self.rospack_succeed(None, "list-duplicates")
302 self.rospack_succeed(None, "langs")
303
304 def test_no_package_allowed_bad(self):
305 self.rospack_fail("deps", "profile")
306 self.rospack_fail("deps", "list")
307 self.rospack_fail("deps", "list-names")
308 self.rospack_fail("deps", "list-duplicates")
309 self.rospack_fail("deps", "langs")
310
311 def test_export_bad(self):
312 self.rospack_fail("base", "export --lang= --attrib=lflags")
313 self.rospack_fail("base", "export --lang=cpp --attrib=")
314 self.rospack_fail("base", "export --attrib=lflags")
315 self.rospack_fail("base", "export --lang=cpp")
316 self.rospack_fail("base", "export --lang=cpp --lang=python --attrib=lflags")
317 self.rospack_fail("base", "export --lang=cpp --attrib=lflags --attrib=cflags")
318 self.rospack_fail("base", "export --lang=cpp --attrib=cflags --top=foo")
319
320 def test_plugins_bad(self):
321 self.rospack_fail("base", "plugins")
322 self.rospack_fail("base", "plugins --lang=cpp")
323 self.rospack_fail("base", "plugins --attrib=")
324 self.rospack_fail("base", "plugins --top=foo")
325
326 def test_rosdep(self):
327 self.rospack_succeed("base", "rosdep")
328 self.assertEquals("name: foo", self.run_rospack("base", "rosdep"))
329 self.rospack_succeed("deps", "rosdep0")
330 self.assertEquals("name: bar", self.run_rospack("deps", "rosdep0"))
331 self.check_unordered_list("rosdep", [(["name: foo", "name: bar"], "deps")])
332
333 ################################################################################
334 ## EXPORT
335
336 def test_export_cpp(self):
337 package = 'base'
338 tests = [("-lfoo", "export --lang=cpp --attrib=lflags"),
339 ("-lfoo", "export --attrib=lflags --lang=cpp"),
340 ("-Isomething", "export --lang=cpp --attrib=cflags"),
341 ("-Isomething", "export --attrib=cflags --lang=cpp"),
342 ]
343 for retval, arg in tests:
344 self.rospack_succeed(package, arg)
345 self.assertEquals(retval, self.strip_opt_ros(self.run_rospack(package, arg)))
346 self.assertEquals("-lfoo -lbar", self.strip_opt_ros(self.run_rospack("deps", "export --lang=cpp --attrib=lflags --deps-only")))
347 #TODO: test export with $prefix
348
349 def test_export_roslang(self):
350 package = 'base'
351 tests = [("something.cmake", "export --lang=roslang --attrib=cmake")]
352 for retval, arg in tests:
353 self.rospack_succeed(package, arg)
354 self.assertEquals(retval, self.strip_opt_ros(self.run_rospack(package, arg)))
355
356 def test_export_non_existent_attrib(self):
357 self.rospack_succeed("base", "export --lang=cpp --attrib=fake")
358 self.failIf(self.run_rospack("base", "export --lang=cpp --attrib=fake"))
359
360 ################################################################################
361 ## Plugins
362
363 def test_plugins(self):
364 tests = [(["deps foo.cmake", "plugins bat.cmake"], "base")]
365 self.check_unordered_list("plugins --attrib=cmake", tests)
366
367 package = 'base'
368 tests = [("deps foo.cmake", "plugins --attrib=cmake --top=deps")]
369 for retval, arg in tests:
370 self.rospack_succeed(package, arg)
371 self.assertEquals(retval, self.strip_opt_ros(self.run_rospack(package, arg)))
372 package = 'base_two'
373 tests = [("deps bar.cmake", "plugins --attrib=cmake")]
374 for retval, arg in tests:
375 self.rospack_succeed(package, arg)
376 self.assertEquals(retval, self.strip_opt_ros(self.run_rospack(package, arg)))
377
378 ################################################################################
379 ## ENVIRONMENT TEST
380
381 def test_no_ros_root(self):
382 testp = os.path.abspath('test')
383 self.erospack_succeed(None, testp, "deps", "deps")
384
385 def test_bad_ros_root(self):
386 non_existent1 = os.path.abspath('non_existent1')
387 testp = os.path.abspath("test")
388 self.erospack_succeed(non_existent1, testp, "deps", "deps")
389
390 ## test rospack with ROS_ROOT=ROS_PACKAGE_PATH
391 def test_ros_root_ros_package_path_identical(self):
392 #implicitly depending on the deps test here
393 #set ros_package_path to be identical to ros_root
394 testp = os.path.abspath('test')
395 tests = [
396 (["base", "base_two"], testp, testp, "deps"),
397 ]
398 self.echeck_ordered_list("deps", tests)
399
400 ## test rospack with ROS_PACKAGE_PATH set to the empty string
401 def test_empty_ros_package_path(self):
402 testp = os.path.abspath('test')
403 tests = [
404 (["base", "base_two"], testp, '', "deps"),
405 ]
406 self.echeck_ordered_list("deps", tests)
407
408 ## tests internal rpp precedence (#2854)
409 def test_ros_package_path_precedence(self):
410 teste = os.path.abspath('test_empty')
411 testp = os.path.abspath('test')
412 test2p = os.path.abspath('test2')
413 testp_roslang = os.path.join(testp, 'roslang')
414 test2p_roslang = os.path.join(test2p, 'roslang')
415 tests = [([testp_roslang], teste, ':'.join([testp, test2p]), "roslang"),
416 ([testp_roslang], teste, ':'.join([testp, test2p_roslang]), "roslang"),
417 ([testp_roslang], teste, ':'.join([testp_roslang, test2p]), "roslang"),
418 ([testp_roslang], teste, ':'.join([testp_roslang, test2p_roslang]), "roslang")]
419 self.echeck_unordered_list('find', tests)
420
421 ## tests rpp vs rr precedence
422 def test_ros_package_path_precedence_1(self):
423 testp = os.path.abspath('test')
424 test2p = os.path.abspath('test2')
425 test3p = os.path.abspath('test3')
426 tests = [
427 (["test"], testp, test2p, "precedence1"),
428 (["test2"], test2p, testp, "precedence1"),
429 (["test2"], testp, "%s:%s"%(test2p, test3p), "precedence2"),
430 (["test3"], testp, "%s:%s"%(test3p, test2p), "precedence2"),
431 ]
432 self.echeck_ordered_list('libs-only-l', tests)
433
434 ## tests list-duplicates
435 def test_list_duplicates(self):
436 testp = os.path.abspath('test')
437 test2p = os.path.abspath('test2')
438 test3p = os.path.abspath('test3')
439 self.erospack_succeed(testp, None, None, 'list-duplicates')
440 self.erospack_succeed(testp, '%s:%s'%(test2p,test3p), None, 'list-duplicates')
441
442 # test ability to point ros_package_path directly at package
443 def test_ros_package_path_direct_package(self):
444 testp = os.path.abspath('test')
445 test2p = os.path.abspath('test2')
446 test3p = os.path.abspath('test3')
447 # point directly at precedence 2/3
448 rpp = ':'.join([os.path.join(test2p, 'precedence2'),os.path.join(test3p, 'precedence3')])
449 tests = [
450 (["test2"], testp, rpp, "precedence2"),
451 (["test3"], testp, rpp, "precedence3"),
452 ]
453 self.echeck_ordered_list('libs-only-l', tests)
454
455 def test_ros_package_path_colons(self):
456 # scatter some colons into ros package path to make sure rospack doesn't mind
457 testp = os.path.abspath('test')
458 test2p = os.path.abspath('test2')
459 # Add a trailing slash, to make sure that it gets removed
460 test3p = os.path.abspath('test3') + '/'
461 tests = [
462 (["base","base_two"], testp, "::%s:::"%testp, "deps"),
463 (["base","base_two"], testp, "::", "deps"),
464 ]
465 self.echeck_ordered_list('deps', tests)
466 tests = [
467 (["test"], testp, ":::%s:"%test2p, "precedence1"),
468 (["test2"],testp, "::%s::%s::"%(test2p,test3p), "precedence2"),
469 ]
470 self.echeck_ordered_list("libs-only-l", tests)
471
472 def test_ros_package_path_bad_paths(self):
473 testp = os.path.abspath('test')
474 test2p = os.path.abspath('test2')
475 non_existentp = os.path.abspath('test')
476 tests = [
477 (["test"], testp, non_existentp, "precedence1"),
478 (["test2"],testp, ":%s:%s"%(non_existentp, test2p), "precedence2"),
479 (["test2"],testp, ":%s:%s"%(test2p, non_existentp), "precedence2"),
480 ]
481 self.echeck_ordered_list("libs-only-l", tests)
482
483 # Test rospack from within a package
484 def test_ros_in_package(self):
485 pwd = os.getcwd()
486 ros_root = os.path.join(pwd, 'test')
487 os.chdir(os.path.abspath(os.path.join('test', 'deps')))
488 self.erospack_succeed(ros_root, None, None, 'depends1')
489 self.echeck_unordered_list('depends1', [(["base", "base_two"], ros_root, None, None)])
490 # Check what happens when we're in an unlinked directory
491 d = tempfile.mkdtemp()
492 os.chdir(d)
493 os.rmdir(d)
494 self.erospack_fail(ros_root, None, None, 'depends1')
495 os.chdir(pwd)
496
497 ################################################################################
498 ## rospack list
499
500 def _rospack_list(self, ros_root, ros_package_path):
501 env = os.environ.copy()
502 if ros_root is not None:
503 env[ROS_ROOT] = ros_root
504 else:
505 del env[ROS_ROOT]
506 if ros_package_path is not None:
507 env[ROS_PACKAGE_PATH] = ros_package_path
508 elif ROS_PACKAGE_PATH in env:
509 del env[ROS_PACKAGE_PATH]
510 args = [ROSPACK_PATH, 'list']
511 p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
512 retval = p.communicate()[0]
513 return p.returncode, retval.strip().decode('ascii')
514
515 def _check_rospack_list(self, expected, retval):
516 lines = [l for l in retval.split('\n') if l]
517 packages = [l[:l.find(' ')] for l in lines]
518 # canonicalize paths
519 paths = [os.path.abspath(l[l.find(' ')+1:]) for l in lines]
520 result = {}
521 for pack, path in zip(packages, paths):
522 result[pack] = os.path.abspath(path)
523 self.failIf(set(expected.keys()) ^ set(packages), "package lists do not match (expected vs. actual): %s vs %s"%(expected.keys(), packages))
524 for pack,path in expected.items():
525 self.assertEquals(path, result[pack])
526
527 ## test rospack list on an empty tree
528 def test_rospack_list_empty(self):
529 rr = os.path.abspath('test_empty')
530 retcode, retval = self._rospack_list(rr, None)
531 self.assertEquals(0, retcode)
532 self.failIf(retval, "rospack list on empty directory returned value %s"%retval)
533
534 ## test rospack depends-on1 in a directory that's not a package (#2556)
535 def test_rospack_depends_on_not_a_package(self):
536 pwd = os.getcwd()
537 ros_root = os.path.abspath('test')
538 os.chdir(os.path.abspath('/'))
539 self.erospack_fail(ros_root, None, None, 'depends-on1')
540 os.chdir(pwd)
541
542 # test that rospack list removes duplicates
543 def test_rospack_list_dups(self):
544 # make sure result is same if ROS_ROOT=ROS_PACKAGE_PATH
545 rr = os.path.abspath('structure_test')
546 retcode, retval = self._rospack_list(rr, None)
547 self.assertEquals(0, retcode)
548 retcode2, retval2 = self._rospack_list(rr, rr)
549 self.assertEquals(0, retcode2)
550 self.assertEquals(retval, retval2, "rospack list did not remove duplicates")
551
552 def test_rospack_list_no_rpp(self):
553 rr = os.path.abspath('structure_test')
554 expected = structure_test.copy()
555 retcode, retval = self._rospack_list(rr, None)
556 self.assertEquals(0, retcode)
557 self._check_rospack_list(expected, retval)
558
559 #TODO: symlink test
560 #TODO: test with ros package path
561
562 ################################################################################
563 ## rospack list-names
564
565 def _rospack_list_names(self, ros_root, ros_package_path):
566 env = os.environ.copy()
567 if ros_root is not None:
568 env[ROS_ROOT] = ros_root
569 else:
570 del env[ROS_ROOT]
571 if ros_package_path is not None:
572 env[ROS_PACKAGE_PATH] = ros_package_path
573 elif ROS_PACKAGE_PATH in env:
574 del env[ROS_PACKAGE_PATH]
575 args = [ROSPACK_PATH, 'list-names']
576 p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
577 retval = p.communicate()[0]
578 return p.returncode, retval.strip().decode('ascii')
579
580 ## test rospack list-names on an empty tree
581 def test_rospack_list_names_empty(self):
582 rr = os.path.abspath('test_empty')
583 retcode, retval = self._rospack_list_names(rr, None)
584 self.assertEquals(0, retcode)
585 self.failIf(retval, "rospack list-names on empty directory returned value %s"%retval)
586
587 # test that rospack list removes duplicates
588 def test_rospack_list_names_dups(self):
589 # make sure result is same if ROS_ROOT=ROS_PACKAGE_PATH
590 rr = os.path.abspath('structure_test')
591 retcode, retval = self._rospack_list_names(rr, None)
592 self.assertEquals(0, retcode)
593 retcode2, retval2 = self._rospack_list_names(rr, rr)
594 self.assertEquals(0, retcode2)
595 self.assertEquals(retval, retval2, "rospack list-names did not remove duplicates")
596
597 def test_rospack_list_names_no_rpp(self):
598 rr = os.path.abspath('structure_test')
599 expected = set(structure_test.copy().keys())
600 retcode, retval = self._rospack_list_names(rr, None)
601 self.assertEquals(0, retcode)
602 self.assertEquals(expected, set(retval.split()))
603
604 #TODO: symlink test
605 #TODO: test with ros package path
606
607 ################################################################################
608 ## rospack find
609
610 ## test rospack find on non-existent package
611 def test_rospack_find_fail(self):
612 rr = os.path.abspath('test_empty')
613 self.erospack_fail(rr, None, 'package', 'find')
614
615 ## test rospack find with ros_package_path set directly to a package
616 def test_rospack_find_direct(self):
617 testp = os.path.abspath('test')
618 package1p = os.path.abspath(os.path.join('structure_test', 'package1'))
619 self.erospack_succeed(testp, package1p, 'package1', 'find')
620 self.assertEquals(package1p, self.erun_rospack(testp, package1p, 'package1', 'find'))
621
622 ## test rospack find with ros_package_path set directly to a package,
623 ## where that package contains a rospack_nosubdirs file, #3191.
624 def test_rospack_find_direct_with_rospack_nosubdirs(self):
625 testp = os.path.abspath('test')
626 package2p = os.path.abspath(os.path.join('structure_test', 'package2'))
627 self.erospack_succeed(testp, package2p, 'package2', 'find')
628 self.assertEquals(package2p, self.erun_rospack(testp, package2p, 'package2', 'find'))
629
630 def test_rospack_find_no_rpp(self):
631 rr = os.path.abspath('structure_test')
632 expected = structure_test.copy()
633 for package,path in expected.items():
634 self.erospack_succeed(rr, None, package, 'find')
635 self.assertEquals(path, os.path.abspath(self.erun_rospack(rr, None, package, 'find')))
636
637 #TODO: symlink test
638 #TODO: test with ros package path
639
640 ################################################################################
641 ## DEPENDENCIES
642
643 def test_deps(self):
644 depth_list = ['depth-%s'%i for i in range(1, 101)]
645 depth_list.reverse()
646 tests = [
647 (["base","base_two"], "deps"),
648 (["base","base_two","deps"], "deps_higher"),
649 (["base","base_two","deps","deps_higher"],"deps_dup"),
650 (depth_list, "depth-0")
651 ]
652 self.check_ordered_list('deps', tests)
653
654 def test_deps1(self):
655 tests = [
656 (["base","base_two"], "deps"),
657 (["deps"], "deps_higher"),
658 (["depth-1"], "depth-0"),
659 (["depth-99"], "depth-98"),
660 ]
661 self.check_ordered_list('deps1',tests)
662
663 def test_deps_invalid(self):
664 self.rospack_fail("deps_invalid", "deps")
665
666 def test_depends_on(self):
667 depth_list = ['depth-%s'%i for i in range(0, 100)]
668 depth_list.reverse()
669
670 self.rospack_succeed("deps", "depends-on")
671 tests = [
672 (["plugins", "deps_dup", "deps", "deps_higher"], "base"),
673 (["deps_higher","deps_dup"], "deps"),
674 ([], "depth-0"),
675 (depth_list, "depth-100"),
676 ]
677 self.check_unordered_list("depends-on", tests)
678
679 def test_depends_on1(self):
680 # sanity check first
681 self.rospack_succeed("deps", "depends-on")
682 tests = [
683 (["deps_higher"], "deps"),
684 (["deps", "deps_dup", "plugins"], "base"),
685 (["deps", "deps_dup"], "base_two"),
686 ]
687 self.check_unordered_list("depends-on1", tests)
688
689 def test_depends_on_nonexistent(self):
690 self.rospack_fail("deps", "deps_nonexistent")
691 self.rospack_fail("deps", "nonexistentpackage")
692 tests = [
693 (["deps_nonexistent"], "nonexistentpackage"),
694 ]
695 self.check_ordered_list("depends-on", tests)
696
697 def test_lflags_base(self):
698 self.rospack_succeed("base", "libs-only-l")
699 self.assertEquals("foo", self.run_rospack("base", "libs-only-l"))
700
701 def test_circular(self):
702 testp = os.path.abspath("test")
703 self.erospack_fail(testp, os.path.abspath("test_circular/cycle0"), "self_ref", "deps")
704 self.erospack_fail(testp, os.path.abspath("test_circular/cycle1"), "friend1", "deps")
705 self.erospack_fail(testp, os.path.abspath("test_circular/cycle1"), "friend2", "deps")
706 self.erospack_fail(testp, os.path.abspath("test_circular/cycle2"), "friend1", "deps")
707 self.erospack_fail(testp, os.path.abspath("test_circular/cycle2"), "friend2", "deps")
708 self.erospack_fail(testp, os.path.abspath("test_circular/cycle2"), "friend3", "deps")
709 self.erospack_fail(testp, os.path.abspath("test_circular/cycle2"), "friend3", "depends-on")
710
711 def test_lflags_backquote(self):
712 self.rospack_succeed("backquote", "libs-only-l")
713 self.assertEquals("loki foo backquote", self.run_rospack("backquote", "libs-only-l"))
714
715 def test_backquote_invalid(self):
716 self.rospack_fail("backquote_invalid", "libs-only-other")
717
718 # Strip out '/opt/ros' and friends from flags before checking them
719 def strip_opt_ros(self, flags):
720 prefix = '/opt/ros'
721 if 'ROS_BINDEPS_PATH' in os.environ:
722 prefix = os.environ['ROS_BINDEPS_PATH']
723 tostrip = [prefix + '/lib',
724 prefix + '/include',
725 '-L' + prefix + '/lib',
726 '-I' + prefix + '/include',
727 '-Wl,-rpath,' + prefix + '/lib']
728 res = ''
729 for f in flags.split(' '):
730 if f and f not in tostrip:
731 if len(res) > 0:
732 res += ' '
733 res += f
734 return res
735
736 def test_Lflags_backquote(self):
737 self.rospack_succeed("backquote", "libs-only-L")
738 self.assertEquals("odin", self.strip_opt_ros(self.run_rospack("backquote", "libs-only-L")))
739
740 def test_cflags_backquote(self):
741 self.rospack_succeed("backquote", "cflags-only-I")
742 self.assertEquals("blah backquote", self.strip_opt_ros(self.run_rospack("backquote", "cflags-only-I")))
743
744 def test_cflags_platform_specific(self):
745 self.rospack_succeed("platform_specific_exports", "cflags-only-other")
746 myos = platform.system()
747 if myos == 'Linux':
748 self.assertEquals("-DLINUX", self.run_rospack("platform_specific_exports", "cflags-only-other"))
749 elif myos == 'Darwin':
750 self.assertEquals("-DAPPLE", self.run_rospack("platform_specific_exports", "cflags-only-other"))
751 elif myos == 'Windows':
752 self.assertEquals("-DWINDOWS", self.run_rospack("platform_specific_exports", "cflags-only-other"))
753 else:
754 self.assertEquals("-DOTHER", self.run_rospack("platform_specific_exports", "cflags-only-other"))
755
756 self.assertEquals("blah backquote", self.strip_opt_ros(self.run_rospack("backquote", "cflags-only-I")))
757
758 def test_lflags_archive(self):
759 self.rospack_succeed("lflags_with_archive_lib", "libs-only-l")
760 self.assertEquals("/usr/lib/libfoo.a", self.run_rospack("lflags_with_archive_lib", "libs-only-l"))
761 self.rospack_succeed("lflags_with_archive_lib", "libs-only-other")
762 self.assertEquals("/a/bad/flag", self.run_rospack("lflags_with_archive_lib", "libs-only-other"))
763
764 def test_lflags_deps(self):
765 self.rospack_succeed("deps", "libs-only-l")
766 self.assertEquals("loki foo bar", self.run_rospack("deps", "libs-only-l"))
767
768 def test_lflags_deps_only(self):
769 self.rospack_succeed("deps", "libs-only-l --deps-only")
770 self.assertEquals("foo bar", self.run_rospack("deps", "libs-only-l --deps-only"))
771
772 def test_empty_lflags(self):
773 tests = [([], "deps_empty")]
774 commands = ["libs-only-l", "libs-only-L", "libs-only-other"]
775 for c in commands:
776 self.check_ordered_list(c, tests)
777
778 def test_empty_cflags(self):
779 tests = [([], "deps_empty")]
780 commands = ["cflags-only-I", "cflags-only-other"]
781 for c in commands:
782 self.check_ordered_list(c, tests)
783
784 def test_empty_vcs(self):
785 self.rospack_succeed("empty", "vcs0")
786 self.assertEquals("type: \turl:", self.run_rospack("empty", "vcs0"))
787 self.rospack_succeed("deps_empty", "vcs")
788 self.assertEquals("type: svn\turl: \ntype: \turl:", self.run_rospack("deps_empty", "vcs"))
789
790 def test_vcs_no_type_or_url(self):
791 self.rospack_succeed("vc_no_type_or_url", "vcs0")
792 self.assertEquals("", self.run_rospack("vc_no_type_or_url", "vcs0"))
793
794 def test_lflags_no_package_attrib(self):
795 self.rospack_fail("no_package_attribute", "libs-only-l")
796
797 def test_lflags_invalid(self):
798 self.rospack_fail("invalid", "libs-only-l")
799
800 def test_vcs_invalid(self):
801 self.rospack_fail("invalid", "vcs")
802
803 def test_deps1_invalid(self):
804 self.rospack_fail("invalid", "deps1")
805
806 def test_vcs0_deps(self):
807 self.rospack_succeed("deps", "vcs0")
808 self.failIf(self.run_rospack("deps", "vcs0"))
809
810 def test_vcs_deps(self):
811 self.rospack_succeed("deps", "vcs")
812 self.assertEquals("type: svn\turl: https://ros.svn.sourceforge.net/svnroot/ros/trunk\n"+
813 "type: svn\turl: https://ros.svn.sourceforge.net/svnroot/ros/branches", self.run_rospack("deps", "vcs"))
814
815 def test_deps_manifests(self):
816 self.rospack_succeed("deps", "deps-manifests")
817 testp = os.path.abspath('test')
818 expected = os.path.join(testp, 'base/manifest.xml') + ' ' + os.path.join(testp, 'base_two/manifest.xml')
819 self.assertEquals(expected,
820 self.run_rospack("deps", "deps-manifests"))
821
822 def test_deps_indent(self):
823 self.rospack_succeed("deps_higher", "deps-indent")
824 testp = os.path.abspath('test')
825 expected = 'deps\n base\n base_two'
826 self.assertEquals(expected,
827 self.run_rospack("deps_higher", "deps-indent"))
828
829 def _rospack_langs(self, ros_root, ros_package_path, ros_lang_disable):
830 env = os.environ.copy()
831 if ros_root is not None:
832 env[ROS_ROOT] = ros_root
833 else:
834 del env[ROS_ROOT]
835 if ros_package_path is not None:
836 env[ROS_PACKAGE_PATH] = ros_package_path
837 elif ROS_PACKAGE_PATH in env:
838 del env[ROS_PACKAGE_PATH]
839 if ros_lang_disable is not None:
840 env[ROS_LANG_DISABLE] = ros_lang_disable
841 elif ROS_LANG_DISABLE in env:
842 del env[ROS_LANG_DISABLE]
843 args = [ROSPACK_PATH, 'langs']
844 p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
845 retval = p.communicate()[0]
846 return p.returncode, retval.strip().decode('ascii')
847
848 def test_langs(self):
849 rr = os.path.abspath('test')
850 retcode, retval = self._rospack_langs(rr, None, None)
851 self.assertEquals(0, retcode)
852 # No guarantees on ordering of lang result
853 l = retval.split()
854 s = set(l)
855 expected = set(['rosfoo', 'rosbar'])
856 self.assertEquals(s, expected)
857
858 def test_langs_disable(self):
859 rr = os.path.abspath('test')
860 disable = 'rosfoo'
861 retcode, retval = self._rospack_langs(rr, None, disable)
862 self.assertEquals(0, retcode)
863 # No guarantees on ordering of lang result
864 l = retval.split()
865 s = set(l)
866 expected = set(['rosbar'])
867 self.assertEquals(s, expected)
868
869 def test_langs_empty(self):
870 rr = os.path.abspath('test2')
871 retcode, retval = self._rospack_langs(rr, None, None)
872 self.assertEquals(0, retcode)
873 self.failIf(retval, "rospack langs on empty directory returned value %s"%retval)
874
875 # Test auto-inclusion of msg_gen include directories, #3018
876 def test_msg_gen(self):
877 test_path = os.path.abspath('test')
878 pkgs = ['msg_gen_no_export', 'msg_gen_no_cpp', 'msg_gen_no_cflags']
879 for p in pkgs:
880 self.rospack_succeed(p, "cflags-only-I")
881 self.assertEquals(os.path.join(test_path, p, "msg_gen/cpp/include"), self.strip_opt_ros(self.run_rospack(p, "cflags-only-I")))
882 # Also test that we don't get auto-inclusion of msg_gen when we're
883 # asking for a different lang / attrib, #3884
884 pkg = 'msg_gen_no_cpp'
885 cmd = 'export --lang=cpp --attrib=lflags'
886 self.rospack_succeed(pkg, cmd)
887 self.assertEquals('', self.strip_opt_ros(self.run_rospack(pkg, cmd)))
888 cmd = 'export --lang=foo --attrib=bar'
889 self.rospack_succeed(pkg, cmd)
890 self.assertEquals('bat', self.run_rospack(pkg, cmd))
891
892 # Test that -q option suppresses errors, #3177.
893 def test_quiet_option(self):
894 ros_root = os.path.abspath('test')
895 # With -q: look for non-existent package, make sure that it fails, yet
896 # produces nothing on stderr.
897 status_code, stdout, stderr = self._run_rospack(ros_root, None, 'nonexistentpackage', 'find -q')
898 self.assertNotEquals(0, status_code)
899 self.assertEquals(0, len(stderr))
900 # Without -q: look for non-existent package, make sure that it fails,
901 # and produces somthing on stderr.
902 status_code, stdout, stderr = self._run_rospack(ros_root, None, 'nonexistentpackage', 'find')
903 self.assertNotEquals(0, status_code)
904 self.assertNotEquals(0, len(stderr))
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2008, Willow Garage, Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials provided
14 # with the distribution.
15 # * Neither the name of Willow Garage, Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31 #
32 # Author: Brian Gerkey/Ken Conley
33
34 import os
35 import unittest
36 from subprocess import Popen, PIPE
37
38 ROS_ROOT = 'ROS_ROOT'
39 ROS_PACKAGE_PATH = 'ROS_PACKAGE_PATH'
40 ROS_LANG_DISABLE = 'ROS_LANG_DISABLE'
41 ROSSTACK_PATH = "@CATKIN_DEVEL_PREFIX@/bin/rosstack"
42
43 # Set the initial CWD, so that we can set it back later.
44 # Go up one directory. Have to do this because nosetests doesn't seem to
45 # run properly from the parent directory, even with --where= passed in.
46 initial_cwd = os.path.dirname(os.path.dirname(__file__))
47 os.chdir(initial_cwd)
48
49 aliases = {
50 'deps': 'depends',
51 'deps1': 'depends1',
52 'deps-manifests': 'depends-manifests',
53 'deps-indent': 'depends-indent',
54 }
55
56 ## Process-level tests of rosstack executable
57 class RosstackTestCase(unittest.TestCase):
58
59 def setUp(self):
60 # Some tests change CWD
61 os.chdir(initial_cwd)
62
63 ## runs rosstack with ROS_ROOT set to ./test and ROS_PACKAGE_PATH unset
64 ## @return int, str: return code, stdout
65 def _run_rosstack(self, ros_root, ros_package_path, pkgname, command):
66 env = os.environ.copy()
67 if ros_root is not None:
68 env[ROS_ROOT] = ros_root
69 else:
70 del env[ROS_ROOT]
71 if ros_package_path is not None:
72 env[ROS_PACKAGE_PATH] = ros_package_path
73 elif ROS_PACKAGE_PATH in env:
74 del env[ROS_PACKAGE_PATH]
75 # Must split up the command string into its whitespace separated
76 # components; otherwise you get multiple words as one element of
77 # argv.
78 #args = ["rospack", command, pkgname]
79 args = [ROSSTACK_PATH]
80 if command:
81 for s in command.split():
82 args.append(s)
83 if pkgname is not None:
84 args.append(pkgname)
85 p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
86 stdout, stderr = p.communicate()
87
88 # Also test command aliases, verifying that they give the same
89 # return code and console output
90 if command:
91 cmd = command.split()[-1]
92 if cmd in aliases:
93 args[-2] = aliases[cmd]
94 alias_p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
95 alias_stdout, alias_stderr = alias_p.communicate()
96 self.assertEquals(p.returncode, alias_p.returncode)
97 self.assertEquals(stdout, alias_stdout)
98 #self.assertEquals(stderr, alias_stderr)
99
100 return p.returncode, stdout.strip().decode('ascii'), stderr.decode('ascii')
101
102 ################################################################################
103 # HELPER ROUTINES
104 # NOTE: helpers with the 'e' prefix take in environment parameters
105
106 ## @return str: stdout
107 def run_rosstack(self, pkgname, command):
108 ros_root = os.path.abspath('test')
109 return self._run_rosstack(ros_root, None, pkgname, command)[1]
110
111 ## @return str: stdout
112 def erun_rosstack(self, ros_root, ros_package_path, pkgname, command):
113 return self._run_rosstack(ros_root, ros_package_path, pkgname, command)[1]
114
115 ## runs rospack with ROS_ROOT set to ./test and ROS_PACKAGE_PATH unset
116 ## @return int: status code
117 def run_rosstack_status(self, pkgname, command):
118 ros_root = os.path.abspath('test')
119 return self._run_rosstack(ros_root, None, pkgname, command)[0]
120
121 ## @return int: status code
122 def erun_rosstack_status(self, ros_root, ros_package_path, pkgname, command):
123 return self._run_rosstack(ros_root, ros_package_path, pkgname, command)[0]
124
125 ## assert that rosstack fails on the specified args
126 def rosstack_fail(self, package, command):
127 ros_root = os.path.abspath('test')
128 code, stdout, stderr = self._run_rosstack(ros_root, None, package, command)
129 self.assertNotEquals(0, code, "rosstack [%s %s] should have failed. \n\nstdout[%s] \n\nstderr[%s]"%(command, package, stdout, stderr))
130
131 ## assert that rosstack fails on the specified args. includes ROS_ROOT and ROS_PACKAGE_PATH
132 def erosstack_fail(self, ros_root, ros_package_path, package, command):
133 code, stdout, stderr = self._run_rosstack(ros_root, ros_package_path, package, command)
134 self.assertNotEquals(0, code, "rosstack [%s %s] should have failed instead of returning status code 0. \n\nstdout[%s] \n\nstderr[%s]"%(command, package, stdout, stderr))
135
136 ## assert that rosstack succeeds on the specified args
137 def rosstack_succeed(self, package, command):
138 ros_root = os.path.abspath('test')
139 status_code, stdout, stderr = self._run_rosstack(ros_root, None, package, command)
140 self.assertEquals(0, status_code, '"rosstack %s %s" failed with status code [%s] instead of succeeding with [0]. \n\nstdout[%s] \n\nstderr[%s]'%(command, package, status_code, stdout, stderr))
141
142 ## assert that rosstack succeeds on the specified args
143 def erosstack_succeed(self, ros_root, ros_package_path, package, command):
144 status_code, stdout, stderr = self._run_rosstack(ros_root, ros_package_path, package, command)
145 self.assertEquals(0, status_code, "rosstack [%s %s, env ROS_ROOT=%s ROS_PACKAGE_PATH=%s] failed with status code [%s] instead of succeeding with [0]. \n\nstdout[%s] \n\nstderr[%s]"%(command, package, ros_root, ros_package_path, status_code, stdout, stderr))
146
147 # helper routine that does return value validation where the return value from
148 # rosstack is an unordered, line-separated list
149 def check_ordered_list(self, command, tests):
150 for retlist, package in tests:
151 expected = set(retlist)
152 self.rosstack_succeed(package, command)
153 retval = self.strip_opt_ros(self.run_rosstack(package, command))
154 retactual = [v for v in retval.split('\n') if v]
155 self.failIf(set(retlist) ^ set(retactual), "rosstack %s %s failed: [%s] vs [%s]"%(command, package, retlist, retactual))
156 self.assertEquals('\n'.join(retlist), '\n'.join(retactual))
157
158 # variant of check_ordered_list that allows specifying ros_root and ros_package_path.
159 # helper routine that does return value validation where the return value from
160 # rosstack is an unordered, line-separated list
161 def echeck_ordered_list(self, command, tests):
162 for retlist, ros_root, ros_package_path, package in tests:
163 expected = set(retlist)
164 self.erosstack_succeed(ros_root, ros_package_path, package, command)
165 retval = self.erun_rosstack(ros_root, ros_package_path, package, command)
166 retactual = [v for v in retval.split('\n') if v]
167 self.failIf(set(retlist) ^ set(retactual), "[env %s %s] rosstack %s %s failed: [%s] vs [%s]"%(ros_root, ros_package_path, command, package, retlist, retactual))
168
169 # variant that does not require ordering among the return values
170 def check_unordered_list(self, command, tests):
171 for retlist, package in tests:
172 expected = set(retlist)
173 self.rosstack_succeed(package, command)
174 retval = self.run_rosstack(package, command)
175 retactual = [v for v in retval.split('\n') if v]
176 self.failIf(set(retlist) ^ set(retactual), "rosstack %s %s failed: [%s] vs [%s]"%(command, package, retlist, retactual))
177 #self.assertEquals('\n'.join(retlist), '\n'.join(retactual))
178
179 # variant that does not require ordering among the return values
180 def echeck_unordered_list(self, command, tests):
181 for retlist, ros_root, ros_package_path, package in tests:
182 expected = set(retlist)
183 self.erosstack_succeed(ros_root, ros_package_path, package, command)
184 retval = self.erun_rosstack(ros_root, ros_package_path, package, command)
185 retactual = [v for v in retval.split('\n') if v]
186 self.failIf(set(retlist) ^ set(retactual), "rosstack %s %s failed: [%s] vs [%s]"%(command, package, retlist, retactual))
187 #self.assertEquals('\n'.join(retlist), '\n'.join(retactual))
188
189
190 ################################################################################
191 ## ARG PARSING
192
193 def test_no_option(self):
194 self.rosstack_succeed(None, None)
195
196 def test_fake_option(self):
197 self.rosstack_fail("deps", "--fake deps")
198
199 def test_invalid_option(self):
200 self.rosstack_fail("deps", "list --length=10")
201
202 def test_no_package_allowed(self):
203 self.rosstack_succeed(None, "help")
204 self.rosstack_succeed(None, "profile")
205 self.rosstack_succeed(None, "list")
206 self.rosstack_succeed(None, "list-names")
207
208 def test_no_package_allowed_bad(self):
209 self.rosstack_fail("deps", "profile")
210 self.rosstack_fail("deps", "list")
211 self.rosstack_fail("deps", "list-names")
212
213 def test_contents(self):
214 tests = [(["precedence1", "precedence2", "precedence3", "roslang"],
215 os.path.abspath("test2"), os.path.abspath("test2"), "test2")]
216 self.echeck_unordered_list("contents", tests)
217
218 # Bug #2854
219 def test_path_precendence(self):
220 tests = [([os.path.abspath("stack_install/stack")],
221 os.path.abspath("test"),
222 ':'.join([os.path.abspath("stack_install"), os.path.abspath("stack_overlay")]), "stack"),
223 ([os.path.abspath("stack_install/stack")],
224 os.path.abspath("test"),
225 ':'.join([os.path.abspath("stack_install"), os.path.abspath("stack_overlay/stack")]), "stack"),
226 ([os.path.abspath("stack_install/stack")],
227 os.path.abspath("test"),
228 ':'.join([os.path.abspath("stack_install/stack"), os.path.abspath("stack_overlay/stack")]), "stack"),
229 ([os.path.abspath("stack_install/stack")],
230 os.path.abspath("test"),
231 ':'.join([os.path.abspath("stack_install/stack"), os.path.abspath("stack_overlay")]), "stack")]
232 self.echeck_unordered_list("find", tests)
233
234 # TODO: port / adapt these rospack tests to exercise rosstack, #2009
235 #
236 # def test_invalid_option_order(self):
237 # self.rospack_fail("deps", "--lang=cpp --attrib=lflags export")
238 # self.rospack_fail("deps", "--lang=cpp export --attrib=lflags")
239 # self.rospack_fail("deps", "--deps-only cflags-only-I")
240 #
241 # def test_export_bad(self):
242 # self.rospack_fail("base", "export --lang= --attrib=lflags")
243 # self.rospack_fail("base", "export --lang=cpp --attrib=")
244 # self.rospack_fail("base", "export --attrib=lflags")
245 # self.rospack_fail("base", "export --lang=cpp")
246 # self.rospack_fail("base", "export --lang=cpp --lang=python --attrib=lflags")
247 # self.rospack_fail("base", "export --lang=cpp --attrib=lflags --attrib=cflags")
248 # self.rospack_fail("base", "export --lang=cpp --attrib=cflags --top=foo")
249 #
250 # def test_plugins_bad(self):
251 # self.rospack_fail("base", "plugins")
252 # self.rospack_fail("base", "plugins --lang=cpp")
253 # self.rospack_fail("base", "plugins --attrib=")
254 # self.rospack_fail("base", "plugins --top=foo")
255 #
256 # ################################################################################
257 # ## EXPORT
258 #
259 # def test_export_cpp(self):
260 # package = 'base'
261 # tests = [("-lfoo", "export --lang=cpp --attrib=lflags"),
262 # ("-lfoo", "export --attrib=lflags --lang=cpp"),
263 # ("-Isomething", "export --lang=cpp --attrib=cflags"),
264 # ("-Isomething", "export --attrib=cflags --lang=cpp"),
265 # ]
266 # for retval, arg in tests:
267 # self.rospack_succeed(package, arg)
268 # self.assertEquals(retval, self.strip_opt_ros(self.run_rospack(package, arg)))
269 # #TODO: test export with $prefix
270 # #TODO: test export with non-cpp attribs
271 #
272 # def test_export_non_existent_attrib(self):
273 # self.rospack_succeed("base", "export --lang=cpp --attrib=fake")
274 # self.failIf(self.run_rospack("base", "export --lang=cpp --attrib=fake"))
275 #
276 # ################################################################################
277 # ## Plugins
278 #
279 # def test_plugins(self):
280 # tests = [(["deps foo.cmake", "plugins bat.cmake"], "base")]
281 # self.check_unordered_list("plugins --attrib=cmake", tests)
282 #
283 # package = 'base'
284 # tests = [("deps foo.cmake", "plugins --attrib=cmake --top=deps")]
285 # for retval, arg in tests:
286 # self.rospack_succeed(package, arg)
287 # self.assertEquals(retval, self.strip_opt_ros(self.run_rospack(package, arg)))
288 # package = 'base_two'
289 # tests = [("deps bar.cmake", "plugins --attrib=cmake")]
290 # for retval, arg in tests:
291 # self.rospack_succeed(package, arg)
292 # self.assertEquals(retval, self.strip_opt_ros(self.run_rospack(package, arg)))
293 #
294 # ################################################################################
295 # ## ENVIRONMENT TEST
296 #
297 # def test_no_ros_root(self):
298 # testp = os.path.abspath('test')
299 # self.erospack_fail(None, testp, "deps", "deps")
300 #
301 # def test_bad_ros_root(self):
302 # # #468 test. this is the chosen exit code, distinguish against segfault
303 # non_existent1 = os.path.abspath('non_existent1')
304 # testp = os.path.abspath("test")
305 # self.assertNotEquals(0, self.erun_rospack_status(non_existent1, testp, "deps", "deps"))
306 #
307 # ## test rospack with ROS_ROOT=ROS_PACKAGE_PATH
308 # def test_ros_root__ros_package_path_identical(self):
309 # #implicitly depending on the deps test here
310 # #set ros_package_path to be identical to ros_root
311 # testp = os.path.abspath('test')
312 # tests = [
313 # (["base", "base_two"], testp, testp, "deps"),
314 # ]
315 # self.echeck_ordered_list("deps", tests)
316 #
317 # ## test rospack with ROS_PACKAGE_PATH set to the empty string
318 # def test_empty_ros_package_path(self):
319 # testp = os.path.abspath('test')
320 # tests = [
321 # (["base", "base_two"], testp, '', "deps"),
322 # ]
323 # self.echeck_ordered_list("deps", tests)
324 #
325 # ## tests rpp vs rr precedence
326 # def test_ros_package_path_precedence_1(self):
327 # testp = os.path.abspath('test')
328 # test2p = os.path.abspath('test2')
329 # test3p = os.path.abspath('test3')
330 # tests = [
331 # (["test"], testp, test2p, "precedence1"),
332 # (["test2"], test2p, testp, "precedence1"),
333 # (["test2"], testp, "%s:%s"%(test2p, test3p), "precedence2"),
334 # (["test3"], testp, "%s:%s"%(test3p, test2p), "precedence2"),
335 # ]
336 # self.echeck_ordered_list('libs-only-l', tests)
337 #
338 # # test ability to point ros_package_path directly at package
339 # def test_ros_package_path_direct_package(self):
340 # testp = os.path.abspath('test')
341 # test2p = os.path.abspath('test2')
342 # test3p = os.path.abspath('test3')
343 # # point directly at precedence 2/3
344 # rpp = ':'.join([os.path.join(test2p, 'precedence2'),os.path.join(test3p, 'precedence3')])
345 # tests = [
346 # (["test2"], testp, rpp, "precedence2"),
347 # (["test3"], testp, rpp, "precedence3"),
348 # ]
349 # self.echeck_ordered_list('libs-only-l', tests)
350 #
351 # def test_ros_package_path_colons(self):
352 # # scatter some colons into ros package path to make sure rospack doesn't mind
353 # testp = os.path.abspath('test')
354 # test2p = os.path.abspath('test2')
355 # test3p = os.path.abspath('test3')
356 # tests = [
357 # (["base","base_two"], testp, "::%s:::"%testp, "deps"),
358 # (["base","base_two"], testp, "::", "deps"),
359 # ]
360 # self.echeck_ordered_list('deps', tests)
361 # tests = [
362 # (["test"], testp, ":::%s:"%test2p, "precedence1"),
363 # (["test2"],testp, "::%s::%s::"%(test2p,test3p), "precedence2"),
364 # ]
365 # self.echeck_ordered_list("libs-only-l", tests)
366 #
367 # def test_ros_package_path_bad_paths(self):
368 # testp = os.path.abspath('test')
369 # test2p = os.path.abspath('test2')
370 # non_existentp = os.path.abspath('test')
371 # tests = [
372 # (["test"], testp, non_existentp, "precedence1"),
373 # (["test2"],testp, ":%s:%s"%(non_existentp, test2p), "precedence2"),
374 # (["test2"],testp, ":%s:%s"%(test2p, non_existentp), "precedence2"),
375 # ]
376 # self.echeck_ordered_list("libs-only-l", tests)
377 #
378 # ################################################################################
379 # ## rospack list
380 #
381 # def _rospack_list(self, ros_root, ros_package_path):
382 # env = os.environ.copy()
383 # if ros_root is not None:
384 # env[ROS_ROOT] = ros_root
385 # else:
386 # del env[ROS_ROOT]
387 # if ros_package_path is not None:
388 # env[ROS_PACKAGE_PATH] = ros_package_path
389 # elif ROS_PACKAGE_PATH in env:
390 # del env[ROS_PACKAGE_PATH]
391 # args = ["rospack", 'list']
392 # p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
393 # retval = p.communicate()[0]
394 # return p.returncode, retval.strip().decode('ascii')
395 #
396 # def _check_rospack_list(self, expected, retval):
397 # lines = [l for l in retval.split('\n') if l]
398 # packages = [l[:l.find(' ')] for l in lines]
399 # # canonicalize paths
400 # paths = [os.path.abspath(l[l.find(' ')+1:]) for l in lines]
401 # result = {}
402 # for pack, path in zip(packages, paths):
403 # result[pack] = os.path.abspath(path)
404 # self.failIf(set(expected.keys()) ^ set(packages), "package lists do not match (expected vs. actual): %s vs %s"%(expected.keys(), packages))
405 # for pack,path in expected.items():
406 # self.assertEquals(path, result[pack])
407 #
408 # ## test rospack list on an empty tree
409 # def test_rospack_list_empty(self):
410 # rr = os.path.abspath('test_empty')
411 # retcode, retval = self._rospack_list(rr, None)
412 # self.assertEquals(0, retcode)
413 # self.failIf(retval, "rospack list on empty directory returned value %s"%retval)
414 #
415 # # test that rospack list removes duplicates
416 # def test_rospack_list_dups(self):
417 # # make sure result is same if ROS_ROOT=ROS_PACKAGE_PATH
418 # rr = os.path.abspath('structure_test')
419 # retcode, retval = self._rospack_list(rr, None)
420 # self.assertEquals(0, retcode)
421 # retcode2, retval2 = self._rospack_list(rr, rr)
422 # self.assertEquals(0, retcode2)
423 # self.assertEquals(retval, retval2, "rospack list did not remove duplicates")
424 #
425 # def test_rospack_list_no_rpp(self):
426 # rr = os.path.abspath('structure_test')
427 # expected = structure_test.copy()
428 # retcode, retval = self._rospack_list(rr, None)
429 # self.assertEquals(0, retcode)
430 # self._check_rospack_list(expected, retval)
431 #
432 # #TODO: symlink test
433 # #TODO: test with ros package path
434 #
435 # ################################################################################
436 # ## rospack list-names
437 #
438 # def _rospack_list_names(self, ros_root, ros_package_path):
439 # env = os.environ.copy()
440 # if ros_root is not None:
441 # env[ROS_ROOT] = ros_root
442 # else:
443 # del env[ROS_ROOT]
444 # if ros_package_path is not None:
445 # env[ROS_PACKAGE_PATH] = ros_package_path
446 # elif ROS_PACKAGE_PATH in env:
447 # del env[ROS_PACKAGE_PATH]
448 # args = ["rospack", 'list-names']
449 # p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
450 # retval = p.communicate()[0]
451 # return p.returncode, retval.strip().decode('ascii')
452 #
453 # ## test rospack list-names on an empty tree
454 # def test_rospack_list_names_empty(self):
455 # rr = os.path.abspath('test_empty')
456 # retcode, retval = self._rospack_list_names(rr, None)
457 # self.assertEquals(0, retcode)
458 # self.failIf(retval, "rospack list-names on empty directory returned value %s"%retval)
459 #
460 # # test that rospack list removes duplicates
461 # def test_rospack_list_names_dups(self):
462 # # make sure result is same if ROS_ROOT=ROS_PACKAGE_PATH
463 # rr = os.path.abspath('structure_test')
464 # retcode, retval = self._rospack_list_names(rr, None)
465 # self.assertEquals(0, retcode)
466 # retcode2, retval2 = self._rospack_list_names(rr, rr)
467 # self.assertEquals(0, retcode2)
468 # self.assertEquals(retval, retval2, "rospack list-names did not remove duplicates")
469 #
470 # def test_rospack_list_names_no_rpp(self):
471 # rr = os.path.abspath('structure_test')
472 # expected = set(structure_test.copy().keys())
473 # retcode, retval = self._rospack_list_names(rr, None)
474 # self.assertEquals(0, retcode)
475 # self.assertEquals(expected, set(retval.split()))
476 #
477 # #TODO: symlink test
478 # #TODO: test with ros package path
479 #
480 # ################################################################################
481 # ## rospack find
482 #
483 # ## test rospack find on non-existent package
484 # def test_rospack_find_fail(self):
485 # rr = os.path.abspath('test_empty')
486 # self.erospack_fail(rr, None, 'package', 'find')
487 #
488 # ## test rospack find with ros_package_path set directly to a package
489 # def test_rospack_find_direct(self):
490 # testp = os.path.abspath('test')
491 # package1p = os.path.abspath(os.path.join('structure_test', 'package1'))
492 # self.erospack_succeed(testp, package1p, 'package1', 'find')
493 # self.assertEquals(package1p, self.erun_rospack(testp, package1p, 'package1', 'find'))
494 #
495 # def test_rospack_find_no_rpp(self):
496 # rr = os.path.abspath('structure_test')
497 # expected = structure_test.copy()
498 # for package,path in expected.items():
499 # self.erospack_succeed(rr, None, package, 'find')
500 # self.assertEquals(path, os.path.abspath(self.erun_rospack(rr, None, package, 'find')))
501 #
502 # #TODO: symlink test
503 # #TODO: test with ros package path
504 #
505 # ################################################################################
506 # ## DEPENDENCIES
507 #
508 # def test_deps(self):
509 # depth_list = ['depth-%s'%i for i in range(1, 101)]
510 # depth_list.reverse()
511 # tests = [
512 # (["base","base_two"], "deps"),
513 # (["base","base_two","deps"], "deps_higher"),
514 # (["base","base_two","deps","deps_higher"],"deps_dup"),
515 # (depth_list, "depth-0")
516 # ]
517 # self.check_ordered_list('deps', tests)
518 #
519 # def test_deps1(self):
520 # tests = [
521 # (["base","base_two"], "deps"),
522 # (["deps"], "deps_higher"),
523 # (["depth-1"], "depth-0"),
524 # (["depth-99"], "depth-98"),
525 # ]
526 # self.check_ordered_list('deps1',tests)
527 #
528 # def test_deps_invalid(self):
529 # self.rospack_fail("deps_invalid", "deps")
530 #
531 # def test_depends_on(self):
532 # depth_list = ['depth-%s'%i for i in range(0, 100)]
533 # depth_list.reverse()
534 #
535 # self.rospack_succeed("deps", "depends-on")
536 # tests = [
537 # (["deps_higher","deps_dup"], "deps"),
538 # ([], "depth-0"),
539 # (depth_list, "depth-100"),
540 # ]
541 # self.check_ordered_list("depends-on", tests)
542 #
543 # def test_depends_on1(self):
544 # # sanity check first
545 # self.rospack_succeed("deps", "depends-on")
546 # tests = [
547 # (["deps_higher"], "deps"),
548 # (["deps", "deps_dup", "plugins"], "base"),
549 # (["deps", "deps_dup"], "base_two"),
550 # ]
551 # self.check_unordered_list("depends-on1", tests)
552 #
553 # def test_depends_on_nonexistent(self):
554 # self.rospack_fail("deps", "deps_nonexistent")
555 # self.rospack_fail("deps", "nonexistentpackage")
556 # tests = [
557 # (["deps_nonexistent"], "nonexistentpackage"),
558 # ]
559 # self.check_ordered_list("depends-on", tests)
560 #
561 # def test_lflags_base(self):
562 # self.rospack_succeed("base", "libs-only-l")
563 # self.assertEquals("foo", self.run_rospack("base", "libs-only-l"))
564 #
565 # def test_circular(self):
566 # testp = os.path.abspath("test")
567 # self.erospack_fail(testp, os.path.abspath("test_circular/cycle0"), "self_ref", "deps")
568 # self.erospack_fail(testp, os.path.abspath("test_circular/cycle1"), "friend1", "deps")
569 # self.erospack_fail(testp, os.path.abspath("test_circular/cycle1"), "friend2", "deps")
570 # self.erospack_fail(testp, os.path.abspath("test_circular/cycle2"), "friend1", "deps")
571 # self.erospack_fail(testp, os.path.abspath("test_circular/cycle2"), "friend2", "deps")
572 # self.erospack_fail(testp, os.path.abspath("test_circular/cycle2"), "friend3", "deps")
573 #
574 # def test_lflags_backquote(self):
575 # self.rospack_succeed("backquote", "libs-only-l")
576 # self.assertEquals("loki foo backquote", self.run_rospack("backquote", "libs-only-l"))
577 #
578 # def test_backquote_invalid(self):
579 # self.rospack_fail("backquote_invalid", "libs-only-other")
580 #
581 # # Strip out '/opt/ros' and friends from flags before checking them
582 # def strip_opt_ros(self, flags):
583 # prefix = '/opt/ros'
584 # if 'ROS_BINDEPS_PATH' in os.environ:
585 # prefix = os.environ['ROS_BINDEPS_PATH']
586 # tostrip = [prefix + '/lib',
587 # prefix + '/include',
588 # '-L' + prefix + '/lib',
589 # '-I' + prefix + '/include',
590 # '-Wl,-rpath,' + prefix + '/lib']
591 # res = ''
592 # for f in flags.split(' '):
593 # if f and f not in tostrip:
594 # if len(res) > 0:
595 # res += ' '
596 # res += f
597 # return res
598 #
599 # def test_Lflags_backquote(self):
600 # self.rospack_succeed("backquote", "libs-only-L")
601 # self.assertEquals("odin", self.strip_opt_ros(self.run_rospack("backquote", "libs-only-L")))
602 #
603 # def test_cflags_backquote(self):
604 # self.rospack_succeed("backquote", "cflags-only-I")
605 # self.assertEquals("blah backquote", self.strip_opt_ros(self.run_rospack("backquote", "cflags-only-I")))
606 #
607 # def test_lflags_deps(self):
608 # self.rospack_succeed("deps", "libs-only-l")
609 # self.assertEquals("loki foo bar", self.run_rospack("deps", "libs-only-l"))
610 #
611 # def test_lflags_deps_only(self):
612 # self.rospack_succeed("deps", "libs-only-l --deps-only")
613 # self.assertEquals("foo bar", self.run_rospack("deps", "libs-only-l --deps-only"))
614 #
615 # def test_empty_lflags(self):
616 # tests = [([], "deps_empty")]
617 # commands = ["libs-only-l", "libs-only-L", "libs-only-other"]
618 # for c in commands:
619 # self.check_ordered_list(c, tests)
620 #
621 # def test_empty_cflags(self):
622 # tests = [([], "deps_empty")]
623 # commands = ["cflags-only-I", "cflags-only-other"]
624 # for c in commands:
625 # self.check_ordered_list(c, tests)
626 #
627 # def test_empty_vcs(self):
628 # self.rospack_succeed("empty", "vcs0")
629 # self.assertEquals("type: \turl:", self.run_rospack("empty", "vcs0"))
630 # self.rospack_succeed("deps_empty", "vcs")
631 # self.assertEquals("type: svn\turl: \ntype: \turl:", self.run_rospack("deps_empty", "vcs"))
632 #
633 # def test_lflags_invalid(self):
634 # self.rospack_fail("invalid", "libs-only-l")
635 #
636 # def test_vcs_invalid(self):
637 # self.rospack_fail("invalid", "vcs")
638 #
639 # def test_deps1_invalid(self):
640 # self.rospack_fail("invalid", "deps1")
641 #
642 # def test_vcs0_deps(self):
643 # self.rospack_succeed("deps", "vcs0")
644 # self.failIf(self.run_rospack("deps", "vcs0"))
645 #
646 # def test_vcs_deps(self):
647 # self.rospack_succeed("deps", "vcs")
648 # self.assertEquals("type: svn\turl: https://ros.svn.sourceforge.net/svnroot/ros/trunk\n"+
649 # "type: svn\turl: https://ros.svn.sourceforge.net/svnroot/ros/branches", self.run_rospack("deps", "vcs"))
650 #
651 # def test_deps_manifests(self):
652 # self.rospack_succeed("deps", "deps-manifests")
653 # testp = os.path.abspath('test')
654 # expected = os.path.join(testp, 'base/manifest.xml') + ' ' + os.path.join(testp, 'base_two/manifest.xml')
655 # self.assertEquals(expected,
656 # self.run_rospack("deps", "deps-manifests"))
657 #
658 # def test_deps_indent(self):
659 # self.rospack_succeed("deps_higher", "deps-indent")
660 # testp = os.path.abspath('test')
661 # expected = 'deps\n base\n base_two'
662 # self.assertEquals(expected,
663 # self.run_rospack("deps_higher", "deps-indent"))
664 #
665 # def _rospack_langs(self, ros_root, ros_package_path, ros_lang_disable):
666 # env = os.environ.copy()
667 # if ros_root is not None:
668 # env[ROS_ROOT] = ros_root
669 # else:
670 # del env[ROS_ROOT]
671 # if ros_package_path is not None:
672 # env[ROS_PACKAGE_PATH] = ros_package_path
673 # elif ROS_PACKAGE_PATH in env:
674 # del env[ROS_PACKAGE_PATH]
675 # if ros_lang_disable is not None:
676 # env[ROS_LANG_DISABLE] = ros_lang_disable
677 # elif ROS_LANG_DISABLE in env:
678 # del env[ROS_LANG_DISABLE]
679 # args = ["rospack", 'langs']
680 # p = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
681 # retval = p.communicate()[0]
682 # return p.returncode, retval.strip().decode('ascii')
683 #
684 # def test_langs(self):
685 # rr = os.path.abspath('test')
686 # retcode, retval = self._rospack_langs(rr, None, None)
687 # self.assertEquals(0, retcode)
688 # # No guarantees on ordering of lang result
689 # l = retval.split()
690 # s = set(l)
691 # expected = set(['rosfoo', 'rosbar'])
692 # self.assertEquals(s, expected)
693 #
694 # def test_langs_disable(self):
695 # rr = os.path.abspath('test')
696 # disable = 'rosfoo'
697 # retcode, retval = self._rospack_langs(rr, None, disable)
698 # self.assertEquals(0, retcode)
699 # # No guarantees on ordering of lang result
700 # l = retval.split()
701 # s = set(l)
702 # expected = set(['rosbar'])
703 # self.assertEquals(s, expected)
704 #
705 # def test_langs_empty(self):
706 # rr = os.path.abspath('test2')
707 # retcode, retval = self._rospack_langs(rr, None, None)
708 # self.assertEquals(0, retcode)
709 # self.failIf(retval, "rospack langs on empty directory returned value %s"%retval)
0 <package>
1 <export>
2 <cpp lflags="-lbar"/>
3 </export>
4 <versioncontrol typo="svn" uri="https://ros.svn.sourceforge.net/svnroot/ros/branches"/>
5 </package>
6
0 <package>
1 <export>
2 <cpp lflags="-ltest2"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-ltest2"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-ltest2"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 </package>
0 <stack>
1 </stack>
2
0 <package>
1 <export>
2 <cpp lflags="-ltest3"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-ltest3"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <export>
2 <cpp lflags="-ltest3"/>
3 </export>
4 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
5 </package>
0 <package>
1 <description>Fake description</description>
2 <author>No One</author>
3 <license>Fake License</license>
4 <export>
5 <cpp lflags="-lfoo"/>
6 </export>
7 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
8 <depend package="self_ref" />
9 </package>
0 <package>
1 <description>Fake description</description>
2 <author>No One</author>
3 <license>Fake License</license>
4 <export>
5 <cpp lflags="-lfoo"/>
6 </export>
7 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
8 <depend package="friend2" />
9 </package>
0 <package>
1 <description>Fake description</description>
2 <author>No One</author>
3 <license>Fake License</license>
4 <export>
5 <cpp lflags="-lfoo"/>
6 </export>
7 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
8 <depend package="friend1" />
9 </package>
0 <package>
1 <description>Fake description</description>
2 <author>No One</author>
3 <license>Fake License</license>
4 <export>
5 <cpp lflags="-lfoo"/>
6 </export>
7 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
8 <depend package="friend2" />
9 </package>
0 <package>
1 <description>Fake description</description>
2 <author>No One</author>
3 <license>Fake License</license>
4 <export>
5 <cpp lflags="-lfoo"/>
6 </export>
7 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
8 <depend package="friend3" />
9 </package>
0 <package>
1 <description>Fake description</description>
2 <author>No One</author>
3 <license>Fake License</license>
4 <export>
5 <cpp lflags="-lfoo"/>
6 </export>
7 <versioncontrol type="svn" url="https://ros.svn.sourceforge.net/svnroot/ros/trunk"/>
8 <depend package="friend1" />
9 </package>
(New empty file)