Codebase list ros-ros / e5dca9f
Imported Upstream version 1.9.11 Thomas Moulard 10 years ago
209 changed file(s) with 18981 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 # Lines that start with '#' are comments.
1 # ignore objects and libraries
2 *.o
3 *.a
4 *.d
5 *.so
6 *.lo
7 *.la
8 # ignore python compiled files:
9 *.pyc
10 # ignore gcov files
11 *.gcda
12 *.gcno
13 *.gcov
14 # ignore tag files
15 tags
16 # we don't care about vi swp files
17 *.swp
18 # we also don't care about emacs swap files
19 *~
20 .backup-*
21 # nothing in the root directory should be checked in.
22 root
23 # setup.bash is a generated file.
24 setup.bash
25 config.mk
26 # gcov files
27 *.gcno
28 *.gcov
29 # Testing output files
30 *.out
31 .build-version
32 .rospack_cache
33 *.tar.gz
34 3rdparty/cmake/build
35 3rdparty/cmake/cmake
36 3rdparty/cmake/cmake-2.6.0
37 3rdparty/numpy/build
38 3rdparty/numpy/numpy
39 3rdparty/numpy/src
40 3rdparty/tinyxml/build
41 3rdparty/xmlrpc++/build
42 bin/rosinstall
43 bin/roslaunch
44 bin/rosmap
45 bin/rosmaster
46 bin/rospack
47 bin/rosplay
48 bin/rosprereq
49 bin/rosrecord
50 bin/rostest
51 bin/svnmerge
52 core/experimental/roslisp/build
53 core/experimental/roslisp/lib
54 core/genmsg_cpp/build
55 core/genmsg_cpp/genmsg
56 core/genmsg_cpp/genmsg_lisp
57 core/genmsg_cpp/genmsgtest
58 core/genmsg_cpp/gensrv
59 core/genmsg_cpp/gensrv_lisp
60 core/genmsg_cpp/submsgs
61 core/rosconsole/build
62 core/rosconsole/examples/example
63 core/roscpp/build
64 core/roscpp/msg/cpp
65 core/roscpp/msg/lisp
66 core/roscpp/src/roscpp
67 core/roscpp/srv/cpp
68 core/roscpp/srv/lisp
69 core/rosout/build
70 core/rosout/rosout
71 core/rospack_test/build
72 core/rospy/build
73 core/rostime/build
74 core/rostools/build
75 core/rostools/msg/cpp
76 core/rostools/msg/lisp
77 core/rostools/src/rostools/msg
78 core/roslib/build
79 core/roslib/msg/cpp
80 core/roslib/msg/lisp
81 core/roslib/src/rostools/msg
82 demos/roscpp_demo/bin
83 demos/roscpp_demo/build
84 demos/rospy_demo/build
85 demos/rospy_demo/msg/cpp
86 demos/rospy_demo/msg/lisp
87 demos/rospy_demo/src/rospy_demo/msg
88 param/rosparam/build
89 param/shparam/build
90 param/shparam/shparam
91 param/xmlparam/build
92 param/xmlparam/xmlparam
93 std_msgs/build
94 std_msgs/msg/cpp
95 std_msgs/msg/lisp
96 std_msgs/src
97 std_srvs/build
98 std_srvs/src
99 std_srvs/srv/cpp
100 std_srvs/srv/lisp
101 test/gtest/build
102 test/gtest/gtest
103 test/gtest/gtest-1.0.1
104 test/param_setget/depend
105 test/param_setget/param_setget
106 test/performance/roscpp_msg_serdes/build
107 test/performance/roscpp_msg_serdes/pointcloud_serdes
108 test/rostest/build
109 test/test_ros/build
110 test/test_roscpp/bin
111 test/test_roscpp/build
112 test/test_roslaunch/build
113 test/test_ros/msg/cpp
114 test/test_ros/msg/lisp
115 test/test_rospy/build
116 test/test_rospy/msg/cpp
117 test/test_rospy/msg/lisp
118 test/test_rospy/src/test_rospy/msg
119 test/test_rospy/src/test_rospy/srv
120 test/test_rospy/srv/cpp
121 test/test_rospy/srv/lisp
122 test/test_ros/src/test_ros/msg
123 tools/rosinstall/build
124 tools/rosinstall/rosinstall
125 tools/roslaunch/build
126 tools/rosmap/depend
127 tools/rospack/depend
128 tools/rosprereq/depend
129 tools/rosrecord/bin
130 tools/rosrecord/build
131 tools/vizdeps/deps.gv
132 tools/vizdeps/deps.pdf
133 tutorials/roscpp_tutorials/bin
134 tutorials/roscpp_tutorials/build
135 tutorials/roscpp_tutorials/src
136 tutorials/roscpp_tutorials/srv/cpp
137 tutorials/roscpp_tutorials/srv/lisp
138 tutorials/rospy_tutorials/build
139 tutorials/rospy_tutorials/src
140 tutorials/rospy_tutorials/srv/cpp
141 tutorials/rospy_tutorials/srv/lisp
142 util/rosthread/build
143 .build_version
144 .rosgcov_files
145 3rdparty/gtest/gtest
146 /core/genmsg_cpp/genmsg_java
147 /core/genmsg_cpp/genmsg_oct
148 /core/genmsg_cpp/gensrv_java
149 /core/genmsg_cpp/gensrv_oct
150 /core/roslib/src/roslib
0 Morgan Quigley <mquigley@cs.stanford.edu>
1
2 Eric Berger <berger@willowgarage.com>
3 Ken Conley <kwc@willowgarage.com>
4 Rosen Diankov <rdiankov@cs.cmu.edu>
5 Josh Faust <jfaust@willowgarage.com>
6 Tully Foote <tfoote@willowgarage.com>
7 Brian Gerkey <gerkey@willowgarage.com>
8 Jeremy Leibs <leibs@willowgarage.com>
9 Bhaskara Marthi <bhaskara@willowgarage.com>
10 Troy Straszheim <straszheim@willowgarage.com>
11 Rob Wheeler <wheeler@willowgarage.com>
0 cmake_minimum_required(VERSION 2.8)
1 project(ros)
2 find_package(catkin REQUIRED)
3 catkin_stack()
4 catkin_project(ros)
5 find_package(Boost REQUIRED COMPONENTS thread)
6
7 # Must call catkin_python_setup() early, to ensure that things like
8 # rosunit are available in build-space, and can be found by things like
9 # catkin_add_gtest(), which is called from within lower-level CMakeLists.txts.
10 catkin_python_setup()
11
12 catkin_add_env_hooks(10.ros SHELLS bat sh DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/env-hooks)
13
14 foreach(subdir
15 core/roslang
16 core/roslib
17 tools/rosboost_cfg
18 tools/rosbash
19 tools/rosclean
20 tools/roscreate
21 tools/rosmake
22 tools/rosunit
23 )
24 add_subdirectory(${subdir})
25 endforeach()
26
27 # INSTALLATION
28 install(FILES config/rosconsole.config
29 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/config)
30
31 # setup our backwards-compatibility ROS_ROOT
32 install(DIRECTORY core/rosbuild/
33 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/core/rosbuild
34 PATTERN ".svn" EXCLUDE
35 PATTERN "bin" EXCLUDE
36 )
37 install(PROGRAMS
38 core/rosbuild/bin/check_same_directories.py
39 core/rosbuild/bin/download_checkmd5.py
40 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/core/rosbuild/bin)
41 install(DIRECTORY core/mk/
42 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/core/mk
43 PATTERN ".svn" EXCLUDE
44 )
45
46 install(DIRECTORY tools/roscreate/templates/
47 DESTINATION share/roscreate/templates
48 PATTERN ".svn" EXCLUDE)
0 Check the manifest.xml in each package directory for license information.
0 Copyright (C) 2008, Morgan Quigley
1
2 Redistribution and use in source and binary forms, with or without
3 modification, are permitted provided that the following conditions are met:
4 * Redistributions of source code must retain the above copyright notice,
5 this list of conditions and the following disclaimer.
6 * Redistributions in binary form must reproduce the above copyright
7 notice, this list of conditions and the following disclaimer in the
8 documentation and/or other materials provided with the distribution.
9 * Neither the name of Stanford University nor the names of its
10 contributors may be used to endorse or promote products derived from
11 this software without specific prior written permission.
12
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
17 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23 POSSIBILITY OF SUCH DAMAGE.
24
0 Copyright (c) 2009, Willow Garage, Inc.
1 All rights reserved.
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
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright
9 notice, this list of conditions and the following disclaimer in the
10 documentation and/or other materials provided with the distribution.
11 * Neither the name of Willow Garage, Inc. nor the names of its
12 contributors may be used to endorse or promote products derived from
13 this software without specific prior written permission.
14
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 POSSIBILITY OF SUCH DAMAGE.
0 Copyright (C) 2008, Morgan Quigley and Willow Garage, Inc.
1
2 Redistribution and use in source and binary forms, with or without
3 modification, are permitted provided that the following conditions are met:
4 * Redistributions of source code must retain the above copyright notice,
5 this list of conditions and the following disclaimer.
6 * Redistributions in binary form must reproduce the above copyright
7 notice, this list of conditions and the following disclaimer in the
8 documentation and/or other materials provided with the distribution.
9 * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
10 contributors may be used to endorse or promote products derived from
11 this software without specific prior written permission.
12
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
17 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23 POSSIBILITY OF SUCH DAMAGE.
0 Copyright (c) 2008, Willow Garage, Inc.
1 All rights reserved.
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
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright
9 notice, this list of conditions and the following disclaimer in the
10 documentation and/or other materials provided with the distribution.
11 * Neither the name of Willow Garage, Inc. nor the names of its
12 contributors may be used to endorse or promote products derived from
13 this software without specific prior written permission.
14
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 POSSIBILITY OF SUCH DAMAGE.
0 Robot Operating System (ROS)
1 ===============================================================================
2
3 ROS is a meta-operating system for your robot. It provides
4 language-independent and network-transparent communication for a
5 distributed robot control system.
6
7 Installation Notes
8 ------------------
9
10 For full installation instructions, including system prerequisites and
11 platform-specific help, see:
12
13 http://ros.org/wiki/ROS/Installation
0 #
1 # rosconsole will find this file by default at $ROS_ROOT/config/rosconsole.config
2 #
3 # You can define your own by e.g. copying this file and setting
4 # ROSCONSOLE_CONFIG_FILE (in your environment) to point to the new file
5 #
6 log4j.logger.ros=INFO
7 log4j.logger.ros.roscpp.superdebug=WARN
0 cmake_minimum_required(VERSION 2.8)
1 project(mk)
2 find_package(catkin REQUIRED)
3 catkin_package()
4 catkin_package_export()
5
6 install(DIRECTORY .
7 DESTINATION ${CATKIN_GLOBAL_SHARE_DESTINATION}/ros/core/mk
8 PATTERN ".svn" EXCLUDE)
0 # A target to test building of a package, all of its dependencies, and all
1 # of the things that depend on it
2 #
3 # For internal use only.
4
5 # HACK: assume that the package name is the name of the directory we're
6 # sitting in
7 pkg = $(shell basename $(PWD))
8
9 rosalldeps = $(shell rospack find rospack)/rosalldeps
10
11 deps = $(shell $(rosalldeps) -H 1 $(pkg))
12 rules = $(foreach d, $(deps), cd $(shell rospack find $(d)) && make &&) true;
13 .PHONY: build
14 build:
15 $(rules)
16 rules_test = $(foreach d, $(deps), cd $(shell rospack find $(d)) && make test &&) true;
17 .PHONY: build-test
18 build-test:
19 $(rules_test)
20
21 deps_all = $(shell $(rosalldeps) $(pkg))
22 rules_all = $(foreach d, $(deps_all), cd $(shell rospack find $(d)) && make &&) true;
23 .PHONY: build-all
24 build-all:
25 $(rules_all)
26 rules_all_test = $(foreach d, $(deps_all), cd $(shell rospack find $(d)) && make test &&) true;
27 .PHONY: build-test-all
28 build-test-all:
29 $(rules_all_test)
0 # This file can be used to automate cloning and patching of 3rd
1 # party software.
2 #
3 # Before including this file, e.g.:
4 # include $(shell rospack find mk)/bzr_checkout.mk
5 # define the following make variables:
6 # BZR_DIR: name of directory into which you want to clone to
7 # BZR_URL: full URL to download
8 # BZR_PATCH: your (list of) patch file(s) to patch the downloaded software
9 # BZR_REVISION: a bzr revisionspec string
10
11 ifneq ($(BZR_REVISION),)
12 BZR_REV=-r $(BZR_REVISION)
13 else
14 BZR_REV=
15 endif
16
17 $(BZR_DIR):
18 bzr branch $(BZR_URL) $(BZR_REV) $(BZR_DIR)
19 touch rospack_nosubdirs
20
21 patched: $(BZR_DIR) $(BZR_PATCH) Makefile
22 cd $(BZR_DIR) && bzr revert $(BZR_REV)
23 ifneq ($(strip $(BZR_PATCH)),)
24 $(foreach PATCH, $(BZR_PATCH), patch -d $(BZR_DIR) -p1 < $(PATCH) && ) echo patched
25 touch rospack_nosubdirs
26 touch patched
27 endif
28
29 download: $(BZR_DIR) patched
0 # set EXTRA_CMAKE_FLAGS in the including Makefile in order to add tweaks
1 CMAKE_FLAGS= -Wdev -DCMAKE_TOOLCHAIN_FILE=`rospack find rosbuild`/rostoolchain.cmake $(EXTRA_CMAKE_FLAGS)
2
3 # The all target does the heavy lifting, creating the build directory and
4 # invoking CMake
5 all:
6 @mkdir -p build
7 -mkdir -p bin
8 @rm -rf msg/cpp srv/cpp # make sure there are no msg/cpp or srv/cpp directories
9 cd build && cmake $(CMAKE_FLAGS) ..
10 ifneq ($(MAKE),)
11 cd build && $(MAKE) $(ROS_PARALLEL_JOBS)
12 else
13 cd build && make $(ROS_PARALLEL_JOBS)
14 endif
15
16 PACKAGE_NAME=$(shell basename $(PWD))
17
18 # The clean target blows everything away
19 # It also removes auto-generated message/service code directories,
20 # to handle the case where the original .msg/.srv file has been removed,
21 # and thus CMake no longer knows about it.
22 clean:
23 -cd build && make clean
24 rm -rf build
25
26 # All other targets are just passed through
27 test: all
28 if cd build && make -k $@; then make test-results; else make test-results && exit 1; fi
29 test-nobuild:
30 @mkdir -p build
31 cd build && cmake $(CMAKE_FLAGS) -Drosbuild_test_nobuild=1 ..
32 if cd build && make rosbuild_clean-test-results && make -k test; then make test-results; else make test-results && exit 1; fi
33 tests: all
34 cd build && make $@
35 test-future: all
36 cd build && make -k $@
37 gcoverage: all
38 cd build && make $@
39
40 eclipse-project:
41 mv Makefile Makefile.ros
42 if ! (cmake -G"Eclipse CDT4 - Unix Makefiles" -Wno-dev . && rm Makefile && rm CMakeCache.txt && rm -rf CMakeFiles); then mv Makefile.ros Makefile && echo "**ERROR building Eclipse project!**" && false; fi
43 mv Makefile.ros Makefile
44 mv .project .project-cmake
45 awk -f $(shell rospack find mk)/eclipse.awk .project-cmake > .project
46 rm .project-cmake
47 python $(shell rospack find mk)/make_pydev_project.py
48
49
50 include $(shell rospack find mk)/buildtest.mk
0 # set EXTRA_CMAKE_FLAGS in the including Makefile in order to add tweaks
1 CMAKE_FLAGS= -Wdev -DCMAKE_TOOLCHAIN_FILE=`rospack find rosbuild`/rostoolchain.cmake $(EXTRA_CMAKE_FLAGS)
2
3 # The all target does the heavy lifting, creating the build directory and
4 # invoking CMake
5 all:
6 @mkdir -p build
7 cd build && cmake $(CMAKE_FLAGS) ..
8 #cd build && make $(ROS_PARALLEL_JOBS)
9
10 # The clean target blows everything away
11 # It also removes auto-generated message/service code directories,
12 # to handle the case where the original .msg/.srv file has been removed,
13 # and thus CMake no longer knows about it.
14 clean:
15 -cd build && make clean
16 #rm -rf msg/cpp msg/lisp msg/oct msg/java srv/cpp srv/lisp srv/oct srv/java src/$(PACKAGE_NAME)/msg src/$(PACKAGE_NAME)/srv
17 rm -rf build
18
19 # Build a source package. Assumes that you're in a clean tree.
20 package_source: all
21 $(shell rospack find rosbuild)/bin/package_source.py $(CURDIR)
22
23 # All other targets are just passed through
24 #test: all
25 # if cd build && make -k $@; then make test-results; else make test-results && exit 1; fi
26 #tests: all
27 # cd build && make $@
28 #test-future: all
29 # cd build && make -k $@
30 #gcoverage: all
31 # cd build && make $@
32
33 #include $(shell rospack find mk)/buildtest.mk
34
35
0 # This file can be used to automate downloading and unpacking of source
1 # distributions
2 #
3 # Before including this file, define the following make variables:
4 #
5 # TARBALL: name of source distribution to be downloaded
6 # TARBALL_URL: full URL (including the file itself) to download
7 # SOURCE_DIR: name of directory into which TARBALL will unpack
8 # UNPACK_CMD: command to use when unpacking (e.g., tar xzf)
9 # TARBALL_PATCH: patch files to apply to SOURCE_DIR
10 # INITIAL_DIR: set this if the tarball unpacks to a different dir than SOURCE_DIR,
11 # and you want the directory moved
12 #
13 # Optional variables:
14 # MD5SUM_FILE: name of md5sum file to check before unpacking
15 #
16 # Because this file declares targets, you almost certainly want to declare
17 # your own 'all' target before including this file. Otherwise, the first
18 # target declared here will become the default.
19
20 # This target is pretty much vestigial, and is only here to support the
21 # download target. The intended use it to depend on the unpacked file,
22 # below, which repeats the download and check logic.
23 $(TARBALL):
24 -mkdir -p build
25 ifneq ($(strip $(MD5SUM_FILE)),)
26 if [ ! -f $(MD5SUM_FILE) ]; then echo "Error: Couldn't find md5sum file $(MD5SUM_FILE)" && false; fi
27 `rospack find rosbuild`/bin/download_checkmd5.py $(TARBALL_URL) $(TARBALL) `awk {'print $$1'} $(MD5SUM_FILE)`
28 else
29 `rospack find rosbuild`/bin/download_checkmd5.py $(TARBALL_URL) $(TARBALL)
30 endif
31 touch -c $(TARBALL)
32
33 download: $(TARBALL)
34
35 $(SOURCE_DIR)/unpacked: $(TARBALL_PATCH)
36 -mkdir -p build
37 ifneq ($(strip $(MD5SUM_FILE)),)
38 if [ ! -f $(MD5SUM_FILE) ]; then echo "Error: Couldn't find md5sum file $(MD5SUM_FILE)" && false; fi
39 `rospack find rosbuild`/bin/download_checkmd5.py $(TARBALL_URL) $(TARBALL) `awk {'print $$1'} $(MD5SUM_FILE)`
40 else
41 `rospack find rosbuild`/bin/download_checkmd5.py $(TARBALL_URL) $(TARBALL)
42 endif
43 touch -c $(TARBALL)
44 rm -rf $(SOURCE_DIR) $(INITIAL_DIR)
45 ifneq ($(strip $(UNPACK_CMD)),)
46 cd build; $(UNPACK_CMD) ../$(TARBALL)
47 else
48 cd build; tar xzf ../$(TARBALL)
49 endif
50 ifneq ($(strip $(INITIAL_DIR)),)
51 mv $(INITIAL_DIR) $(SOURCE_DIR)
52 endif
53 ifneq ($(strip $(TARBALL_PATCH)),)
54 $(foreach patch,$(TARBALL_PATCH), patch -d $(SOURCE_DIR) -p0 < $(patch);)
55 endif
56 touch $(SOURCE_DIR)/unpacked
57
0 //{
1 if(index($0,"VERBOSE=1")>0) {
2 printf "\t\t\t\t\t<value>VERBOSE=1|"
3 printf "ROS_ROOT="ENVIRON["ROS_ROOT"]"|"
4 printf "ROS_PACKAGE_PATH="ENVIRON["ROS_PACKAGE_PATH"]"|"
5 printf "PYTHONPATH="ENVIRON["PYTHONPATH"]"|"
6 printf "PATH="ENVIRON["PATH"]"|"
7 print "</value>"
8 } else {
9 print $0
10 }
11 }
12
0 # This file can be used to automate cloning and patching of 3rd
1 # party software.
2 #
3 # Before including this file, e.g.:
4 # include $(shell rospack find mk)/git_checkout.mk
5 # define the following make variables:
6 # GIT_DIR: name of directory into which you want to clone to
7 # GIT_URL: full URL to download
8 # GIT_PATCH: your (list of) patch file(s) to patch the downloaded software
9 # GIT_REVISION: -
10 $(GIT_DIR):
11 git clone $(GIT_URL) $(GIT_DIR)
12 cd $(GIT_DIR) && git checkout $(GIT_REVISION)
13 touch rospack_nosubdirs
14
15 patched: $(GIT_DIR) $(GIT_PATCH) Makefile
16 cd $(GIT_DIR) && git reset --hard
17 ifneq ($(strip $(GIT_PATCH)),)
18 $(foreach PATCH, $(GIT_PATCH), patch -d $(GIT_DIR) -p1 < $(PATCH) && ) echo patched
19 touch rospack_nosubdirs
20 touch patched
21 endif
22
23 download: $(GIT_DIR) patched
0 # This file can be used to automate cloning and patching of 3rd
1 # party software.
2 #
3 # Before including this file, e.g.:
4 # include $(shell rospack find mk)/hg_checkout.mk
5 # define the following make variables:
6 # HG_DIR: name of directory into which you want to clone to
7 # HG_URL: full URL to download
8 # HG_PATCH: your (list of) patch file(s) to patch the downloaded software
9 # HG_BRANCH: the branch of the repository to update
10 # HG_REVISION: the revision to update
11
12 $(HG_DIR):
13 mkdir -p $(HG_DIR)
14 hg clone $(HG_URL) $(HG_DIR)
15 cd $(HG_DIR) && hg update $(HG_BRANCH) && hg update $(HG_REVISION)
16 touch rospack_nosubdirs
17
18 patched: $(HG_PATCH) Makefile
19 ifneq ($(strip $(HG_PATCH)),)
20 cd $(HG_DIR) && hg revert --all
21 $(foreach PATCH, $(HG_PATCH), patch -d $(HG_DIR) -p1 < $(PATCH) && ) echo patched
22 touch rospack_nosubdirs
23 touch patched
24 endif
25
26 download: $(HG_DIR) patched
0 #!/usr/bin/python
1
2 import sys
3 import os
4 PKG = os.path.split(os.getcwd())[1]
5 print "Creating pydev project for package '%s'"%PKG
6 import roslib; roslib.load_manifest(PKG)
7
8 pathlist = "\n".join(["<path>%s</path>"%path for path in sys.path if os.path.exists(path)])
9
10 pydev_project= '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
11 <?eclipse-pydev version="1.0"?>
12
13 <pydev_project>
14 <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
15 <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property>
16 <pydev_pathproperty name="org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH">
17 %s
18 </pydev_pathproperty>
19 </pydev_project>
20 '''%pathlist
21
22 print "Writing .pydevproject, adding %d modules"%len(sys.path)
23 f = open(".pydevproject","w")
24 f.write(pydev_project)
25 f.close()
0 <package>
1 <description brief="ROS Makefile Includes">A collection of .mk include files for building ROS architectural elements. Most package authors should use cmake .mk, which calls CMake for the build of the package. The other files in this package are intended for use in exotic situations that mostly arise when importing 3rdparty code.</description>
2 <author>Morgan Quigley, Brian Gerkey</author>
3 <license>BSD</license>
4 <review status="Doc reviewed" notes="2010/01/12"/>
5 </package>
0 <package>
1 <name>mk</name>
2 <version>1.9.11</version>
3 <description>
4 A collection of .mk include files for building ROS architectural elements. Most package authors should use cmake .mk, which calls CMake for the build of the package. The other files in this package are intended for use in exotic situations that mostly arise when importing 3rdparty code.
5 </description>
6 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
7 <license>BSD</license>
8
9 <url>http://www.ros.org/wiki/ROS</url>
10 <author>Morgan Quigley</author>
11 <author>Brian Gerkey</author>
12
13 <build_depend>catkin</build_depend>
14 </package>
0
1 ifeq ($(strip $(SVN_CMDLINE)),)
2 SVN_CMDLINE = svn
3 endif
4
5 $(SVN_DIR):
6 $(SVN_CMDLINE) co $(SVN_REVISION) $(SVN_URL) $(SVN_DIR)
7 ifneq ($(strip $(SVN_PATCH)),)
8 $(foreach patch,$(SVN_PATCH), patch -d $(SVN_DIR) -p0 < $(patch);)
9 endif
10 -if test -z "$(SVN_REVISION)" -o "x$(SVN_REVISION)" != "x-r `svn info $(SVN_DIR) | grep Revision | cut -d " " -f 2,2`"; then \
11 cd $(SVN_DIR) && $(SVN_CMDLINE) up $(SVN_REVISION); \
12 fi
13 touch rospack_nosubdirs
14 touch patched
15
16 SVN_UP: $(SVN_DIR)
17
18 # Note that 'svn revert' can't use the --non-interactive option, so we
19 # invoke 'svn' directly, instead of calling $(SVN_CMDLINE)
20 patched: $(SVN_PATCH) Makefile
21 ifneq ($(strip $(SVN_PATCH)),)
22 svn revert -R $(SVN_DIR)
23 endif
24 -cd $(SVN_DIR) && $(SVN_CMDLINE) up $(SVN_REVISION)
25 $(foreach PATCH, $(SVN_PATCH), patch -d $(SVN_DIR) -p0 < $(PATCH) && ) echo patched
26 touch rospack_nosubdirs
27 touch patched
28
29 SVN_UP_REVERT_PATCH: $(SVN_DIR) patched
30
31 download: SVN_UP
0 cmake_minimum_required(VERSION 2.8)
1 project(rosbuild)
2 find_package(catkin REQUIRED)
3 catkin_package()
4 catkin_package_export()
5
6 install(DIRECTORY .
7 DESTINATION ${CATKIN_GLOBAL_SHARE_DESTINATION}/ros/core/rosbuild
8 PATTERN ".svn" EXCLUDE
9 PATTERN "bin" EXCLUDE
10 )
11 install(PROGRAMS
12 bin/check_same_directories.py
13 bin/download_checkmd5.py
14 DESTINATION ${CATKIN_GLOBAL_SHARE_DESTINATION}/ros/core/rosbuild/bin)
0 # - a pkg-config module for CMake
1 #
2 # Usage:
3 # pkg_check_modules(<PREFIX> [REQUIRED] <MODULE> [<MODULE>]*)
4 # checks for all the given modules
5 #
6 # pkg_search_module(<PREFIX> [REQUIRED] <MODULE> [<MODULE>]*)
7 # checks for given modules and uses the first working one
8 #
9 # When the 'REQUIRED' argument was set, macros will fail with an error
10 # when module(s) could not be found
11 #
12 # It sets the following variables:
13 # PKG_CONFIG_FOUND ... true iff pkg-config works on the system
14 # PKG_CONFIG_EXECUTABLE ... pathname of the pkg-config program
15 # <PREFIX>_FOUND ... set to 1 iff module(s) exist
16 #
17 # For the following variables two sets of values exist; first one is the
18 # common one and has the given PREFIX. The second set contains flags
19 # which are given out when pkgconfig was called with the '--static'
20 # option.
21 # <XPREFIX>_LIBRARIES ... only the libraries (w/o the '-l')
22 # <XPREFIX>_LIBRARY_DIRS ... the paths of the libraries (w/o the '-L')
23 # <XPREFIX>_LDFLAGS ... all required linker flags
24 # <XPREFIX>_LDFLAGS_OTHER ... all other linker flags
25 # <XPREFIX>_INCLUDE_DIRS ... the '-I' preprocessor flags (w/o the '-I')
26 # <XPREFIX>_CFLAGS ... all required cflags
27 # <XPREFIX>_CFLAGS_OTHER ... the other compiler flags
28 #
29 # <XPREFIX> = <PREFIX> for common case
30 # <XPREFIX> = <PREFIX>_STATIC for static linking
31 #
32 # There are some special variables whose prefix depends on the count
33 # of given modules. When there is only one module, <PREFIX> stays
34 # unchanged. When there are multiple modules, the prefix will be
35 # changed to <PREFIX>_<MODNAME>:
36 # <XPREFIX>_VERSION ... version of the module
37 # <XPREFIX>_PREFIX ... prefix-directory of the module
38 # <XPREFIX>_INCLUDEDIR ... include-dir of the module
39 # <XPREFIX>_LIBDIR ... lib-dir of the module
40 #
41 # <XPREFIX> = <PREFIX> when |MODULES| == 1, else
42 # <XPREFIX> = <PREFIX>_<MODNAME>
43 #
44 # A <MODULE> parameter can have the following formats:
45 # {MODNAME} ... matches any version
46 # {MODNAME}>={VERSION} ... at least version <VERSION> is required
47 # {MODNAME}={VERSION} ... exactly version <VERSION> is required
48 # {MODNAME}<={VERSION} ... modules must not be newer than <VERSION>
49 #
50 # Examples
51 # pkg_check_modules (GLIB2 glib-2.0)
52 #
53 # pkg_check_modules (GLIB2 glib-2.0>=2.10)
54 # requires at least version 2.10 of glib2 and defines e.g.
55 # GLIB2_VERSION=2.10.3
56 #
57 # pkg_check_modules (FOO glib-2.0>=2.10 gtk+-2.0)
58 # requires both glib2 and gtk2, and defines e.g.
59 # FOO_glib-2.0_VERSION=2.10.3
60 # FOO_gtk+-2.0_VERSION=2.8.20
61 #
62 # pkg_check_modules (XRENDER REQUIRED xrender)
63 # defines e.g.:
64 # XRENDER_LIBRARIES=Xrender;X11
65 # XRENDER_STATIC_LIBRARIES=Xrender;X11;pthread;Xau;Xdmcp
66 #
67 # pkg_search_module (BAR libxml-2.0 libxml2 libxml>=2)
68
69
70 # Copyright (C) 2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
71 #
72 # Redistribution and use, with or without modification, are permitted
73 # provided that the following conditions are met:
74 #
75 # 1. Redistributions must retain the above copyright notice, this
76 # list of conditions and the following disclaimer.
77 # 2. The name of the author may not be used to endorse or promote
78 # products derived from this software without specific prior
79 # written permission.
80 #
81 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
82 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
83 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
84 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
85 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
86 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
87 # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
88 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
89 # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
90 # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
91 # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
92
93
94 ### Common stuff ####
95 set(PKG_CONFIG_VERSION 1)
96 set(PKG_CONFIG_FOUND 0)
97
98 find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config DOC "pkg-config executable")
99 mark_as_advanced(PKG_CONFIG_EXECUTABLE)
100
101 if(PKG_CONFIG_EXECUTABLE)
102 set(PKG_CONFIG_FOUND 1)
103 endif(PKG_CONFIG_EXECUTABLE)
104
105
106 # Unsets the given variables
107 macro(_pkgconfig_unset var)
108 set(${var} "" CACHE INTERNAL "")
109 endmacro(_pkgconfig_unset)
110
111 macro(_pkgconfig_set var value)
112 set(${var} ${value} CACHE INTERNAL "")
113 endmacro(_pkgconfig_set)
114
115 # Invokes pkgconfig, cleans up the result and sets variables
116 macro(_pkgconfig_invoke _pkglist _prefix _varname _regexp)
117 set(_pkgconfig_invoke_result)
118
119 execute_process(
120 COMMAND ${PKG_CONFIG_EXECUTABLE} ${ARGN} ${_pkglist}
121 OUTPUT_VARIABLE _pkgconfig_invoke_result
122 RESULT_VARIABLE _pkgconfig_failed)
123
124 if (_pkgconfig_failed)
125 set(_pkgconfig_${_varname} "")
126 _pkgconfig_unset(${_prefix}_${_varname})
127 else(_pkgconfig_failed)
128 string(REGEX REPLACE "[\r\n]" " " _pkgconfig_invoke_result "${_pkgconfig_invoke_result}")
129 string(REGEX REPLACE " +$" "" _pkgconfig_invoke_result "${_pkgconfig_invoke_result}")
130
131 if (NOT ${_regexp} STREQUAL "")
132 string(REGEX REPLACE "${_regexp}" " " _pkgconfig_invoke_result "${_pkgconfig_invoke_result}")
133 endif(NOT ${_regexp} STREQUAL "")
134
135 separate_arguments(_pkgconfig_invoke_result)
136
137 #message(STATUS " ${_varname} ... ${_pkgconfig_invoke_result}")
138 set(_pkgconfig_${_varname} ${_pkgconfig_invoke_result})
139 _pkgconfig_set(${_prefix}_${_varname} "${_pkgconfig_invoke_result}")
140 endif(_pkgconfig_failed)
141 endmacro(_pkgconfig_invoke)
142
143 # Invokes pkgconfig two times; once without '--static' and once with
144 # '--static'
145 macro(_pkgconfig_invoke_dyn _pkglist _prefix _varname cleanup_regexp)
146 _pkgconfig_invoke("${_pkglist}" ${_prefix} ${_varname} "${cleanup_regexp}" ${ARGN})
147 _pkgconfig_invoke("${_pkglist}" ${_prefix} STATIC_${_varname} "${cleanup_regexp}" --static ${ARGN})
148 endmacro(_pkgconfig_invoke_dyn)
149
150 # Splits given arguments into options and a package list
151 macro(_pkgconfig_parse_options _result _is_req)
152 set(${_is_req} 0)
153
154 foreach(_pkg ${ARGN})
155 if (_pkg STREQUAL "REQUIRED")
156 set(${_is_req} 1)
157 endif (_pkg STREQUAL "REQUIRED")
158 endforeach(_pkg ${ARGN})
159
160 set(${_result} ${ARGN})
161 list(REMOVE_ITEM ${_result} "REQUIRED")
162 endmacro(_pkgconfig_parse_options)
163
164 ###
165 macro(_pkg_check_modules_internal _is_required _is_silent _prefix)
166 _pkgconfig_unset(${_prefix}_FOUND)
167 _pkgconfig_unset(${_prefix}_VERSION)
168 _pkgconfig_unset(${_prefix}_PREFIX)
169 _pkgconfig_unset(${_prefix}_INCLUDEDIR)
170 _pkgconfig_unset(${_prefix}_LIBDIR)
171 _pkgconfig_unset(${_prefix}_LIBS)
172 _pkgconfig_unset(${_prefix}_LIBS_L)
173 _pkgconfig_unset(${_prefix}_LIBS_PATHS)
174 _pkgconfig_unset(${_prefix}_LIBS_OTHER)
175 _pkgconfig_unset(${_prefix}_CFLAGS)
176 _pkgconfig_unset(${_prefix}_CFLAGS_I)
177 _pkgconfig_unset(${_prefix}_CFLAGS_OTHER)
178 _pkgconfig_unset(${_prefix}_STATIC_LIBDIR)
179 _pkgconfig_unset(${_prefix}_STATIC_LIBS)
180 _pkgconfig_unset(${_prefix}_STATIC_LIBS_L)
181 _pkgconfig_unset(${_prefix}_STATIC_LIBS_PATHS)
182 _pkgconfig_unset(${_prefix}_STATIC_LIBS_OTHER)
183 _pkgconfig_unset(${_prefix}_STATIC_CFLAGS)
184 _pkgconfig_unset(${_prefix}_STATIC_CFLAGS_I)
185 _pkgconfig_unset(${_prefix}_STATIC_CFLAGS_OTHER)
186
187 # create a better addressable variable of the modules and calculate its size
188 set(_pkg_check_modules_list ${ARGN})
189 list(LENGTH _pkg_check_modules_list _pkg_check_modules_cnt)
190
191 if(PKG_CONFIG_EXECUTABLE)
192 # give out status message telling checked module
193 if (NOT ${_is_silent})
194 if (_pkg_check_modules_cnt EQUAL 1)
195 message(STATUS "checking for module '${_pkg_check_modules_list}'")
196 else(_pkg_check_modules_cnt EQUAL 1)
197 message(STATUS "checking for modules '${_pkg_check_modules_list}'")
198 endif(_pkg_check_modules_cnt EQUAL 1)
199 endif(NOT ${_is_silent})
200
201 set(_pkg_check_modules_packages)
202 set(_pkg_check_modules_failed)
203
204 # iterate through module list and check whether they exist and match the required version
205 foreach (_pkg_check_modules_pkg ${_pkg_check_modules_list})
206 set(_pkg_check_modules_exist_query)
207
208 # check whether version is given
209 if (_pkg_check_modules_pkg MATCHES ".*(>=|=|<=).*")
210 string(REGEX REPLACE "(.*[^><])(>=|=|<=)(.*)" "\\1" _pkg_check_modules_pkg_name "${_pkg_check_modules_pkg}")
211 string(REGEX REPLACE "(.*[^><])(>=|=|<=)(.*)" "\\2" _pkg_check_modules_pkg_op "${_pkg_check_modules_pkg}")
212 string(REGEX REPLACE "(.*[^><])(>=|=|<=)(.*)" "\\3" _pkg_check_modules_pkg_ver "${_pkg_check_modules_pkg}")
213 else(_pkg_check_modules_pkg MATCHES ".*(>=|=|<=).*")
214 set(_pkg_check_modules_pkg_name "${_pkg_check_modules_pkg}")
215 set(_pkg_check_modules_pkg_op)
216 set(_pkg_check_modules_pkg_ver)
217 endif(_pkg_check_modules_pkg MATCHES ".*(>=|=|<=).*")
218
219 # handle the operands
220 if (_pkg_check_modules_pkg_op STREQUAL ">=")
221 list(APPEND _pkg_check_modules_exist_query --atleast-version)
222 endif(_pkg_check_modules_pkg_op STREQUAL ">=")
223
224 if (_pkg_check_modules_pkg_op STREQUAL "=")
225 list(APPEND _pkg_check_modules_exist_query --exact-version)
226 endif(_pkg_check_modules_pkg_op STREQUAL "=")
227
228 if (_pkg_check_modules_pkg_op STREQUAL "<=")
229 list(APPEND _pkg_check_modules_exist_query --max-version)
230 endif(_pkg_check_modules_pkg_op STREQUAL "<=")
231
232 # create the final query which is of the format:
233 # * --atleast-version <version> <pkg-name>
234 # * --exact-version <version> <pkg-name>
235 # * --max-version <version> <pkg-name>
236 # * --exists <pkg-name>
237 if (_pkg_check_modules_pkg_op)
238 list(APPEND _pkg_check_modules_exist_query "${_pkg_check_modules_pkg_ver}")
239 else(_pkg_check_modules_pkg_op)
240 list(APPEND _pkg_check_modules_exist_query --exists)
241 endif(_pkg_check_modules_pkg_op)
242
243 _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_VERSION)
244 _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_PREFIX)
245 _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_INCLUDEDIR)
246 _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_LIBDIR)
247
248 list(APPEND _pkg_check_modules_exist_query "${_pkg_check_modules_pkg_name}")
249 list(APPEND _pkg_check_modules_packages "${_pkg_check_modules_pkg_name}")
250
251 # execute the query
252 execute_process(
253 COMMAND ${PKG_CONFIG_EXECUTABLE} ${_pkg_check_modules_exist_query}
254 RESULT_VARIABLE _pkgconfig_retval)
255
256 # evaluate result and tell failures
257 if (_pkgconfig_retval)
258 if(NOT ${_is_silent})
259 message(STATUS " package '${_pkg_check_modules_pkg}' not found")
260 endif(NOT ${_is_silent})
261
262 set(_pkg_check_modules_failed 1)
263 endif(_pkgconfig_retval)
264 endforeach(_pkg_check_modules_pkg)
265
266 if(_pkg_check_modules_failed)
267 # fail when requested
268 if (${_is_required})
269 message(SEND_ERROR "A required package was not found")
270 endif (${_is_required})
271 else(_pkg_check_modules_failed)
272 # when we are here, we checked whether requested modules
273 # exist. Now, go through them and set variables
274
275 _pkgconfig_set(${_prefix}_FOUND 1)
276 list(LENGTH _pkg_check_modules_packages pkg_count)
277
278 # iterate through all modules again and set individual variables
279 foreach (_pkg_check_modules_pkg ${_pkg_check_modules_packages})
280 # handle case when there is only one package required
281 if (pkg_count EQUAL 1)
282 set(_pkg_check_prefix "${_prefix}")
283 else(pkg_count EQUAL 1)
284 set(_pkg_check_prefix "${_prefix}_${_pkg_check_modules_pkg}")
285 endif(pkg_count EQUAL 1)
286
287 _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" VERSION "" --modversion )
288 _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" PREFIX "" --variable=prefix )
289 _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" INCLUDEDIR "" --variable=includedir )
290 _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" LIBDIR "" --variable=libdir )
291
292 message(STATUS " found ${_pkg_check_modules_pkg}, version ${_pkgconfig_VERSION}")
293 endforeach(_pkg_check_modules_pkg)
294
295 # set variables which are combined for multiple modules
296 _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LIBRARIES "(^| )-l" --libs-only-l )
297 _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LIBRARY_DIRS "(^| )-L" --libs-only-L )
298 _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS "" --libs )
299 _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS_OTHER "" --libs-only-other )
300
301 _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" INCLUDE_DIRS "(^| )-I" --cflags-only-I )
302 _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS "" --cflags )
303 _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS_OTHER "" --cflags-only-other )
304 endif(_pkg_check_modules_failed)
305 else(PKG_CONFIG_EXECUTABLE)
306 if (${_is_required})
307 message(SEND_ERROR "pkg-config tool not found")
308 endif (${_is_required})
309 endif(PKG_CONFIG_EXECUTABLE)
310 endmacro(_pkg_check_modules_internal)
311
312 ###
313 ### User visible macros start here
314 ###
315
316 ###
317 macro(pkg_check_modules _prefix _module0)
318 # check cached value
319 if (NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION})
320 _pkgconfig_parse_options (_pkg_modules _pkg_is_required "${_module0}" ${ARGN})
321 _pkg_check_modules_internal("${_pkg_is_required}" 0 "${_prefix}" ${_pkg_modules})
322
323 _pkgconfig_set(__pkg_config_checked_${_prefix} ${PKG_CONFIG_VERSION})
324 endif(NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION})
325 endmacro(pkg_check_modules)
326
327 ###
328 macro(pkg_search_module _prefix _module0)
329 # check cached value
330 if (NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION} OR NOT ${_prefix}_FOUND)
331 set(_pkg_modules_found 0)
332 _pkgconfig_parse_options(_pkg_modules_alt _pkg_is_required "${_module0}" ${ARGN})
333
334 message(STATUS "checking for one of the modules '${_pkg_modules_alt}'")
335
336 # iterate through all modules and stop at the first working one.
337 foreach(_pkg_alt ${_pkg_modules_alt})
338 if(NOT _pkg_modules_found)
339 _pkg_check_modules_internal(0 1 "${_prefix}" "${_pkg_alt}")
340 endif(NOT _pkg_modules_found)
341
342 if (${_prefix}_FOUND)
343 set(_pkg_modules_found 1)
344 endif(${_prefix}_FOUND)
345 endforeach(_pkg_alt)
346
347 if (NOT ${_prefix}_FOUND)
348 if(${_pkg_is_required})
349 message(SEND_ERROR "None of the required '${_pkg_modules_alt}' found")
350 endif(${_pkg_is_required})
351 endif(NOT ${_prefix}_FOUND)
352
353 _pkgconfig_set(__pkg_config_checked_${_prefix} ${PKG_CONFIG_VERSION})
354 endif(NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION} OR NOT ${_prefix}_FOUND)
355 endmacro(pkg_search_module)
356
357 ### Local Variables:
358 ### mode: cmake
359 ### End:
0 # - ProcessorCount(var)
1 # Determine the number of processors/cores and save value in ${var}
2 #
3 # Sets the variable named ${var} to the number of physical cores available on
4 # the machine if the information can be determined. Otherwise it is set to 0.
5 # Currently this functionality is only implemented for Windows, Mac OS X and
6 # Unix systems providing getconf or the /proc/cpuinfo interface (e.g. Linux).
7
8 # A more reliable way might be to compile a small C program that uses the CPUID
9 # instruction, but that again requires compiler support or compiling assembler
10 # code.
11
12 #=============================================================================
13 # Copyright 2010 Kitware, Inc.
14 #
15 # Distributed under the OSI-approved BSD License (the "License");
16 # see accompanying file Copyright.txt for details.
17 #
18 # This software is distributed WITHOUT ANY WARRANTY; without even the
19 # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 # See the License for more information.
21 #=============================================================================
22 # (To distribute this file outside of CMake, substitute the full
23 # License text for the above reference.)
24
25 function(ProcessorCount var)
26 # Unknown:
27 set(count 0)
28
29 if(WIN32)
30 # Windows:
31 set(count "$ENV{NUMBER_OF_PROCESSORS}")
32 #message("ProcessorCount: using environment variable")
33 elseif(APPLE)
34 # Mac:
35 find_program(ProcessorCount_cmd_sysctl sysctl
36 PATHS /usr/sbin)
37 if(ProcessorCount_cmd_sysctl)
38 execute_process(COMMAND ${ProcessorCount_cmd_sysctl} -n hw.ncpu
39 OUTPUT_STRIP_TRAILING_WHITESPACE
40 OUTPUT_VARIABLE count)
41 #message("ProcessorCount: using sysctl '${ProcessorCount_cmd_sysctl}'")
42 endif()
43 else()
44 # Linux (and other systems with getconf):
45 find_program(ProcessorCount_cmd_getconf getconf)
46 if(ProcessorCount_cmd_getconf)
47 execute_process(COMMAND ${ProcessorCount_cmd_getconf} _NPROCESSORS_ONLN
48 OUTPUT_STRIP_TRAILING_WHITESPACE
49 OUTPUT_VARIABLE count)
50 #message("ProcessorCount: using getconf '${ProcessorCount_cmd_getconf}'")
51 endif()
52 endif()
53
54 # Execute this code when there is no 'sysctl' or 'getconf' or
55 # when previously executed methods return empty output:
56 #
57 if(NOT count)
58 # Systems with /proc/cpuinfo:
59 set(cpuinfo_file /proc/cpuinfo)
60 if(EXISTS "${cpuinfo_file}")
61 file(STRINGS "${cpuinfo_file}" procs REGEX "^processor.: [0-9]+$")
62 list(LENGTH procs count)
63 #message("ProcessorCount: using cpuinfo '${cpuinfo_file}'")
64 endif()
65 endif()
66
67 set(${var} ${count} PARENT_SCOPE)
68 endfunction()
69
0 #!/usr/bin/env python
1
2 # Simple script to check whether two directories are the same. I'm doing
3 # it in this script because the following command-line invocation produces
4 # a syntax error for reasons that I don't understand:
5 #
6 # python -c 'import os; if os.path.realpath("/u/gerkey/code/ros/ros/core/rosconsole") != os.path.realpath("/u/gerkey/code/ros/ros/core/rosconsole"): raise Exception'
7
8 import sys, os
9
10 if __name__ == '__main__':
11 if len(sys.argv) != 3:
12 raise Exception
13 if os.path.realpath(sys.argv[1]) != os.path.realpath(sys.argv[2]):
14 raise Exception
0 #!/usr/bin/env python
1
2 NAME="download_checkmd5.py"
3
4 import urllib, hashlib, os, sys
5 from optparse import OptionParser
6
7 def main():
8 parser = OptionParser(usage="usage: %prog URI dest [md5sum]", prog=NAME)
9 options, args = parser.parse_args()
10 md5sum = None
11 if len(args) == 2:
12 uri, dest = args
13 elif len(args) == 3:
14 uri, dest, md5sum = args
15 else:
16 parser.error("wrong number of arguments")
17
18 # Create intermediate directories as necessary, #2970
19 d = os.path.dirname(dest)
20 if len(d) and not os.path.exists(d):
21 os.makedirs(d)
22
23 fresh = False
24 if not os.path.exists(dest):
25 sys.stdout.write('[rosbuild] Downloading %s to %s...'%(uri, dest))
26 sys.stdout.flush()
27 urllib.urlretrieve(uri, dest)
28 sys.stdout.write('Done\n')
29 fresh = True
30
31 if md5sum:
32 m = hashlib.md5(open(dest).read())
33 d = m.hexdigest()
34
35 print '[rosbuild] Checking md5sum on %s'%(dest)
36
37 if d != md5sum:
38 if not fresh:
39 print '[rosbuild] WARNING: md5sum mismatch (%s != %s); re-downloading file %s'%(d, md5sum, dest)
40 os.remove(dest)
41
42 # Try one more time
43 urllib.urlretrieve(uri, dest)
44 m = hashlib.md5(open(dest).read())
45 d = m.hexdigest()
46
47 if d != md5sum:
48 print '[rosbuild] ERROR: md5sum mismatch (%s != %s) on %s; aborting'%(d, md5sum, dest)
49 return 1
50
51 return 0
52
53
54 if __name__ == '__main__':
55 sys.exit(main())
0 #!/bin/sh
1
2 # A helper script to invoke gcov from CMake, which seems to have problems
3 # with pipes inside an add_custom_target()
4
5 src=$1
6 p=$2
7 for f in `find $p -name \`basename $src\`.o`; do
8 gcov -b -o $f $src 1>/dev/null 2>&1
9 done
10 true
0 #!/usr/bin/env python
1
2 import sys
3 import fileinput
4 import re
5 import os.path
6
7 USAGE = "USAGE: rosgcov_summarize <package_dir> <rosgcov_file>"
8
9 if len(sys.argv) != 3:
10 print USAGE
11 sys.exit(-1)
12
13 pkg = sys.argv[1]
14 fname = sys.argv[2]
15
16 if not os.path.exists(fname):
17 print '[rosgcov] %s : %.2f%% (no coverage results)' % (os.path.split(pkg)[1],0.0)
18 sys.exit(0)
19
20 re_hit = re.compile('^ *[0-9]*:.*')
21 re_miss = re.compile('^ *#####:.*')
22
23 re_branch_hit = re.compile('^branch *[0-9] *taken [0-9]*.*')
24 re_branch_miss = re.compile('^branch *[0-9] *never executed.*')
25
26
27 files = []
28 finput = fileinput.input(fname)
29 for l in finput:
30 ls = l.strip().split(' ')
31 f = os.path.join(ls[0],os.path.split(ls[1])[1])
32 files.append(f.strip())
33
34 total = 0
35 hits = 0
36 misses = 0
37 branch_total = 0
38 branch_hits = 0
39 branch_misses = 0
40 print '-------------------------------------------------------'
41 print 'Coverage summary: '
42 print '-------------------------------------------------------'
43 for f in files:
44 prefix = os.path.commonprefix([pkg, f])
45 display_name = f[len(prefix):]
46 if display_name[0] == '/':
47 display_name = display_name[1:]
48 print ' ' + display_name + ': '
49 gcov_fname = f + '.gcov'
50 if not os.path.exists(gcov_fname):
51 print 'WARNING: no coverage results for %s' % (display_name)
52 continue
53 gcovf = fileinput.input(gcov_fname)
54 local_total = 0
55 local_hits = 0
56 local_misses = 0
57 local_branch_total = 0
58 local_branch_hits = 0
59 local_branch_misses = 0
60 for s in gcovf:
61 if re_hit.match(s):
62 local_hits += 1
63 local_total += 1
64 elif re_miss.match(s):
65 local_misses += 1
66 local_total += 1
67 if re_branch_hit.match(s):
68 local_branch_hits += 1
69 local_branch_total += 1
70 elif re_branch_miss.match(s):
71 local_branch_misses += 1
72 local_branch_total += 1
73
74 print ' line: %.2f%% (%d / %d)' % ((100.0 * local_hits / max(local_total,1)), local_hits, local_total)
75 hits += local_hits
76 misses += local_misses
77 total += local_total
78
79 print ' branch: %.2f%% (%d / %d)' % ((100.0 * local_branch_hits / max(local_branch_total,1)), local_branch_hits, local_branch_total)
80 branch_hits += local_branch_hits
81 branch_misses += local_branch_misses
82 branch_total += local_branch_total
83
84 print '-------------------------------------------------------'
85 print '[rosgcov] %s : %.2f%% (%d / %d)' % (os.path.split(pkg)[1],(100.0 * hits / max(total,1)), hits, total)
86 print '[rosgcov] %s : branch %.2f%% (%d / %d)' % (os.path.split(pkg)[1],(100.0 * branch_hits / max(branch_total,1)), branch_hits, branch_total)
87 print '-------------------------------------------------------'
0 <package>
1 <description brief="ROS Build System">
2
3 rosbuild contains scripts for managing the CMake-based build system for ROS.
4
5 </description>
6 <author>Brian Gerkey, Troy Straszheim, Morgan Quigley</author>
7 <license>BSD</license>
8 <review status="Doc reviewed" notes="2010/01/20"/>
9 <url>http://ros.org/wiki/rosbuild</url>
10 <rosdep name="bzip2"/>
11 </package>
0 <package>
1 <name>rosbuild</name>
2 <version>1.9.11</version>
3 <description>
4
5 rosbuild contains scripts for managing the CMake-based build system for ROS.
6
7 </description>
8 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
9 <license>BSD</license>
10
11 <url>http://ros.org/wiki/rosbuild</url>
12 <author>Brian Gerkey</author>
13 <author>Troy Straszheim</author>
14 <author>Morgan Quigley</author>
15
16 <build_depend>catkin</build_depend>
17 </package>
0 ###############################################################################
1 # Internal macros below
2
3 macro(_rosbuild_warn)
4 message("[rosbuild] WARNING: " ${ARGV})
5 endmacro(_rosbuild_warn)
6
7 macro(_rosbuild_warn_deprecate_rospack_prefix name)
8 string(REPLACE rospack rosbuild new_name ${name})
9 message("[rosbuild] WARNING: ${name} is deprecated; please use ${new_name} instead")
10 endmacro(_rosbuild_warn_deprecate_rospack_prefix)
11
12 macro(_rosbuild_warn_deprecate_no_prefix name)
13 message("[rosbuild] WARNING: ${name} is deprecated; please use rosbuild_${name} instead")
14 endmacro(_rosbuild_warn_deprecate_no_prefix)
15
16 # look up python interpreter, store in ${PYTHON_EXECUTABLE}
17 find_package(PythonInterp)
18
19 ###############################################################################
20 # Macro to turn a list into a string (why doesn't CMake have this
21 # built-in?)
22 macro(_rosbuild_list_to_string _string _list)
23 set(${_string})
24 foreach(_item ${_list})
25 string(LENGTH "${${_string}}" _len)
26 if(${_len} GREATER 0)
27 set(${_string} "${${_string}} ${_item}")
28 else(${_len} GREATER 0)
29 set(${_string} "${_item}")
30 endif(${_len} GREATER 0)
31 endforeach(_item)
32 endmacro(_rosbuild_list_to_string)
33
34 ###############################################################################
35 # Macro to dequote a string, in order to properly construct a command line.
36 # There must be an easier way to do this.
37 macro(_rosbuild_dequote_string _out _in)
38 set(${_out})
39 string(REGEX REPLACE " " ";" tmp "${_in}")
40 foreach(_item ${tmp})
41 string(LENGTH "${${_out}}" _len)
42 if(${_len} GREATER 0)
43 set(${_out} ${${_out}} ${_item})
44 else(${_len} GREATER 0)
45 set(${_out} ${_item})
46 endif(${_len} GREATER 0)
47 endforeach(_item)
48 endmacro(_rosbuild_dequote_string)
49
50
51 # list(FIND) was introduced after cmake 2.4.6, so we write our own
52 macro(_rosbuild_list_find _list _item _idx)
53 set(${_idx} -1)
54 list(LENGTH ${_list} _len)
55 math(EXPR _total "${_len} - 1")
56 foreach(_i RANGE ${_total})
57 list(GET ${_list} ${_i} _it)
58 if(_it STREQUAL ${_item})
59 set(${_idx} ${_i})
60 endif(_it STREQUAL ${_item})
61 endforeach(_i)
62 endmacro(_rosbuild_list_find)
63
64 # Check validity of manifest.xml, to avoid esoteric build errors
65 macro(_rosbuild_check_manifest)
66 execute_process(
67 COMMAND ${PYTHON_EXECUTABLE} -c "import roslib.manifest; roslib.manifest.parse_file('manifest.xml')"
68 OUTPUT_VARIABLE _manifest_error
69 ERROR_VARIABLE _manifest_error
70 RESULT_VARIABLE _manifest_failed
71 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
72 OUTPUT_STRIP_TRAILING_WHITESPACE
73 )
74 if(_manifest_failed)
75 message("[rosbuild] Error from syntax check of ${PROJECT_NAME}/manifest.xml")
76 message("${_manifest_error}")
77 message(FATAL_ERROR "[rosbuild] Syntax check of ${PROJECT_NAME}/manifest.xml failed; aborting")
78 endif(_manifest_failed)
79
80
81 endmacro(_rosbuild_check_manifest)
82
83 # Check that the directory where we're building is also where rospack
84 # thinks that the package lives, to avoid esoteric build errors.
85 macro(_rosbuild_check_package_location)
86 # Ask rospack where our package is
87 rosbuild_find_ros_package(${PROJECT_NAME})
88 # Compare to where we are
89 execute_process(
90 COMMAND $ENV{ROS_ROOT}/core/rosbuild/bin/check_same_directories.py ${${PROJECT_NAME}_PACKAGE_PATH} ${PROJECT_SOURCE_DIR}
91 OUTPUT_VARIABLE _rosbuild_check_package_location_error
92 ERROR_VARIABLE _rosbuild_check_package_location_error
93 RESULT_VARIABLE _rosbuild_check_package_location_failed
94 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
95 OUTPUT_STRIP_TRAILING_WHITESPACE
96 )
97 if(_rosbuild_check_package_location_failed)
98 message(FATAL_ERROR "[rosbuild] rospack found package \"${PROJECT_NAME}\" at \"${${PROJECT_NAME}_PACKAGE_PATH}\", but the current directory is \"${PROJECT_SOURCE_DIR}\". You should double-check your ROS_PACKAGE_PATH to ensure that packages are found in the correct precedence order.")
99 endif(_rosbuild_check_package_location_failed)
100 endmacro(_rosbuild_check_package_location)
101
102 # helper function to register check that results were generated (#580)
103 macro(_rosbuild_check_rostest_xml_result test_name test_file)
104 add_custom_target(${test_name}_result
105 COMMAND ${rosunit_path}/bin/check_test_ran.py ${test_file}
106 VERBATIM)
107 # Redeclaration of target is to workaround bug in 2.4.6
108 if(CMAKE_MINOR_VERSION LESS 6)
109 add_custom_target(test-results-run)
110 endif(CMAKE_MINOR_VERSION LESS 6)
111 add_dependencies(test-results-run ${test_name}_result)
112 endmacro(_rosbuild_check_rostest_xml_result test_name)
113
114 macro(_rosbuild_add_gtest exe)
115 # Look for optional TIMEOUT argument, #2645
116 parse_arguments(_gtest "TIMEOUT" "" ${ARGN})
117 if(NOT _gtest_TIMEOUT)
118 set(_gtest_TIMEOUT 60.0)
119 endif(NOT _gtest_TIMEOUT)
120
121 # Create the program, with basic + gtest build flags
122 rosbuild_add_executable(${exe} EXCLUDE_FROM_ALL ${_gtest_DEFAULT_ARGS})
123 rosbuild_add_gtest_build_flags(${exe})
124
125 # Create a legal target name, in case the target name has slashes in it
126 string(REPLACE "/" "_" _testname ${exe})
127
128
129 # Create target for this test
130 # We use rosunit to call the executable to get process control, #1629, #3112
131 # But don't depend on the gtest executable if rosbuild_test_nobuild is set, #3008
132 if(NOT rosbuild_test_nobuild)
133 add_custom_target(test_${_testname}
134 COMMAND ${rosunit_path}/bin/rosunit --name=${_testname} --time-limit=${_gtest_TIMEOUT} ${EXECUTABLE_OUTPUT_PATH}/${exe}
135 DEPENDS ${EXECUTABLE_OUTPUT_PATH}/${exe}
136 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
137 VERBATIM)
138 else(NOT rosbuild_test_nobuild)
139 add_custom_target(test_${_testname}
140 COMMAND ${rosunit_path}/bin/rosunit --name=${_testname} --time-limit=${_gtest_TIMEOUT} ${EXECUTABLE_OUTPUT_PATH}/${exe}
141 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
142 VERBATIM)
143 endif(NOT rosbuild_test_nobuild)
144 # Don't register to check xml output here, because we may have gotten
145 # here through registration of a future test. Eventually, we should pass
146 # in the overriding target (e.g., test-results vs. test-future-results).
147 # For now, we call _rosbuild_check_rostest_xml_result() in rosbuild_add_gtest() instead.
148 #_rosbuild_check_rostest_xml_result(test_${_testname} ${rosbuild_test_results_dir}/${PROJECT_NAME}/${_testname}.xml)
149
150 # Make sure that any messages get generated prior to building this target
151 add_dependencies(${exe} rospack_genmsg)
152 add_dependencies(${exe} rospack_gensrv)
153
154 # Make sure all test programs are built before running this test
155 # but not if rosbuild_test_nobuild is set, #3008
156 if(NOT rosbuild_test_nobuild)
157 add_dependencies(test_${_testname} tests)
158 endif(NOT rosbuild_test_nobuild)
159
160 endmacro(_rosbuild_add_gtest)
161
162 # helper function to register check that results were generated (#580)
163 # this one specifically targets rostest. rostest requires different
164 # arguments as cmake doesn't know the name of the output file
165 macro(_rosbuild_check_rostest_result test_name test_pkg test_file)
166 add_custom_target(${test_name}_result
167 COMMAND ${rosunit_path}/bin/check_test_ran.py --rostest ${test_pkg} ${test_file}
168 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
169 VERBATIM)
170 # Redeclaration of target is to workaround bug in 2.4.6
171 if(CMAKE_MINOR_VERSION LESS 6)
172 add_custom_target(test-results-run)
173 endif(CMAKE_MINOR_VERSION LESS 6)
174 add_dependencies(test-results-run ${test_name}_result)
175 endmacro(_rosbuild_check_rostest_result test_name)
176
177 macro(_rosbuild_add_rostest file)
178
179 # Check that the file exists, #1621
180 set(_file_name _file_name-NOTFOUND)
181 find_file(_file_name ${file} ${PROJECT_SOURCE_DIR} /)
182 if(NOT _file_name)
183 message(FATAL_ERROR "Can't find rostest file \"${file}\"")
184 endif(NOT _file_name)
185
186 # Create a legal target name, in case the target name has slashes in it
187 string(REPLACE "/" "_" _testname ${file})
188
189 # Create target for this test
190 add_custom_target(rostest_${_testname}
191 COMMAND ${ARGN} rostest ${file}
192 DEPENDS ${file}
193 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
194 VERBATIM)
195
196 # Make sure all test programs are built before running this test
197 # but not if rosbuild_test_nobuild is set, #3008
198 if(NOT rosbuild_test_nobuild)
199 add_dependencies(rostest_${_testname} tests)
200 endif(NOT rosbuild_test_nobuild)
201
202 # rostest-check-results will do the magic of fixing an incorrect file extension
203 # Don't register to check rostest output here, because we may have gotten
204 # here through registration of a future test. Eventually, we should pass
205 # in the overriding target (e.g., test-results vs. test-future-results).
206 # For now, we call _rosbuild_check_rostest_xml_result() in
207 # rosbuild_add_rostest()
208 # and rosbuild_add_rostest_future() instead.
209 #_rosbuild_check_rostest_result(rostest_${_testname} ${PROJECT_NAME} ${file})
210 endmacro(_rosbuild_add_rostest)
211
212 macro(_rosbuild_add_pyunit file)
213 # Look for optional TIMEOUT argument, #2645
214 parse_arguments(_pyunit "TIMEOUT" "" ${ARGN})
215 if(NOT _pyunit_TIMEOUT)
216 set(_pyunit_TIMEOUT 60.0)
217 endif(NOT _pyunit_TIMEOUT)
218
219 # Check that the file exists, #1621
220 set(_file_name _file_name-NOTFOUND)
221 find_file(_file_name ${file} ${PROJECT_SOURCE_DIR} /)
222 if(NOT _file_name)
223 message(FATAL_ERROR "Can't find pyunit file \"${file}\"")
224 endif(NOT _file_name)
225
226 # Create a legal target name, in case the target name has slashes in it
227 string(REPLACE "/" "_" _testname ${file})
228
229 # We look for ROS_TEST_COVERAGE=1
230 # to indicate that coverage reports are being requested.
231 if("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
232 set(_covarg "--cov")
233 else("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
234 set(_covarg)
235 endif("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
236
237 # Create target for this test
238 # We use rostest to call the executable to get process control, #1629
239 add_custom_target(pyunit_${_testname}
240 COMMAND ${rosunit_path}/bin/rosunit --name=${_testname} --time-limit=${_pyunit_TIMEOUT} -- ${file} ${_covarg}
241 DEPENDS ${file}
242 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
243 VERBATIM)
244
245 # Make sure all test programs are built before running this test
246 # but not if rosbuild_test_nobuild is set, #3008
247 if(NOT rosbuild_test_nobuild)
248 add_dependencies(pyunit_${_testname} tests)
249 endif(NOT rosbuild_test_nobuild)
250
251 endmacro(_rosbuild_add_pyunit)
252
253 # Actual signature:
254 # _rosbuild_add_roslaunch_check targetname file var=val var=val...
255 macro(_rosbuild_add_roslaunch_check targetname file)
256 # Check that the file exists, #1621
257 set(_file_name _file_name-NOTFOUND)
258 find_file(_file_name ${file} ${CMAKE_CURRENT_SOURCE_DIR} /)
259 if(NOT _file_name)
260 message(FATAL_ERROR "Can't find roslaunch file or directory \"${file}\"")
261 endif(NOT _file_name)
262
263 # Find rostest
264 rosbuild_invoke_rospack("" rostest path find rostest)
265
266 # Create target for this test.
267 # First check version on ros_comm to see whether we can give
268 # roslaunch-check.py an output file name (first appears in 1.6.3/1.7.0)
269 # Related to #3674.
270 rosbuild_get_stack_version(ros_comm_version "ros_comm")
271 if(ros_comm_version VERSION_LESS "1.6.3")
272 add_custom_target(${targetname}
273 COMMAND ${rostest_path}/scripts/roslaunch-check.py ${file} ${ARGN}
274 DEPENDS ${file}
275 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
276 VERBATIM)
277 else()
278 # It's the newer roslaunch-check.py, so we can pass the full path to
279 # the test result file.
280 add_custom_target(${targetname}
281 COMMAND ${rostest_path}/scripts/roslaunch-check.py -o ${rosbuild_test_results_dir}/${PROJECT_NAME}/TEST-${targetname}.xml ${file} ${ARGN}
282 DEPENDS ${file}
283 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
284 VERBATIM)
285 endif()
286
287 # Make sure all test programs are built before running this test
288 # but not if rosbuild_test_nobuild is set, #3008
289 if(NOT rosbuild_test_nobuild)
290 add_dependencies(${targetname} tests)
291 endif(NOT rosbuild_test_nobuild)
292
293 endmacro(_rosbuild_add_roslaunch_check)
294
295 macro(_rosbuild_wget_and_build tarball tarball_url tarball_dir unpack_cmd configure_cmd make_cmd install_cmd)
296 find_package(Wget REQUIRED)
297
298 _rosbuild_dequote_string(_unpack_cmd ${unpack_cmd})
299 _rosbuild_dequote_string(_configure_cmd ${configure_cmd})
300 _rosbuild_dequote_string(_make_cmd ${make_cmd})
301 _rosbuild_dequote_string(_install_cmd ${install_cmd})
302
303 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/${tarball}
304 COMMAND ${WGET_EXECUTABLE} ${tarball_url} -O ${tarball}
305 VERBATIM)
306
307 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/${tarball_dir}
308 COMMAND ${_unpack_cmd} ${tarball}
309 COMMAND touch ${tarball_dir}
310 DEPENDS ${PROJECT_BINARY_DIR}/${tarball}
311 VERBATIM)
312
313 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/installed
314 COMMAND cmake -E chdir ${PROJECT_BINARY_DIR}/${tarball_dir} ${_configure_cmd}
315 COMMAND cmake -E chdir ${PROJECT_BINARY_DIR}/${tarball_dir} ${_make_cmd}
316 COMMAND cmake -E chdir ${PROJECT_BINARY_DIR}/${tarball_dir} ${_install_cmd}
317 COMMAND touch ${PROJECT_BINARY_DIR}/installed
318 DEPENDS ${PROJECT_BINARY_DIR}/${tarball_dir}
319 VERBATIM)
320
321 add_custom_target(fetch_and_build ALL
322 DEPENDS ${PROJECT_BINARY_DIR}/installed)
323 endmacro(_rosbuild_wget_and_build)
324
325 macro(_rosbuild_add_library lib libname type)
326
327 add_library(${lib} ${type} ${ARGN})
328
329 if(${type} STREQUAL STATIC)
330 # Set output name to be the same as shared lib (may not work on Windows)
331 set_target_properties(${lib} PROPERTIES OUTPUT_NAME ${libname})
332 # Also add -fPIC, because CMake leaves it out when building static
333 # libs, even though it's necessary on 64-bit machines for linking this
334 # lib against shared libs downstream. The only exception is mingw gcc
335 # which doesn't specifically need to worry about building
336 # position independant libs.
337 if(NOT MINGW)
338 rosbuild_add_compile_flags(${lib} -fPIC)
339 endif()
340 endif(${type} STREQUAL STATIC)
341
342 # Add explicit dependency of each file on our manifest.xml and those of
343 # our dependencies
344 get_target_property(_srclist ${lib} SOURCES)
345 foreach(_src ${_srclist})
346 set(_file_name _file_name-NOTFOUND)
347 find_file(_file_name ${_src} ${CMAKE_CURRENT_SOURCE_DIR} /)
348 if(NOT _file_name)
349 message("[rosbuild] Couldn't find source file ${_src}; assuming that it is in ${CMAKE_CURRENT_SOURCE_DIR} and will be generated later")
350 set(_file_name ${CMAKE_CURRENT_SOURCE_DIR}/${_src})
351 endif(NOT _file_name)
352 add_file_dependencies(${_file_name} ${ROS_MANIFEST_LIST})
353 endforeach(_src)
354
355 # Prevent deletion of existing lib of same name
356 set_target_properties(${lib} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
357 # Attach compile and link flags
358 rosbuild_add_compile_flags(${lib} ${${PROJECT_NAME}_CFLAGS_OTHER})
359 rosbuild_add_link_flags(${lib} ${${PROJECT_NAME}_LDFLAGS_OTHER})
360 # Link lib against dependent libs
361 target_link_libraries(${lib} ${${PROJECT_NAME}_LIBRARIES})
362
363 # Add ROS-wide compile and link flags (usually things like -Wall). These
364 # are set in rosconfig.cmake.
365 rosbuild_add_compile_flags(${lib} ${ROS_COMPILE_FLAGS})
366 rosbuild_add_link_flags(${lib} ${ROS_LINK_FLAGS})
367
368 # Make sure to do any prebuild work (e.g., msg/srv generation) before
369 # building this target.
370 add_dependencies(${lib} rosbuild_precompile)
371 endmacro(_rosbuild_add_library)
372
373 macro(_rosbuild_get_clock var)
374 execute_process(
375 COMMAND ${PYTHON_EXECUTABLE} -c "import time, sys; sys.stdout.write(str(time.time()));"
376 OUTPUT_VARIABLE ${var}
377 ERROR_VARIABLE _time_error
378 RESULT_VARIABLE _time_failed
379 OUTPUT_STRIP_TRAILING_WHITESPACE
380 )
381 if(_time_failed)
382 message("[rosbuild] Error from calling to Python to get system time:")
383 message("${_time_error}")
384 message(FATAL_ERROR "[rosbuild] Failed to get system time; aborting")
385 endif(_time_failed)
386 endmacro(_rosbuild_get_clock var)
387
388 macro(_rosbuild_cmakelist_to_pylist _cmakelist _pylist)
389 # Convert a CMake list into a Python list
390 set(_pyl "[")
391 foreach(_f ${_cmakelist})
392 set(_pyl "${_pyl} '${_f}',")
393 endforeach(_f)
394 set(_pyl "${_pyl}]")
395 set(${_pylist} "${_pyl}")
396 endmacro(_rosbuild_cmakelist_to_pylist _cmakelist _pylist)
397
398 macro(_rosbuild_compare_manifests var _t _c _m)
399 if("${_t}" STREQUAL "")
400 # No time was given, so it's too old
401 set(${var} 1)
402 else("${_t}" STREQUAL "")
403 _rosbuild_cmakelist_to_pylist("${_m}" _pylist)
404 _rosbuild_cmakelist_to_pylist("${_c}" _cached_pylist)
405
406 # Call Python to compare the provided time to the latest mtime on all
407 # the files
408 execute_process(
409 COMMAND ${PYTHON_EXECUTABLE} -c "import os, sys; sys.stdout.write('1' if set(${_pylist}) != set(${_cached_pylist}) or ${_t} < max(os.stat(f).st_mtime for f in ${_pylist}) else '0');"
410 OUTPUT_VARIABLE ${var}
411 ERROR_VARIABLE _mtime_error
412 RESULT_VARIABLE _mtime_failed
413 OUTPUT_STRIP_TRAILING_WHITESPACE
414 )
415 if(_mtime_failed)
416 message("[rosbuild] Error from calling to Python to get latest mtime:")
417 message("${_mtime_error}")
418 message(FATAL_ERROR "[rosbuild] Failed to get latest mtime; aborting")
419 endif(_mtime_failed)
420 endif("${_t}" STREQUAL "")
421 endmacro(_rosbuild_compare_manifests var _t)
422
423 # parse_arguments() taken from
424 # http://www.itk.org/Wiki/CMakeMacroParseArguments
425 MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
426 SET(DEFAULT_ARGS)
427 FOREACH(arg_name ${arg_names})
428 SET(${prefix}_${arg_name})
429 ENDFOREACH(arg_name)
430 FOREACH(option ${option_names})
431 SET(${prefix}_${option} FALSE)
432 ENDFOREACH(option)
433
434 SET(current_arg_name DEFAULT_ARGS)
435 SET(current_arg_list)
436 FOREACH(arg ${ARGN})
437 SET(larg_names ${arg_names})
438 LIST(FIND larg_names "${arg}" is_arg_name)
439 IF (is_arg_name GREATER -1)
440 SET(${prefix}_${current_arg_name} ${current_arg_list})
441 SET(current_arg_name ${arg})
442 SET(current_arg_list)
443 ELSE (is_arg_name GREATER -1)
444 SET(loption_names ${option_names})
445 LIST(FIND loption_names "${arg}" is_option)
446 IF (is_option GREATER -1)
447 SET(${prefix}_${arg} TRUE)
448 ELSE (is_option GREATER -1)
449 SET(current_arg_list ${current_arg_list} ${arg})
450 ENDIF (is_option GREATER -1)
451 ENDIF (is_arg_name GREATER -1)
452 ENDFOREACH(arg)
453 SET(${prefix}_${current_arg_name} ${current_arg_list})
454 ENDMACRO(PARSE_ARGUMENTS)
455
456 # Internal macros above
457 ###############################################################################
458
0 # Set a flag to indicate that rosbuild_init() has not been called, so that
1 # we can later catch out-of-order calls to macros that must be called prior
2 # to rosbuild_init(), related to #1487.
3 set(ROSBUILD_init_called 0)
4
5 # Use this package to get add_file_dependencies()
6 include(AddFileDependencies)
7 # Used to check if a function exists
8 include(CheckFunctionExists)
9
10 # look up python interpreter, store in ${PYTHON_EXECUTABLE}, #3763 BSD supprot
11 find_package(PythonInterp)
12
13 # Find a ros package.
14 macro(rosbuild_find_ros_package pkgname)
15 # catch the error output to suppress it
16 execute_process(
17 COMMAND rospack find ${pkgname}
18 ERROR_VARIABLE __rospack_err_ignore
19 OUTPUT_VARIABLE __pkg_dir
20 OUTPUT_STRIP_TRAILING_WHITESPACE)
21 # todo: catch return code and be smart about it
22 set(${pkgname}_PACKAGE_PATH ${__pkg_dir})
23 endmacro(rosbuild_find_ros_package)
24
25 # Find a ros stack.
26 macro(rosbuild_find_ros_stack stackname)
27 # catch the error output to suppress it
28 execute_process(
29 COMMAND rosstack find ${stackname}
30 ERROR_VARIABLE __rospack_err_ignore
31 OUTPUT_VARIABLE __stack_dir
32 OUTPUT_STRIP_TRAILING_WHITESPACE)
33 # todo: catch return code and be smart about it
34 set(${stackname}_STACK_PATH ${__stack_dir})
35 endmacro(rosbuild_find_ros_stack)
36
37 # Retrieve the current COMPILE_FLAGS for the given target, append the new
38 # ones, and set the result.
39 macro(rosbuild_add_compile_flags target)
40 set(args ${ARGN})
41 separate_arguments(args)
42 get_target_property(_flags ${target} COMPILE_FLAGS)
43 if(NOT _flags)
44 set(_flags ${ARGN})
45 else(NOT _flags)
46 separate_arguments(_flags)
47 list(APPEND _flags "${args}")
48 endif(NOT _flags)
49
50 _rosbuild_list_to_string(_flags_str "${_flags}")
51 set_target_properties(${target} PROPERTIES
52 COMPILE_FLAGS "${_flags_str}")
53 endmacro(rosbuild_add_compile_flags)
54
55 # Retrieve the current COMPILE_FLAGS for the given target, remove the given
56 # ones, and set the result.
57 macro(rosbuild_remove_compile_flags target)
58 set(args ${ARGN})
59 separate_arguments(args)
60 get_target_property(_flags ${target} COMPILE_FLAGS)
61 separate_arguments(_flags)
62 list(REMOVE_ITEM _flags ${args})
63
64 _rosbuild_list_to_string(_flags_str "${_flags}")
65 set_target_properties(${target} PROPERTIES
66 COMPILE_FLAGS "${_flags_str}")
67 endmacro(rosbuild_remove_compile_flags)
68
69 # Retrieve the current LINK_FLAGS for the given target, append the new
70 # ones, and set the result.
71 macro(rosbuild_add_link_flags target)
72 set(args ${ARGN})
73 separate_arguments(args)
74 get_target_property(_flags ${target} LINK_FLAGS)
75 if(NOT _flags)
76 set(_flags ${ARGN})
77 else(NOT _flags)
78 separate_arguments(_flags)
79 list(APPEND _flags "${args}")
80 endif(NOT _flags)
81
82 _rosbuild_list_to_string(_flags_str "${_flags}")
83 set_target_properties(${target} PROPERTIES
84 LINK_FLAGS "${_flags_str}")
85 endmacro(rosbuild_add_link_flags)
86
87 # Retrieve the current LINK_FLAGS for the given target, remove the given
88 # ones, and set the result.
89 macro(rosbuild_remove_link_flags target)
90 set(args ${ARGN})
91 separate_arguments(args)
92 get_target_property(_flags ${target} LINK_FLAGS)
93 separate_arguments(_flags)
94 list(REMOVE_ITEM _flags ${args})
95
96 _rosbuild_list_to_string(_flags_str "${_flags}")
97 set_target_properties(${target} PROPERTIES
98 LINK_FLAGS "${_flags_str}")
99 endmacro(rosbuild_remove_link_flags)
100
101 macro(rosbuild_invoke_rospack pkgname _prefix _varname)
102 # Check that our cached location of rospack is valid. It can be invalid
103 # if rospack has moved since last time we ran, #1154. If it's invalid,
104 # search again.
105 if(NOT EXISTS ${ROSPACK_EXE})
106 message("Cached location of rospack is invalid; searching for rospack...")
107 set(ROSPACK_EXE ROSPACK_EXE-NOTFOUND)
108 # Only look in PATH for rospack, #3831
109 find_program(ROSPACK_EXE NAMES rospack DOC "rospack executable" NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
110 if (NOT ROSPACK_EXE)
111 message(FATAL_ERROR "Couldn't find rospack. Please run 'make' in $ROS_ROOT")
112 endif(NOT ROSPACK_EXE)
113 endif(NOT EXISTS ${ROSPACK_EXE})
114 set(_rospack_invoke_result)
115 execute_process(
116 COMMAND ${ROSPACK_EXE} ${ARGN} ${pkgname}
117 OUTPUT_VARIABLE _rospack_invoke_result
118 ERROR_VARIABLE _rospack_err_ignore
119 RESULT_VARIABLE _rospack_failed
120 OUTPUT_STRIP_TRAILING_WHITESPACE
121 )
122 if (_rospack_failed)
123 #set(_rospack_${_varname} "")
124 #set(${_prefix}_${_varname} "" CACHE INTERNAL "")
125 message("Failed to invoke ${ROSPACK_EXE} ${ARGN} ${pkgname}")
126 message("${_rospack_err_ignore}")
127 message("${_rospack_invoke_result}")
128 message(FATAL_ERROR "\nFailed to invoke rospack to get compile flags for package '${pkgname}'. Look above for errors from rospack itself. Aborting. Please fix the broken dependency!\n")
129 else(_rospack_failed)
130 separate_arguments(_rospack_invoke_result)
131 set(_rospack_${_varname} ${_rospack_invoke_result})
132 # We don't cache results that contain newlines, because
133 # they make CMake's cache unhappy. This check should only affect calls
134 # to `rospack plugins`, which we don't need to cache.
135 if(_rospack_invoke_result MATCHES ".*\n.*")
136 set(${_prefix}_${_varname} "${_rospack_invoke_result}")
137 else(_rospack_invoke_result MATCHES ".*\n.*")
138 set(${_prefix}_${_varname} "${_rospack_invoke_result}" CACHE INTERNAL "")
139 endif(_rospack_invoke_result MATCHES ".*\n.*")
140 endif(_rospack_failed)
141 endmacro(rosbuild_invoke_rospack)
142
143 ###############################################################################
144 # This is the user's main entry point. A *lot* of work gets done here. It
145 # should probably be split up into multiple macros.
146 macro(rosbuild_init)
147 # Record that we've been called
148 set(ROSBUILD_init_called 1)
149
150 # Don't override the project name if the user said not to, #3119
151 if(NOT DEFINED ROSBUILD_DONT_REDEFINE_PROJECT)
152 # Infer package name from directory name.
153 get_filename_component(_project ${CMAKE_SOURCE_DIR} NAME)
154 project(${_project})
155 else(NOT DEFINED ROSBUILD_DONT_REDEFINE_PROJECT)
156 set(_project ${PROJECT_NAME})
157 endif(NOT DEFINED ROSBUILD_DONT_REDEFINE_PROJECT)
158
159 # Declare that the minimum version of OSX we support is 10.6, #3491.
160 # For background on how CMAKE_OSX_DEPLOYMENT_TARGET works, see:
161 # http://public.kitware.com/Bug/view.php?id=6195
162 if(APPLE)
163 SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.6" CACHE STRING "Deployment target for OSX" FORCE)
164 endif(APPLE)
165
166 message("[rosbuild] Building package ${_project}")
167
168 # Must call include(rosconfig) after project, because rosconfig uses
169 # PROJECT_SOURCE_DIR
170 include($ENV{ROS_ROOT}/core/rosbuild/rosconfig.cmake)
171
172 # Check that manifest.xml is valid
173 _rosbuild_check_manifest()
174
175 # Check that the package directory is correct
176 _rosbuild_check_package_location()
177
178 # Add ROS_PACKAGE_NAME define
179 add_definitions(-DROS_PACKAGE_NAME='\"${PROJECT_NAME}\"')
180
181 # ROS_BUILD_TYPE is set by rosconfig
182 # RelWithAsserts is our own type, not supported by CMake
183 if("${ROS_BUILD_TYPE}" STREQUAL "RelWithAsserts")
184 set(CMAKE_BUILD_TYPE "")
185 set(ROS_COMPILE_FLAGS "-O3 ${ROS_COMPILE_FLAGS}")
186 else("${ROS_BUILD_TYPE}" STREQUAL "RelWithAsserts")
187 set(CMAKE_BUILD_TYPE ${ROS_BUILD_TYPE})
188 endif("${ROS_BUILD_TYPE}" STREQUAL "RelWithAsserts")
189
190 # Set default output directories
191 set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
192 set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
193
194 # By default, look in the local include dir
195 include_directories(${PROJECT_SOURCE_DIR}/include)
196
197 set(_prefix ${PROJECT_NAME})
198 set(${_prefix}_INCLUDEDIR "" CACHE INTERNAL "")
199
200 # Get the full paths to the manifests for all packages on which
201 # we depend
202 rosbuild_invoke_rospack(${PROJECT_NAME} _rospack deps_manifests_invoke_result deps-manifests)
203 rosbuild_invoke_rospack(${PROJECT_NAME} _rospack msgsrv_gen_invoke_result deps-msgsrv)
204 set(ROS_MANIFEST_LIST "${PROJECT_SOURCE_DIR}/manifest.xml ${_rospack_deps_manifests_invoke_result} ${_rospack_msgsrv_gen_invoke_result}")
205 # convert whitespace-separated string to ;-separated list
206 separate_arguments(ROS_MANIFEST_LIST)
207
208 # Check the time at which we last cached flags against the latest
209 # modification time for all manifests that we depend on. If our cache
210 # time is smaller, then we need to rebuild our cached values by calling
211 # out to rospack to get flags. This is an optimization in the service of
212 # speeding up the build, #2109.
213 _rosbuild_compare_manifests(_rebuild_cache "${_rosbuild_cached_flag_time}" "${${_prefix}_cached_manifest_list}" "${ROS_MANIFEST_LIST}")
214 if(_rebuild_cache)
215 # Explicitly unset all cached variables, to avoid possible accumulation
216 # across builds, #2389.
217 set(${_prefix}_INCLUDE_DIRS "" CACHE INTERNAL "")
218 set(${_prefix}_CFLAGS_OTHER "" CACHE INTERNAL "")
219 set(${_prefix}_LIBRARY_DIRS "" CACHE INTERNAL "")
220 set(${_prefix}_LIBRARIES "" CACHE INTERNAL "")
221 set(${_prefix}_LDFLAGS_OTHER "" CACHE INTERNAL "")
222 set(${_prefix}_cached_manifest_list "" CACHE INTERNAL "")
223
224 message("[rosbuild] Cached build flags older than manifests; calling rospack to get flags")
225 # Get the include dirs
226 rosbuild_invoke_rospack(${PROJECT_NAME} ${_prefix} INCLUDE_DIRS cflags-only-I --deps-only)
227 #message("${pkgname} include dirs: ${${_prefix}_INCLUDE_DIRS}")
228 set(${_prefix}_INCLUDE_DIRS ${${_prefix}_INCLUDE_DIRS} CACHE INTERNAL "")
229
230 # Get the other cflags
231 rosbuild_invoke_rospack(${PROJECT_NAME} ${_prefix} temp cflags-only-other --deps-only)
232 _rosbuild_list_to_string(${_prefix}_CFLAGS_OTHER "${${_prefix}_temp}")
233 #message("${pkgname} other cflags: ${${_prefix}_CFLAGS_OTHER}")
234 set(${_prefix}_CFLAGS_OTHER ${${_prefix}_CFLAGS_OTHER} CACHE INTERNAL "")
235
236 # Get the lib dirs
237 rosbuild_invoke_rospack(${PROJECT_NAME} ${_prefix} LIBRARY_DIRS libs-only-L --deps-only)
238 #message("${pkgname} library dirs: ${${_prefix}_LIBRARY_DIRS}")
239 set(${_prefix}_LIBRARY_DIRS ${${_prefix}_LIBRARY_DIRS} CACHE INTERNAL "")
240
241 # Get the libs
242 rosbuild_invoke_rospack(${PROJECT_NAME} ${_prefix} LIBRARIES libs-only-l --deps-only)
243 #
244 # The following code removes duplicate libraries from the link line,
245 # saving only the last one.
246 #
247 list(REVERSE ${_prefix}_LIBRARIES)
248 list(REMOVE_DUPLICATES ${_prefix}_LIBRARIES)
249 list(REVERSE ${_prefix}_LIBRARIES)
250
251 # Also throw in the libs that we want to link everything against (only
252 # use case for this so far is -lgcov when building with code coverage
253 # support).
254 list(APPEND ${_prefix}_LIBRARIES "${ROS_LINK_LIBS}")
255 set(${_prefix}_LIBRARIES ${${_prefix}_LIBRARIES} CACHE INTERNAL "")
256
257 # Get the other lflags
258 rosbuild_invoke_rospack(${PROJECT_NAME} ${_prefix} temp libs-only-other --deps-only)
259 _rosbuild_list_to_string(${_prefix}_LDFLAGS_OTHER "${${_prefix}_temp}")
260 #message("${pkgname} other ldflags: ${${_prefix}_LDFLAGS_OTHER}")
261 set(${_prefix}_LDFLAGS_OTHER ${${_prefix}_LDFLAGS_OTHER} CACHE INTERNAL "")
262
263 # Record the time at which we cached those values
264 _rosbuild_get_clock(_time)
265 set(_rosbuild_cached_flag_time ${_time} CACHE INTERNAL "")
266 set(${_prefix}_cached_manifest_list ${ROS_MANIFEST_LIST} CACHE INTERNAL "")
267 endif(_rebuild_cache)
268
269 # Use the (possibly cached) values returned by rospack.
270 include_directories(${${_prefix}_INCLUDE_DIRS})
271 link_directories(${${_prefix}_LIBRARY_DIRS})
272
273 #
274 # Catch absolute pathnames to archive libraries and bracket them with
275 # linker args necessary to force extraction of the entire archive.
276 #
277 # The OS X linker doesn't accept the -whole-archive and -no-whole-archive
278 # arguments.
279 #
280 if(NOT APPLE)
281 foreach(_lib ${${_prefix}_LIBRARIES})
282 if(_lib MATCHES "/[^ ]*\\.a")
283 set(_bracket_str "-Wl,-whole-archive ${_lib} -Wl,-no-whole-archive")
284 list(APPEND ${_prefix}_LDFLAGS_OTHER "${_bracket_str}")
285 endif(_lib MATCHES "/[^ ]*\\.a")
286 endforeach(_lib)
287 endif(NOT APPLE)
288
289 # Set up the test targets. Subsequent calls to rosbuild_add_gtest and
290 # friends add targets and dependencies from these targets.
291 #
292
293 # Find rosunit; rosunit_path will be used later
294 rosbuild_invoke_rospack("" rosunit path find rosunit)
295
296 # Record where we're going to put test results (#2003)
297 execute_process(COMMAND ${rosunit_path}/scripts/test_results_dir.py
298 OUTPUT_VARIABLE rosbuild_test_results_dir
299 RESULT_VARIABLE _test_results_dir_failed
300 OUTPUT_STRIP_TRAILING_WHITESPACE)
301 if(_test_results_dir_failed)
302 message(FATAL_ERROR "Failed to invoke rosunit/scripts/test_results_dir.py")
303 endif(_test_results_dir_failed)
304
305 # The 'tests' target builds the test program
306 add_custom_target(tests)
307 # The 'test' target runs all but the future tests
308 add_custom_target(test)
309 # We need to build tests before running them. Addition of this
310 # dependency also ensures that old test results get cleaned prior to a
311 # new test run.
312 # but not if rosbuild_test_nobuild is set, #3008
313 if(NOT rosbuild_test_nobuild)
314 add_dependencies(test tests)
315 endif(NOT rosbuild_test_nobuild)
316
317 # Clean out previous test results before running tests. Use bash
318 # conditional to ignore failures (most often happens when a stale NFS
319 # handle lingers in the test results directory), because CMake doesn't
320 # seem to be able to do it.
321 add_custom_target(rosbuild_clean-test-results
322 if ! rm -rf ${rosbuild_test_results_dir}/${PROJECT_NAME}\; then echo "WARNING: failed to remove test-results directory"\; fi)
323 # Make the tests target depend on rosbuild_clean-test-results, which will ensure
324 # that test results are deleted before we try to build tests, and thus
325 # before we try to run tests.
326 add_dependencies(tests rosbuild_clean-test-results)
327 # The 'test-future' target runs the future tests
328 add_custom_target(test-future)
329
330
331 add_custom_target(test-results-run)
332 add_custom_target(test-results
333 COMMAND ${rosunit_path}/bin/summarize_results.py --nodeps ${_project})
334 add_dependencies(test-results test-results-run)
335 # Do we want coverage reporting (only matters for Python, because
336 # Bullseye already collects everything into a single file).
337 if("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
338 add_custom_target(test-results-coverage
339 COMMAND ${rosunit_path}/bin/pycoverage_to_html.py
340 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
341 # Make tests run before collecting coverage results
342 add_dependencies(test-results-coverage test-results-run)
343 # Make coverage collection happen
344 add_dependencies(test-results test-results-coverage)
345 endif("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
346
347 # Initialize a counter to be used to uniquify target names for
348 # calls to rosbuild_add_roslaunch_check(), #3674.
349 set(ROSBUILD_roslaunch_check_count 1)
350
351 # Find roslib; roslib_path will be used later
352 rosbuild_invoke_rospack("" roslib path find roslib)
353
354 # Create targets for client libs attach their message-generation output to
355 add_custom_target(rospack_genmsg)
356 add_custom_target(rospack_gensrv)
357
358 # Add a target that will fire before doing message or service generation.
359 # This is used by packages that do automatic generation of message and
360 # service files.
361 add_custom_target(rosbuild_premsgsrvgen)
362
363 # Add a target that will fire before compiling anything. This is used by
364 # message and service generation, as well as things outside of ros, like
365 # dynamic_reconfigure.
366 add_custom_target(rosbuild_precompile)
367
368 # The rospack_genmsg_libexe target is defined for backward compatibility,
369 # and will eventually be removed.
370 add_custom_target(rospack_genmsg_libexe)
371 add_dependencies(rosbuild_precompile rospack_genmsg_libexe)
372
373 # ${gendeps_exe} is a convenience variable that roslang cmake rules
374 # must reference as a dependency of msg/srv generation
375 # ${gendeps_exe} is set by roslib-extras.cmake
376 find_package(catkin REQUIRED COMPONENTS roslib)
377
378 # If the roslang package is available, pull in cmake/roslang.cmake from
379 # there; it will in turn include message-generation logic from client
380 # libs. This is to allow roslang to live outside the ros stack, #3108.
381 rosbuild_find_ros_package("roslang")
382 if(roslang_PACKAGE_PATH)
383 # Can't use rosbuild_include() here, because there's no guarantee that
384 # the package we're building depends on roslang (in fact, it probably
385 # doesn't.
386 include("${roslang_PACKAGE_PATH}/cmake/roslang.cmake")
387 endif(roslang_PACKAGE_PATH)
388
389 # Collect cmake fragments exported by packages that depend on
390 # rosbuild. This behavior is deprecated, in favor of using
391 # rosbuild_include() to explicitly include cmake code from other packages.
392 rosbuild_invoke_rospack(rosbuild _rosbuild EXPORTS plugins --attrib=cmake --top=${_project})
393 list(LENGTH _rosbuild_EXPORTS _rosbuild_EXPORTS_length)
394
395 # rospack plugins outputs the list as:
396 # <package name> <attribute value>
397 # Here we remove <package name> in all cases by:
398 # 1) Remove the first element of the returned list
399 # 2) Search for all instances of <newline><string><semicolon>, replacing them with just a semicolon
400
401 # 1) Remove the first package name if the list has at least one element
402 if (${_rosbuild_EXPORTS_length} GREATER 0)
403 list(REMOVE_AT _rosbuild_EXPORTS 0)
404 endif(${_rosbuild_EXPORTS_length} GREATER 0)
405
406 # 2) Remove the rest of the package names
407 string(REGEX REPLACE "\n[^;]*;" ";" _rosbuild_EXPORTS_stripped "${_rosbuild_EXPORTS}")
408
409 set(_rosbuild_EXPORTS "" CACHE INTERNAL "")
410
411 set(_cmake_fragments)
412 foreach(_f ${_rosbuild_EXPORTS_stripped})
413 list(APPEND _cmake_fragments ${_f})
414 message("\n[rosbuild] WARNING: the file ${_f} is being included automatically. This behavior is deprecated. The package containing that file should instead export the directory containing the file, and you should use rosbuild_include() to include the file explicitly.\n")
415 endforeach(_f)
416
417 # Now include them all
418 foreach(_f ${_cmake_fragments})
419 if(NOT EXISTS ${_f})
420 message(FATAL_ERROR "Cannot include non-existent exported cmake file ${_f}")
421 endif(NOT EXISTS ${_f})
422 # Include this cmake fragment; presumably it will do /
423 # provide something useful. Only include each file once (a file
424 # might be multiply referenced because of package dependencies
425 # dependencies).
426 if(NOT ${_f}_INCLUDED)
427 message("[rosbuild] Including ${_f}")
428 include(${_f})
429 set(${_f}_INCLUDED Y)
430 endif(NOT ${_f}_INCLUDED)
431 endforeach(_f)
432
433
434 #
435 # Gather the gtest build flags, for use when building unit tests. We
436 # don't require the user to declare a dependency on gtest.
437 #
438 find_program(GTEST_EXE NAMES gtest-config DOC "gtest-config executable" ONLY_CMAKE_FIND_ROOT_PATH)
439 if (NOT GTEST_EXE)
440 if (GTEST_LIBRARIES)
441 set(_gtest_LIBRARIES -l${GTEST_LIBRARIES})
442 else(GTEST_LIBRARIES)
443 set(_gtest_LIBRARIES -lgtest)
444 endif(GTEST_LIBRARIES)
445 # Couldn't find gtest-config. Hoping that gtest is in our path either in the system install or where ROS_BINDEPS points to
446 else (NOT GTEST_EXE)
447
448 execute_process(COMMAND ${GTEST_EXE} --includedir
449 OUTPUT_VARIABLE gtest_include_dir
450 RESULT_VARIABLE _gtest_include_dir
451 OUTPUT_STRIP_TRAILING_WHITESPACE)
452 if(_gtest_include_dir)
453 message(FATAL_ERROR "Failed to invoke gtest-config")
454 endif(_gtest_include_dir)
455
456 include_directories(${gtest_include_dir})
457
458 execute_process(COMMAND ${GTEST_EXE} --libdir
459 OUTPUT_VARIABLE gtest_lib_dir
460 RESULT_VARIABLE _gtest_lib_dir
461 OUTPUT_STRIP_TRAILING_WHITESPACE)
462 if(_gtest_lib_dir)
463 message(FATAL_ERROR "Failed to invoke gtest-config")
464 endif(_gtest_lib_dir)
465 link_directories(${gtest_lib_dir})
466
467
468 execute_process(COMMAND ${GTEST_EXE} --libs
469 OUTPUT_VARIABLE gtest_libs
470 RESULT_VARIABLE _gtest_libs
471 OUTPUT_STRIP_TRAILING_WHITESPACE)
472 if(_gtest_libs)
473 message(FATAL_ERROR "Failed to invoke gtest-config")
474 endif(_gtest_libs)
475
476
477 set(_gtest_LIBRARIES ${gtest_libs})
478 set(_gtest_CFLAGS_OTHER "")
479 set(_gtest_LDFLAGS_OTHER "-Wl,-rpath,${gtest_lib_dir}")
480 endif (NOT GTEST_EXE)
481
482 #
483 # The following code removes duplicate libraries from the link line,
484 # saving only the last one.
485 #
486 list(REVERSE _gtest_LIBRARIES)
487 list(REMOVE_DUPLICATES _gtest_LIBRARIES)
488 list(REVERSE _gtest_LIBRARIES)
489
490 # Delete the files that let rospack know messages/services have been generated
491 file(REMOVE ${PROJECT_SOURCE_DIR}/msg_gen/generated)
492 file(REMOVE ${PROJECT_SOURCE_DIR}/srv_gen/generated)
493 endmacro(rosbuild_init)
494 ###############################################################################
495
496 # A wrapper around add_executable(), using info from the rospack
497 # invocation to set up compiling and linking.
498 macro(rosbuild_add_executable exe)
499 add_executable(${ARGV})
500
501 # Add explicit dependency of each file on our manifest.xml and those of
502 # our dependencies.
503 get_target_property(_srclist ${exe} SOURCES)
504 foreach(_src ${_srclist})
505 set(_file_name _file_name-NOTFOUND)
506 find_file(_file_name ${_src} ${CMAKE_CURRENT_SOURCE_DIR} /)
507 if(NOT _file_name)
508 message("[rosbuild] Couldn't find source file ${_src}; assuming that it is in ${CMAKE_CURRENT_SOURCE_DIR} and will be generated later")
509 set(_file_name ${CMAKE_CURRENT_SOURCE_DIR}/${_src})
510 endif(NOT _file_name)
511 add_file_dependencies(${_file_name} ${ROS_MANIFEST_LIST})
512 endforeach(_src)
513
514 rosbuild_add_compile_flags(${exe} ${${PROJECT_NAME}_CFLAGS_OTHER})
515 rosbuild_add_link_flags(${exe} ${${PROJECT_NAME}_LDFLAGS_OTHER})
516
517 if(ROS_BUILD_STATIC_EXES AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
518 # This will probably only work on Linux. The LINK_SEARCH_END_STATIC
519 # property should be sufficient, but it doesn't appear to work
520 # properly.
521 rosbuild_add_link_flags(${exe} -static-libgcc -Wl,-Bstatic)
522 endif(ROS_BUILD_STATIC_EXES AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
523
524 target_link_libraries(${exe} ${${PROJECT_NAME}_LIBRARIES})
525
526 # Add ROS-wide compile and link flags (usually things like -Wall). These
527 # are set in rosconfig.cmake.
528 rosbuild_add_compile_flags(${exe} ${ROS_COMPILE_FLAGS})
529 rosbuild_add_link_flags(${exe} ${ROS_LINK_FLAGS})
530
531 # Make sure to do any prebuild work (e.g., msg/srv generation) before
532 # building this target.
533 add_dependencies(${exe} rosbuild_precompile)
534
535 # If we're linking boost statically, we have to force allow multiple definitions because
536 # rospack does not remove duplicates
537 if ("$ENV{ROS_BOOST_LINK}" STREQUAL "static")
538 rosbuild_add_link_flags(${exe} "-Wl,--allow-multiple-definition")
539 endif("$ENV{ROS_BOOST_LINK}" STREQUAL "static")
540
541 endmacro(rosbuild_add_executable)
542
543 # Wrapper around add_library. We can build shared static and shared libs, and
544 # set up compile and link flags for both.
545 macro(rosbuild_add_library lib)
546
547 # Sanity check; must build at least one kind of library.
548 if(NOT ROS_BUILD_STATIC_LIBS AND NOT ROS_BUILD_SHARED_LIBS)
549 message(FATAL_ERROR "Neither shared nor static libraries are enabled. Please set either ROS_BUILD_STATIC_LIBS or ROS_BUILD_SHARED_LIBS to true in your $ROS_ROOT/rosconfig.cmake")
550 endif(NOT ROS_BUILD_STATIC_LIBS AND NOT ROS_BUILD_SHARED_LIBS)
551 # Sanity check; it's too hard to support building shared libs and static
552 # executables.
553 if(ROS_BUILD_STATIC_EXES AND ROS_BUILD_SHARED_LIBS)
554 message(FATAL_ERROR "Static executables are requested, but so are shared libs. This configuration is unsupported. Please either set ROS_BUILD_SHARED_LIBS to false or set ROS_BUILD_STATIC_EXES to false.")
555 endif(ROS_BUILD_STATIC_EXES AND ROS_BUILD_SHARED_LIBS)
556
557 # What are we building?
558 if(ROS_BUILD_SHARED_LIBS)
559 # If shared libs are being built, they get the default CMake target name
560 # No matter what, the libraries get the same name in the end.
561 _rosbuild_add_library(${lib} ${lib} SHARED ${ARGN})
562 endif(ROS_BUILD_SHARED_LIBS)
563
564 if(ROS_BUILD_STATIC_LIBS)
565 # If we're only building static libs, then they get the default CMake
566 # target name.
567 if(NOT ROS_BUILD_SHARED_LIBS)
568 set(static_lib_name "${lib}")
569 else(NOT ROS_BUILD_SHARED_LIBS)
570 set(static_lib_name "${lib}-static")
571 endif(NOT ROS_BUILD_SHARED_LIBS)
572
573 _rosbuild_add_library(${static_lib_name} ${lib} STATIC ${ARGN})
574 endif(ROS_BUILD_STATIC_LIBS)
575
576 endmacro(rosbuild_add_library)
577
578 # Wrapper around add_library for the specific case of building a MODULE,
579 # which works a little differently on dyld systems (e.g., OS X)
580 macro(rosbuild_add_library_module lib)
581 _rosbuild_add_library(${lib} ${lib} MODULE ${ARGN})
582 endmacro(rosbuild_add_library_module)
583
584 # Explicitly add flags for gtest. We do this here, instead of using
585 # manifest dependencies, because there are situations in which it is
586 # undesirable to link in gtest where's it's not being used. gtest is
587 # part of the "core" build that happens during a 'make' in ros, so we can
588 # assume that's already built.
589 macro(rosbuild_add_gtest_build_flags exe)
590 if (NOT GTEST_FOUND)
591 # gtest is installed but there might not be any library (e.g. Ubuntu Precise), so build it
592 if (EXISTS "/usr/src/gtest")
593 if (NOT EXISTS "${CMAKE_BINARY_DIR}/_gtest_from_src")
594 # for now, this would only work on Ubuntu
595 add_subdirectory("/usr/src/gtest/" ${CMAKE_BINARY_DIR}/_gtest_from_src)
596 endif()
597 else()
598 message(WARNING "GTest not found; C++ tests will fail to build.")
599 endif()
600 endif()
601 rosbuild_add_compile_flags(${exe} ${_gtest_CFLAGS_OTHER})
602 if (LIBRARY_OUTPUT_PATH)
603 # add this as GTest builds there
604 set_target_properties(${exe} PROPERTIES LINK_FLAGS "-L${LIBRARY_OUTPUT_PATH}")
605 endif()
606 target_link_libraries(${exe} ${_gtest_LIBRARIES})
607 rosbuild_add_link_flags(${exe} ${_gtest_LDFLAGS_OTHER})
608 rosbuild_declare_test(${exe})
609 add_dependencies(${exe} gtest gtest_main)
610 endmacro(rosbuild_add_gtest_build_flags)
611
612 # Declare an executable to be a test harness, which excludes it from the
613 # all target, and adds a dependency to the tests target.
614 macro(rosbuild_declare_test exe)
615 # We provide a 'tests' target that just builds the tests.
616 # Redeclaration of target is to workaround bug in 2.4.6
617 if(CMAKE_MINOR_VERSION LESS 6)
618 add_custom_target(tests)
619 endif(CMAKE_MINOR_VERSION LESS 6)
620 add_dependencies(tests ${exe})
621 # Don't build this target with the all target, #3110
622 set_target_properties(${exe} PROPERTIES EXCLUDE_FROM_ALL TRUE)
623 endmacro(rosbuild_declare_test)
624
625 # A helper to create test programs. It calls rosbuild_add_executable() to
626 # create the program, and augments a test target that was created in the
627 # call rospack()
628 macro(rosbuild_add_gtest exe)
629 _rosbuild_add_gtest(${ARGV})
630 # Create a legal target name, in case the target name has slashes in it
631 string(REPLACE "/" "_" _testname ${exe})
632 # Redeclaration of test target is to workaround bug in 2.4.6
633 if(CMAKE_MINOR_VERSION LESS 6)
634 add_custom_target(test)
635 endif(CMAKE_MINOR_VERSION LESS 6)
636 add_dependencies(test test_${_testname})
637 # Register check for test output
638 _rosbuild_check_rostest_xml_result(${_testname} ${rosbuild_test_results_dir}/${PROJECT_NAME}/TEST-${_testname}.xml)
639 endmacro(rosbuild_add_gtest)
640
641 # A version of add_gtest that checks a label against ROS_BUILD_TEST_LABEL
642 macro(rosbuild_add_gtest_labeled label)
643 if("$ENV{ROS_BUILD_TEST_LABEL}" STREQUAL "" OR "${label}" STREQUAL "$ENV{ROS_BUILD_TEST_LABEL}")
644 rosbuild_add_gtest(${ARGN})
645 endif("$ENV{ROS_BUILD_TEST_LABEL}" STREQUAL "" OR "${label}" STREQUAL "$ENV{ROS_BUILD_TEST_LABEL}")
646 endmacro(rosbuild_add_gtest_labeled)
647
648 # A helper to run rostests. It generates a command to run rostest on
649 # the specified file and makes this target a dependency of test.
650 macro(rosbuild_add_rostest file)
651 string(REPLACE "/" "_" _testname ${file})
652 _rosbuild_add_rostest(${file})
653 # Redeclaration of target is to workaround bug in 2.4.6
654 if(CMAKE_MINOR_VERSION LESS 6)
655 add_custom_target(test)
656 endif(CMAKE_MINOR_VERSION LESS 6)
657 add_dependencies(test rostest_${_testname})
658 _rosbuild_check_rostest_result(rostest_${_testname} ${PROJECT_NAME} ${file})
659 endmacro(rosbuild_add_rostest)
660
661 # A version of add_rostest that checks a label against ROS_BUILD_TEST_LABEL
662 macro(rosbuild_add_rostest_labeled label)
663 if("$ENV{ROS_BUILD_TEST_LABEL}" STREQUAL "" OR "${label}" STREQUAL "$ENV{ROS_BUILD_TEST_LABEL}")
664 rosbuild_add_rostest(${ARGN})
665 endif("$ENV{ROS_BUILD_TEST_LABEL}" STREQUAL "" OR "${label}" STREQUAL "$ENV{ROS_BUILD_TEST_LABEL}")
666 endmacro(rosbuild_add_rostest_labeled)
667
668 # A helper to run Python unit tests. It generates a command to run python
669 # the specified file
670 macro(rosbuild_add_pyunit file)
671 string(REPLACE "/" "_" _testname ${file})
672 _rosbuild_add_pyunit(${ARGV})
673 # Redeclaration of target is to workaround bug in 2.4.6
674 if(CMAKE_MINOR_VERSION LESS 6)
675 add_custom_target(test)
676 endif(CMAKE_MINOR_VERSION LESS 6)
677 add_dependencies(test pyunit_${_testname})
678 # Register check for test output
679 _rosbuild_check_rostest_xml_result(${_testname} ${rosbuild_test_results_dir}/${PROJECT_NAME}/TEST-${_testname}.xml)
680 endmacro(rosbuild_add_pyunit)
681
682 # A version of add_pyunit that checks a label against ROS_BUILD_TEST_LABEL
683 macro(rosbuild_add_pyunit_labeled label)
684 if("$ENV{ROS_BUILD_TEST_LABEL}" STREQUAL "" OR "${label}" STREQUAL "$ENV{ROS_BUILD_TEST_LABEL}")
685 rosbuild_add_pyunit(${ARGN})
686 endif("$ENV{ROS_BUILD_TEST_LABEL}" STREQUAL "" OR "${label}" STREQUAL "$ENV{ROS_BUILD_TEST_LABEL}")
687 endmacro(rosbuild_add_pyunit_labeled)
688
689 # Declare as a unit test a check of a roslaunch file, or a directory
690 # containing roslaunch files. Following the file/directory, you can
691 # specify environment variables as var=val var=val ...
692 macro(rosbuild_add_roslaunch_check file)
693 string(REPLACE "/" "_" _testname ${file})
694 # Append a unique count to avoid target name collisions when the same
695 # launch file is tested in different environment variable configurations,
696 # #3674. The counter is initialized in rosbuild_init().
697 set(_targetname roslaunch_check_${_testname}_${ROSBUILD_roslaunch_check_count})
698 # Increment counter for the next call
699 math(EXPR ROSBUILD_roslaunch_check_count "${ROSBUILD_roslaunch_check_count} + 1")
700 _rosbuild_add_roslaunch_check(${_targetname} ${ARGV})
701 # Redeclaration of target is to workaround bug in 2.4.6
702 if(CMAKE_MINOR_VERSION LESS 6)
703 add_custom_target(test)
704 endif(CMAKE_MINOR_VERSION LESS 6)
705 add_dependencies(test ${_targetname})
706 endmacro(rosbuild_add_roslaunch_check)
707
708 set(_ROSBUILD_GENERATED_MSG_FILES "")
709 macro(rosbuild_add_generated_msgs)
710 if(ROSBUILD_init_called)
711 message(FATAL_ERROR "rosbuild_add_generated_msgs() cannot be called after rosbuild_init()")
712 endif(ROSBUILD_init_called)
713 list(APPEND _ROSBUILD_GENERATED_MSG_FILES ${ARGV})
714 endmacro(rosbuild_add_generated_msgs)
715
716 # Return a list of all msg/.msg files
717 macro(rosbuild_get_msgs msgvar)
718 file(GLOB _msg_files RELATIVE "${PROJECT_SOURCE_DIR}/msg" "${PROJECT_SOURCE_DIR}/msg/*.msg")
719 set(${msgvar} ${_ROSBUILD_GENERATED_MSG_FILES})
720 # Loop over each .msg file, establishing a rule to compile it
721 foreach(_msg ${_msg_files})
722 # Make sure we didn't get a bogus match (e.g., .#Foo.msg, which Emacs
723 # might create as a temporary file). the file()
724 # command doesn't take a regular expression, unfortunately.
725 if(${_msg} MATCHES "^[^\\.].*\\.msg$")
726 list(APPEND ${msgvar} ${_msg})
727 endif(${_msg} MATCHES "^[^\\.].*\\.msg$")
728 endforeach(_msg)
729 endmacro(rosbuild_get_msgs)
730
731 set(_ROSBUILD_GENERATED_SRV_FILES "")
732 macro(rosbuild_add_generated_srvs)
733 if(ROSBUILD_init_called)
734 message(FATAL_ERROR "rosbuild_add_generated_srvs() cannot be called after rosbuild_init()")
735 endif(ROSBUILD_init_called)
736 list(APPEND _ROSBUILD_GENERATED_SRV_FILES ${ARGV})
737 endmacro(rosbuild_add_generated_srvs)
738
739 # Return a list of all srv/.srv files
740 macro(rosbuild_get_srvs srvvar)
741 file(GLOB _srv_files RELATIVE "${PROJECT_SOURCE_DIR}/srv" "${PROJECT_SOURCE_DIR}/srv/*.srv")
742 set(${srvvar} ${_ROSBUILD_GENERATED_SRV_FILES})
743 # Loop over each .srv file, establishing a rule to compile it
744 foreach(_srv ${_srv_files})
745 # Make sure we didn't get a bogus match (e.g., .#Foo.srv, which Emacs
746 # might create as a temporary file). the file()
747 # command doesn't take a regular expression, unfortunately.
748 if(${_srv} MATCHES "^[^\\.].*\\.srv$")
749 list(APPEND ${srvvar} ${_srv})
750 endif(${_srv} MATCHES "^[^\\.].*\\.srv$")
751 endforeach(_srv)
752 endmacro(rosbuild_get_srvs)
753
754 # Compute msg/srv depenendency list, with simple caching
755 macro(rosbuild_gendeps _pkg _msgfile)
756 # Did we already compute it?
757 if(NOT ${_pkg}_${_msgfile}_GENDEPS_COMPUTED)
758 # Call out to the gendeps tool to get full paths to .msg files on
759 # which this one depends, for proper dependency tracking
760 execute_process(
761 COMMAND ${gendeps_exe} ${_input}
762 OUTPUT_VARIABLE __other_msgs
763 ERROR_VARIABLE __rospack_err_ignore
764 OUTPUT_STRIP_TRAILING_WHITESPACE)
765 # For some reason, the output from gendeps has escaped spaces in it.
766 # Converting to a string and then back to a list removes them.
767 _rosbuild_list_to_string(${_pkg}_${_msgfile}_GENDEPS "${__other_msgs}")
768 separate_arguments(${_pkg}_${_msgfile}_GENDEPS)
769 set(${_pkg}_${_msgfile}_GENDEPS_COMPUTED Y)
770 endif(NOT ${_pkg}_${_msgfile}_GENDEPS_COMPUTED)
771 endmacro(rosbuild_gendeps)
772
773 # gensrv processes srv/*.srv files into language-specific source files
774 macro(rosbuild_gensrv)
775 # roslang_PACKAGE_PATH was set in rosbuild_init(), if roslang was found
776 if(NOT roslang_PACKAGE_PATH)
777 _rosbuild_warn("rosbuild_gensrv() was called, but the roslang package cannot be found. Service generation will NOT occur")
778 endif(NOT roslang_PACKAGE_PATH)
779 # Check whether there are any .srv files
780 rosbuild_get_srvs(_srvlist)
781 if(NOT _srvlist)
782 _rosbuild_warn("rosbuild_gensrv() was called, but no .srv files were found")
783 else(NOT _srvlist)
784 file(WRITE ${PROJECT_SOURCE_DIR}/srv_gen/generated "yes")
785 # Now set the mtime to something consistent. We only want whether or not this file exists to matter
786 # But we set it to the current time, because setting it to zero causes
787 # annoying warning, #3396.
788 execute_process(
789 COMMAND ${PYTHON_EXECUTABLE} -c "import os; os.utime('${PROJECT_SOURCE_DIR}/srv_gen/generated', (1, 1))"
790 ERROR_VARIABLE _set_mtime_error
791 RESULT_VARIABLE _set_mtime_failed
792 OUTPUT_STRIP_TRAILING_WHITESPACE)
793 if(_set_mtime_failed)
794 message("[rosbuild] Error from calling to Python to set the mtime on ${PROJECT_SOURCE_DIR}/srv_gen/generated:")
795 message("${_mtime_error}")
796 message(FATAL_ERROR "[rosbuild] Failed to set mtime; aborting")
797 endif(_set_mtime_failed)
798 endif(NOT _srvlist)
799 # Create target to trigger service generation in the case where no libs
800 # or executables are made.
801 add_custom_target(rospack_gensrv_all ALL)
802 add_dependencies(rospack_gensrv_all rospack_gensrv)
803 # Make the precompile target, on which libraries and executables depend,
804 # depend on the message generation.
805 add_dependencies(rosbuild_precompile rospack_gensrv)
806 # add in the directory that will contain the auto-generated .h files
807 include_directories(${PROJECT_SOURCE_DIR}/srv_gen/cpp/include)
808 endmacro(rosbuild_gensrv)
809
810 # genmsg processes msg/*.msg files into language-specific source files
811 macro(rosbuild_genmsg)
812 # roslang_PACKAGE_PATH was set in rosbuild_init(), if roslang was found
813 if(NOT roslang_PACKAGE_PATH)
814 _rosbuild_warn("rosbuild_genmsg() was called, but the roslang package cannot be found. Message generation will NOT occur")
815 endif(NOT roslang_PACKAGE_PATH)
816 # Check whether there are any .srv files
817 rosbuild_get_msgs(_msglist)
818 if(NOT _msglist)
819 _rosbuild_warn("rosbuild_genmsg() was called, but no .msg files were found")
820 else(NOT _msglist)
821 file(WRITE ${PROJECT_SOURCE_DIR}/msg_gen/generated "yes")
822 # Now set the mtime to something consistent. We only want whether or not this file exists to matter
823 # But we set it to the current time, because setting it to zero causes
824 # annoying warning, #3396.
825 execute_process(
826 COMMAND ${PYTHON_EXECUTABLE} -c "import os; os.utime('${PROJECT_SOURCE_DIR}/msg_gen/generated', (1, 1))"
827 ERROR_VARIABLE _set_mtime_error
828 RESULT_VARIABLE _set_mtime_failed
829 OUTPUT_STRIP_TRAILING_WHITESPACE)
830 if(_set_mtime_failed)
831 message("[rosbuild] Error from calling to Python to set the mtime on ${PROJECT_SOURCE_DIR}/msg_gen/generated:")
832 message("${_mtime_error}")
833 message(FATAL_ERROR "[rosbuild] Failed to set mtime; aborting")
834 endif(_set_mtime_failed)
835 endif(NOT _msglist)
836 # Create target to trigger message generation in the case where no libs
837 # or executables are made.
838 add_custom_target(rospack_genmsg_all ALL)
839 add_dependencies(rospack_genmsg_all rospack_genmsg)
840 # Make the precompile target, on which libraries and executables depend,
841 # depend on the message generation.
842 add_dependencies(rosbuild_precompile rospack_genmsg)
843 # add in the directory that will contain the auto-generated .h files
844 include_directories(${PROJECT_SOURCE_DIR}/msg_gen/cpp/include)
845 endmacro(rosbuild_genmsg)
846
847 macro(rosbuild_add_boost_directories)
848 set(_sysroot "--sysroot=${CMAKE_FIND_ROOT_PATH}")
849 execute_process(COMMAND "rosboost-cfg" ${_sysroot} "--include_dirs"
850 OUTPUT_VARIABLE BOOST_INCLUDE_DIRS
851 RESULT_VARIABLE _boostcfg_failed
852 OUTPUT_STRIP_TRAILING_WHITESPACE)
853
854 if (_boostcfg_failed)
855 message(FATAL_ERROR "rosboost-cfg --include_dirs failed")
856 endif(_boostcfg_failed)
857
858 set(_sysroot "--sysroot=${CMAKE_FIND_ROOT_PATH}")
859 execute_process(COMMAND "rosboost-cfg" ${_sysroot} "--lib_dirs"
860 OUTPUT_VARIABLE BOOST_LIB_DIRS
861 RESULT_VARIABLE _boostcfg_failed
862 OUTPUT_STRIP_TRAILING_WHITESPACE)
863
864 if (_boostcfg_failed)
865 message(FATAL_ERROR "rosboost-cfg --lib_dirs failed")
866 endif(_boostcfg_failed)
867
868 add_definitions(-DBOOST_CB_DISABLE_DEBUG)
869 include_directories(${BOOST_INCLUDE_DIRS})
870 link_directories(${BOOST_LIB_DIRS})
871 endmacro(rosbuild_add_boost_directories)
872
873 macro(rosbuild_link_boost target)
874 set(_libs "")
875 set(_first 1)
876 foreach(arg ${ARGN})
877 if (_first)
878 set(_first 0)
879 set(_libs "${arg}")
880 else(_first)
881 set(_libs "${_libs},${arg}")
882 endif(_first)
883 endforeach(arg)
884 set(_sysroot "--sysroot=${CMAKE_FIND_ROOT_PATH}")
885 # We ask for --lflags, as recommended in #3773. This means that we might
886 # pass non-library arguments to target_link_libraries() below, but CMake
887 # doesn't seem to mind.
888 execute_process(COMMAND "rosboost-cfg" ${_sysroot} "--lflags" ${_libs}
889 OUTPUT_VARIABLE BOOST_LIBS
890 ERROR_VARIABLE _boostcfg_error
891 RESULT_VARIABLE _boostcfg_failed
892 OUTPUT_STRIP_TRAILING_WHITESPACE)
893
894 if (_boostcfg_failed)
895 message(FATAL_ERROR "[rosboost-cfg --libs ${_libs}] failed with error: ${_boostcfg_error}")
896 endif(_boostcfg_failed)
897
898 separate_arguments(BOOST_LIBS)
899
900 target_link_libraries(${target} ${BOOST_LIBS})
901 endmacro(rosbuild_link_boost)
902
903 # Macro to download data on the tests target
904 # The real signature is:
905 #macro(rosbuild_download_test_data _url _filename _md5)
906 macro(rosbuild_download_test_data _url _filename)
907 if("${ARGN}" STREQUAL "")
908 _rosbuild_warn("The 2-argument rosbuild_download_test_data(url file) is deprecated; please switch to the 3-argument form, supplying an md5sum for the file: rosbuild_download_test_data(url file md5)")
909 endif("${ARGN}" STREQUAL "")
910
911 # Create a legal target name, in case the target name has slashes in it
912 string(REPLACE "/" "_" _testname download_data_${_filename})
913 add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/${_filename}
914 COMMAND $ENV{ROS_ROOT}/core/rosbuild/bin/download_checkmd5.py ${_url} ${PROJECT_SOURCE_DIR}/${_filename} ${ARGN}
915 VERBATIM)
916 add_custom_target(${_testname} DEPENDS ${PROJECT_SOURCE_DIR}/${_filename})
917 # Redeclaration of target is to workaround bug in 2.4.6
918 if(CMAKE_MINOR_VERSION LESS 6)
919 add_custom_target(tests)
920 endif(CMAKE_MINOR_VERSION LESS 6)
921 add_dependencies(tests ${_testname})
922 endmacro(rosbuild_download_test_data)
923
924 # There's an optional 3rd arg, which is a target that should be made to
925 # depend on the result of untarring the file (can be ALL).
926 macro(rosbuild_untar_file _filename _unpacked_name)
927 get_filename_component(unpack_dir ${_filename} PATH)
928 # Check whether the filename has a directory component, #3034
929 if(NOT unpack_dir)
930 set(unpack_dir ${PROJECT_SOURCE_DIR})
931 endif(NOT unpack_dir)
932 add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/${_unpacked_name}
933 COMMAND rm -rf ${PROJECT_SOURCE_DIR}/${_unpacked_name}
934 COMMAND tar xvCf ${unpack_dir} ${PROJECT_SOURCE_DIR}/${_filename}
935 COMMAND touch -c ${PROJECT_SOURCE_DIR}/${_unpacked_name}
936 DEPENDS ${PROJECT_SOURCE_DIR}/${_filename}
937 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
938 VERBATIM)
939 string(REPLACE "/" "_" _target_name untar_file_${_filename}_${_unpacked_name})
940
941 if("${ARGN}" STREQUAL "ALL")
942 add_custom_target(${_target_name} ALL DEPENDS ${PROJECT_SOURCE_DIR}/${_unpacked_name})
943 else("${ARGN}" STREQUAL "ALL")
944 add_custom_target(${_target_name} DEPENDS ${PROJECT_SOURCE_DIR}/${_unpacked_name})
945 if(NOT "${ARGN}" STREQUAL "")
946 # Redeclaration of target is to workaround bug in 2.4.6
947 if(CMAKE_MINOR_VERSION LESS 6)
948 add_custom_target(${ARGN})
949 endif(CMAKE_MINOR_VERSION LESS 6)
950 add_dependencies(${ARGN} ${_target_name})
951 endif(NOT "${ARGN}" STREQUAL "")
952 endif("${ARGN}" STREQUAL "ALL")
953 endmacro(rosbuild_untar_file)
954
955 # Macro to download data on the all target
956 # The real signature is:
957 #macro(rosbuild_download_data _url _filename _md5)
958 macro(rosbuild_download_data _url _filename)
959 if("${ARGN}" STREQUAL "")
960 _rosbuild_warn("The 2-argument rosbuild_download_data(url file) is deprecated; please switch to the 3-argument form, supplying an md5sum for the file: rosbuild_download_data(url file md5)")
961 endif("${ARGN}" STREQUAL "")
962 # Create a legal target name, in case the target name has slashes in it
963 string(REPLACE "/" "_" _testname download_data_${_filename})
964 add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/${_filename}
965 COMMAND $ENV{ROS_ROOT}/core/rosbuild/bin/download_checkmd5.py ${_url} ${PROJECT_SOURCE_DIR}/${_filename} ${ARGN}
966 VERBATIM)
967 add_custom_target(${_testname} ALL DEPENDS ${PROJECT_SOURCE_DIR}/${_filename})
968 endmacro(rosbuild_download_data)
969
970 macro(rosbuild_add_openmp_flags target)
971 # Bullseye's wrappers appear to choke on OpenMP pragmas. So if
972 # ROS_TEST_COVERAGE is set (which indicates that we're doing a coverage
973 # build with Bullseye), we make this macro a no-op.
974 if("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
975 _rosbuild_warn("because ROS_TEST_COVERAGE is set, OpenMP support is disabled")
976 else("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
977
978 # First, try to use the standard FindOpenMP module (#3184). If that
979 # fails, fall back on manual detection. I don't know why the standard
980 # detection would ever fail; it's possible that the manual
981 # detection is now vestigial and could be removed.
982 include(FindOpenMP)
983 if(${OPENMP_FOUND} STREQUAL "TRUE")
984 rosbuild_add_compile_flags(${target} ${OpenMP_C_FLAGS})
985 rosbuild_add_link_flags(${target} ${OpenMP_C_FLAGS})
986 else(${OPENMP_FOUND} STREQUAL "TRUE")
987 # list of OpenMP flags to check
988 set(_rospack_check_openmp_flags
989 "-fopenmp" # gcc
990 "-openmp" # icc
991 "-mp" # SGI & PGI
992 "-xopenmp" # Sun
993 "-omp" # Tru64
994 "-qsmp=omp" # AIX
995 )
996
997 # backup for a variable we will change
998 set(_rospack_openmp_flags_backup ${CMAKE_REQUIRED_FLAGS})
999
1000 # mark the fact we do not yet know the flag
1001 set(_rospack_openmp_flag_found FALSE)
1002 set(_rospack_openmp_flag_value)
1003
1004 # find an OpenMP flag that works
1005 foreach(_rospack_openmp_test_flag ${_rospack_check_openmp_flags})
1006 if(NOT _rospack_openmp_flag_found)
1007 set(CMAKE_REQUIRED_FLAGS ${_rospack_openmp_test_flag})
1008 check_function_exists(omp_set_num_threads _rospack_openmp_function_found${_rospack_openmp_test_flag})
1009
1010 if(_rospack_openmp_function_found${_rospack_openmp_test_flag})
1011 set(_rospack_openmp_flag_value ${_rospack_openmp_test_flag})
1012 set(_rospack_openmp_flag_found TRUE)
1013 endif(_rospack_openmp_function_found${_rospack_openmp_test_flag})
1014 endif(NOT _rospack_openmp_flag_found)
1015 endforeach(_rospack_openmp_test_flag ${_rospack_check_openmp_flags})
1016
1017 # restore the CMake variable
1018 set(CMAKE_REQUIRED_FLAGS ${_rospack_openmp_flags_backup})
1019
1020 # add the flags or warn
1021 if(_rospack_openmp_flag_found)
1022 rosbuild_add_compile_flags(${target} ${_rospack_openmp_flag_value})
1023 rosbuild_add_link_flags(${target} ${_rospack_openmp_flag_value})
1024 else(_rospack_openmp_flag_found)
1025 message("WARNING: OpenMP compile flag not found")
1026 endif(_rospack_openmp_flag_found)
1027
1028 endif(${OPENMP_FOUND} STREQUAL "TRUE")
1029 endif("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
1030 endmacro(rosbuild_add_openmp_flags)
1031
1032 macro(rosbuild_make_distribution)
1033 # Noop. This logic is handled in Python instead.
1034 endmacro(rosbuild_make_distribution)
1035
1036 # Compute the number of hardware cores on the machine. Intended to use for
1037 # gating tests that have heavy processor requirements.
1038 include($ENV{ROS_ROOT}/core/rosbuild/ProcessorCount.cmake)
1039 macro(rosbuild_count_cores num)
1040 ProcessorCount(${num})
1041 endmacro(rosbuild_count_cores)
1042
1043 # Check whether we're running as a VM Intended to use for
1044 # gating tests that have heavy processor requirements. It checks for
1045 # /proc/xen
1046 macro(rosbuild_check_for_vm var)
1047 set(_xen_dir _xen_dir-NOTFOUND)
1048 find_file(_xen_dir "xen" PATHS "/proc" NO_DEFAULT_PATH)
1049 if(_xen_dir)
1050 set(${var} 1)
1051 else(_xen_dir)
1052 set(${var} 0)
1053 endif(_xen_dir)
1054 endmacro(rosbuild_check_for_vm var)
1055
1056 # Check whether there's an X display. Intended to use in gating tests that
1057 # require a display.
1058 macro(rosbuild_check_for_display var)
1059 execute_process(COMMAND "xdpyinfo"
1060 OUTPUT_VARIABLE _dummy
1061 ERROR_VARIABLE _dummy
1062 RESULT_VARIABLE _xdpyinfo_failed)
1063 if(_xdpyinfo_failed)
1064 set(${var} 0)
1065 else(_xdpyinfo_failed)
1066 set(${var} 1)
1067 endif(_xdpyinfo_failed)
1068 endmacro(rosbuild_check_for_display)
1069
1070 macro(rosbuild_add_swigpy_library target lib)
1071 rosbuild_add_library(${target} ${ARGN})
1072 # swig python needs a shared library named _<modulename>.[so|dll|...]
1073 # this renames the output file to conform to that by prepending
1074 # an underscore in place of the "lib" prefix.
1075 # If on Darwin, force the suffix so ".so", because the MacPorts
1076 # version of Python won't find _foo.dylib for 'import _foo'
1077 if(APPLE)
1078 set_target_properties(${target}
1079 PROPERTIES OUTPUT_NAME ${lib}
1080 PREFIX "_" SUFFIX ".so")
1081 else(APPLE)
1082 set_target_properties(${target}
1083 PROPERTIES OUTPUT_NAME ${lib}
1084 PREFIX "_")
1085 endif(APPLE)
1086 endmacro(rosbuild_add_swigpy_library)
1087
1088 macro(rosbuild_check_for_sse)
1089 # check for SSE extensions
1090 include(CheckCXXSourceRuns)
1091 if( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX )
1092 set(SSE_FLAGS)
1093
1094 set(CMAKE_REQUIRED_FLAGS "-msse3")
1095 check_cxx_source_runs("
1096 #include <pmmintrin.h>
1097
1098 int main()
1099 {
1100 __m128d a, b;
1101 double vals[2] = {0};
1102 a = _mm_loadu_pd(vals);
1103 b = _mm_hadd_pd(a,a);
1104 _mm_storeu_pd(vals, b);
1105 return 0;
1106 }"
1107 HAS_SSE3_EXTENSIONS)
1108
1109 set(CMAKE_REQUIRED_FLAGS "-msse2")
1110 check_cxx_source_runs("
1111 #include <emmintrin.h>
1112
1113 int main()
1114 {
1115 __m128d a, b;
1116 double vals[2] = {0};
1117 a = _mm_loadu_pd(vals);
1118 b = _mm_add_pd(a,a);
1119 _mm_storeu_pd(vals,b);
1120 return 0;
1121 }"
1122 HAS_SSE2_EXTENSIONS)
1123
1124 set(CMAKE_REQUIRED_FLAGS "-msse")
1125 check_cxx_source_runs("
1126 #include <xmmintrin.h>
1127 int main()
1128 {
1129 __m128 a, b;
1130 float vals[4] = {0};
1131 a = _mm_loadu_ps(vals);
1132 b = a;
1133 b = _mm_add_ps(a,b);
1134 _mm_storeu_ps(vals,b);
1135 return 0;
1136 }"
1137 HAS_SSE_EXTENSIONS)
1138
1139 set(CMAKE_REQUIRED_FLAGS)
1140
1141 if(HAS_SSE3_EXTENSIONS)
1142 set(SSE_FLAGS "-msse3 -mfpmath=sse")
1143 message(STATUS "[rosbuild] Found SSE3 extensions, using flags: ${SSE_FLAGS}")
1144 elseif(HAS_SSE2_EXTENSIONS)
1145 set(SSE_FLAGS "-msse2 -mfpmath=sse")
1146 message(STATUS "[rosbuild] Found SSE2 extensions, using flags: ${SSE_FLAGS}")
1147 elseif(HAS_SSE_EXTENSIONS)
1148 set(SSE_FLAGS "-msse -mfpmath=sse")
1149 message(STATUS "[rosbuild] Found SSE extensions, using flags: ${SSE_FLAGS}")
1150 endif()
1151 elseif(MSVC)
1152 check_cxx_source_runs("
1153 #include <emmintrin.h>
1154
1155 int main()
1156 {
1157 __m128d a, b;
1158 double vals[2] = {0};
1159 a = _mm_loadu_pd(vals);
1160 b = _mm_add_pd(a,a);
1161 _mm_storeu_pd(vals,b);
1162 return 0;
1163 }"
1164 HAS_SSE2_EXTENSIONS)
1165 if( HAS_SSE2_EXTENSIONS )
1166 message(STATUS "[rosbuild] Found SSE2 extensions")
1167 set(SSE_FLAGS "/arch:SSE2 /fp:fast -D__SSE__ -D__SSE2__" )
1168 endif()
1169 endif()
1170 endmacro(rosbuild_check_for_sse)
1171
1172 macro(rosbuild_include pkg module)
1173 # Find exported cmake directories
1174 rosbuild_invoke_rospack(rosbuild _rosbuild EXPORTS plugins --attrib=cmake_directory --top=${_project})
1175 string(REGEX REPLACE "\n" ";" _rosbuild_EXPORTS_stripped "${_rosbuild_EXPORTS}")
1176 list(LENGTH _rosbuild_EXPORTS_stripped _rosbuild_EXPORTS_stripped_length)
1177 set(_idx 0)
1178 set(_found False)
1179 while(_idx LESS ${_rosbuild_EXPORTS_stripped_length} AND NOT _found)
1180 list(GET _rosbuild_EXPORTS_stripped ${_idx} _pkg)
1181 math(EXPR _idx "${_idx} + 1")
1182 list(GET _rosbuild_EXPORTS_stripped ${_idx} _dir)
1183 if("${_pkg}" STREQUAL "${pkg}")
1184 message("[rosbuild] Including ${_dir}/${module}.cmake")
1185 # Add this directory to CMake's search path, so that included files
1186 # can include other files in a relative manner, #2813.
1187 if(NOT CMAKE_MODULE_PATH MATCHES ${_dir})
1188 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${_dir})
1189 endif()
1190 include(${_dir}/${module}.cmake)
1191 # Poor man's break
1192 set(_found True)
1193 endif("${_pkg}" STREQUAL "${pkg}")
1194 math(EXPR _idx "${_idx} + 1")
1195 endwhile(_idx LESS ${_rosbuild_EXPORTS_stripped_length} AND NOT _found)
1196 if(NOT _found)
1197 message(FATAL_ERROR "[rosbuild] Failed to include ${module} from ${pkg}")
1198 endif(NOT _found)
1199 endmacro(rosbuild_include)
1200
1201 macro(rosbuild_get_package_version _var pkgname)
1202 execute_process(
1203 COMMAND rosstack contains ${pkgname}
1204 ERROR_VARIABLE __rosstack_err_ignore
1205 OUTPUT_VARIABLE __stack
1206 RESULT_VARIABLE _rosstack_failed
1207 OUTPUT_STRIP_TRAILING_WHITESPACE)
1208 if(_rosstack_failed OR NOT __stack)
1209 message(FATAL_ERROR "[rosbuild] Failed to find stack containing package ${pkgname}")
1210 else(_rosstack_failed OR NOT __stack)
1211 rosbuild_get_stack_version(${_var} ${__stack})
1212 endif(_rosstack_failed OR NOT __stack)
1213 endmacro(rosbuild_get_package_version)
1214
1215 macro(rosbuild_get_stack_version _var stackname)
1216 execute_process(
1217 COMMAND rosversion ${stackname}
1218 ERROR_VARIABLE __rosversion_err_ignore
1219 OUTPUT_VARIABLE __version
1220 RESULT_VARIABLE _rosversion_failed
1221 OUTPUT_STRIP_TRAILING_WHITESPACE)
1222 if(_rosversion_failed OR NOT __version)
1223 message(FATAL_ERROR "[rosbuild] Failed to find version of stack ${stackname}")
1224 else(_rosversion_failed OR NOT __version)
1225 set(${_var} ${__version})
1226 endif(_rosversion_failed OR NOT __version)
1227 endmacro(rosbuild_get_stack_version _var stackname)
1228
1229 # *_future() calls are deprecated
1230 macro(rosbuild_add_gtest_future exe)
1231 _rosbuild_warn("The *_future() macros are deprecated.")
1232 # Still need to create the executable, just in case somebody is using
1233 # this macro, then doing something that refers to the executable target.
1234 _rosbuild_add_gtest(${ARGV})
1235 endmacro(rosbuild_add_gtest_future)
1236
1237 macro(rosbuild_add_rostest_future file)
1238 _rosbuild_warn("The *_future() macros are deprecated.")
1239 endmacro(rosbuild_add_rostest_future)
1240
1241 macro(rosbuild_add_pyunit_future file)
1242 _rosbuild_warn("The *_future() macros are deprecated.")
1243 endmacro(rosbuild_add_pyunit_future)
1244
0
1 cmake_minimum_required(VERSION 2.4.6)
2
3 #
4 # Catkin-compat thunks
5 #
6 cmake_policy(SET CMP0011 OLD)
7
8 macro(rosbuild_catkinize)
9 endmacro()
10
11 # Policy settings to prevent warnings on 2.6 but ensure proper operation on
12 # 2.4.
13 if(COMMAND cmake_policy)
14 # Logical target names must be globally unique.
15 cmake_policy(SET CMP0002 OLD)
16 # Libraries linked via full path no longer produce linker search paths.
17 cmake_policy(SET CMP0003 OLD)
18 # Preprocessor definition values are now escaped automatically.
19 cmake_policy(SET CMP0005 OLD)
20 if(POLICY CMP0011)
21 # Included scripts do automatic cmake_policy PUSH and POP.
22 cmake_policy(SET CMP0011 OLD)
23 endif(POLICY CMP0011)
24 endif(COMMAND cmake_policy)
25
26 set(CMAKE_OSX_ARCHITECTURES "x86_64")
27
28 ##############################################################################
29 # First things first: we must have rospack. But only look at PATH, #3831
30 find_program(ROSPACK_EXE NAMES rospack DOC "rospack executable" NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
31 if (NOT ROSPACK_EXE)
32 message(FATAL_ERROR "Couldn't find rospack. Please run 'make' in $ROS_ROOT")
33 endif(NOT ROSPACK_EXE)
34 ##############################################################################
35
36 # Load private macros (not to be used externally)
37 include($ENV{ROS_ROOT}/core/rosbuild/private.cmake)
38 # Load public macros (developer's API)
39 include($ENV{ROS_ROOT}/core/rosbuild/public.cmake)
0 # DO NOT EDIT THIS FILE
1
2 # To change your build configuration, create a file called
3 # 'rosconfig.cmake' and put it where it will be loaded from here.
4 # The order of processing is the following, with later steps overriding
5 # earlier ones:
6 # - read the beginning of the package's CMakeLists.txt (i.e., the
7 # content before the invocation of rosbuild_init()),
8 # which is how we get here
9 # - read this file
10 # - if present, read $(ROS_ROOT)/rosconfig.cmake
11 # - if present, read rosconfig.cmake from the current packages's top-level
12 # directory.
13
14 # Unset variables, to force cmake to go looking for the rosconfig.cmake
15 # again (as opposed to looking up the variables' values in the cache). This
16 # specifically avoids build failures when a rosconfig.cmake was created,
17 # then removed, #617.
18 set(USERCONFIG USERCONFIG-NOTFOUND)
19 set(PROJECTCONFIG PROJECTCONFIG-NOTFOUND)
20
21 #############################################################
22 # look for user's override in $ROS_ROOT/rosconfig.cmake
23 find_file(USERCONFIG
24 rosconfig.cmake
25 PATHS ENV ROS_ROOT
26 NO_DEFAULT_PATH
27 NO_CMAKE_FIND_ROOT_PATH)
28 if(USERCONFIG)
29 message("including user's config file: ${USERCONFIG}")
30 include(${USERCONFIG})
31 endif(USERCONFIG)
32
33 # look for user's override in rosconfig.cmake in current package directory
34 find_file(PROJECTCONFIG
35 rosconfig.cmake
36 PATHS ${PROJECT_SOURCE_DIR}
37 NO_DEFAULT_PATH)
38 if(PROJECTCONFIG)
39 message("including package's config file: ${PROJECTCONFIG}")
40 include(${PROJECTCONFIG})
41 endif(PROJECTCONFIG)
42 #############################################################
43
44
45 #############################################################
46 # These are default ROS-wide build configuration settings for CMake. These
47 # settings are used wherever rospack(<packagename>) is done in a
48 # CMakeLists.txt file. Note that these setting only affect packages
49 # that are built with CMake.
50
51 # Set the build type. Options are:
52 # Debug : w/ debug symbols, w/o optimization
53 # Release : w/o debug symbols, w/ optimization
54 # RelWithDebInfo : w/ debug symbols, w/ optimization
55 # RelWithAsserts : w/o debug symbols, w/ optimization
56 # MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
57 if(NOT DEFINED ROS_BUILD_TYPE)
58 set(ROS_BUILD_TYPE RelWithDebInfo)
59 endif(NOT DEFINED ROS_BUILD_TYPE)
60
61 # Build static-only executables (e.g., for copying over to another
62 # machine)? true or false
63 if(NOT DEFINED ROS_BUILD_STATIC_EXES)
64 set(ROS_BUILD_STATIC_EXES false)
65 endif(NOT DEFINED ROS_BUILD_STATIC_EXES)
66
67 # Build shared libs? true or false
68 if(NOT DEFINED ROS_BUILD_SHARED_LIBS)
69 set(ROS_BUILD_SHARED_LIBS true)
70 endif(NOT DEFINED ROS_BUILD_SHARED_LIBS)
71
72 # Build static libs? true or false
73 if(NOT DEFINED ROS_BUILD_STATIC_LIBS)
74 set(ROS_BUILD_STATIC_LIBS false)
75 endif(NOT DEFINED ROS_BUILD_STATIC_LIBS)
76
77 # Default compile flags for all source files
78 include(CheckCXXCompilerFlag)
79 if(NOT DEFINED ROS_COMPILE_FLAGS)
80 set(ROS_COMPILE_FLAGS "-W -Wall -Wno-unused-parameter -fno-strict-aliasing")
81 # Old versions of gcc need -pthread to enable threading, #2095.
82 # Also, some linkers, e.g., goLD, require -pthread (or another way to
83 # generate -lpthread).
84 # CYGWIN gcc has their -pthread disabled
85 if(UNIX AND NOT CYGWIN)
86 set(ROS_COMPILE_FLAGS "${ROS_COMPILE_FLAGS} -pthread")
87 endif(UNIX AND NOT CYGWIN)
88 endif(NOT DEFINED ROS_COMPILE_FLAGS)
89
90 # Default link flags for all executables and libraries
91 if(NOT DEFINED ROS_LINK_FLAGS)
92 set(ROS_LINK_FLAGS "")
93 # Old versions of gcc need -pthread to enable threading, #2095.
94 # Also, some linkers, e.g., goLD, require -pthread (or another way to
95 # generate -lpthread).
96 # CYGWIN gcc has their -pthread disabled
97 if(UNIX AND NOT CYGWIN)
98 set(ROS_LINK_FLAGS "${ROS_LINK_FLAGS} -pthread")
99 endif(UNIX AND NOT CYGWIN)
100 endif(NOT DEFINED ROS_LINK_FLAGS)
101
102 # Default libraries to link against for all executables and libraries
103 if(NOT DEFINED ROS_LINK_LIBS)
104 set(ROS_LINK_LIBS "")
105 endif(NOT DEFINED ROS_LINK_LIBS)
106
107 #############################################################
108
0 # DO NOT EDIT THIS FILE
1 #
2 # To set up cross-compilation, create the file
3 # $(ROS_ROOT)/rostoolchain.cmake. It gets read first, prior to
4 # any of cmake's system tests.
5
6 #############################################################
7 #
8 # An example for using the gumstix arm-linux toolchain is below.
9 # Copy these lines to $(ROS_ROOT)/rostoolchain.cmake to try them out.
10 #
11 #set(CMAKE_SYSTEM_NAME Linux)
12 #set(CMAKE_C_COMPILER /opt/arm-linux/bin/arm-linux-gcc)
13 #set(CMAKE_CXX_COMPILER /opt/arm-linux/bin/arm-linux-g++)
14 #set(CMAKE_FIND_ROOT_PATH /opt/arm-linux)
15 # Have to set this one to BOTH, to allow CMake to find rospack
16 #set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
17 #set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
18 #set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
19
20 #############################################################
21 # Cross-compilation loader
22 #
23 # This file is passed to cmake on the command line via
24 # -DCMAKE_TOOLCHAIN_FILE.
25
26 #############################################################
27 # look for user's toolchain file in $ROS_ROOT/rostoolchain.cmake
28 find_file(TOOLCHAINCONFIG
29 rostoolchain.cmake
30 PATHS ENV ROS_ROOT
31 NO_DEFAULT_PATH)
32 if(TOOLCHAINCONFIG)
33 include(${TOOLCHAINCONFIG})
34 endif(TOOLCHAINCONFIG)
35 #############################################################
0 #!/usr/bin/env python
1
2 import os
3 print os.sysconf(os.sysconf_names['SC_NPROCESSORS_ONLN'])
0 cmake_minimum_required(VERSION 2.8)
1 project(roslang)
2 find_package(catkin REQUIRED)
3 catkin_package()
4 catkin_package_export()
5
6 install(FILES cmake/roslang.cmake
7 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/cmake)
0 # Figure out which languages we're building for. "rospack langs" will
1 # return a list of packages that:
2 # - depend directly on roslang
3 # - are not in the env var ROS_LANG_DISABLE
4 rosbuild_invoke_rospack("" _roslang LANGS langs)
5 separate_arguments(_roslang_LANGS)
6
7 # Iterate over the languages, retrieving any exported cmake fragment from
8 # each one.
9 set(_cmake_fragments)
10 foreach(_l ${_roslang_LANGS})
11 # Get the roslang attributes from this package.
12
13 # cmake
14 rosbuild_invoke_rospack(${_l} ${_l} CMAKE export --lang=roslang --attrib=cmake)
15 if(${_l}_CMAKE)
16 foreach(_f ${${_l}_CMAKE})
17 list(APPEND _cmake_fragments ${_f})
18 endforeach(_f)
19 endif(${_l}_CMAKE)
20 endforeach(_l)
21
22 # Now include them all
23 foreach(_f ${_cmake_fragments})
24 if(NOT EXISTS ${_f})
25 message(FATAL_ERROR "Cannot include non-existent exported cmake file ${_f}")
26 endif(NOT EXISTS ${_f})
27 # Include this cmake fragment; presumably it will do /
28 # provide something useful. Only include each file once (a file
29 # might be multiply referenced because of package dependencies
30 # dependencies).
31 if(NOT ${_f}_INCLUDED)
32 message("[rosbuild] Including ${_f}")
33 include(${_f})
34 set(${_f}_INCLUDED Y)
35 endif(NOT ${_f}_INCLUDED)
36 endforeach(_f)
0 <package>
1 <description brief="ROS client library base">
2
3 roslang is a common package that all
4 <a href="http://www.ros.org/wiki/Client%20Libraries">ROS client
5 libraries</a> depend on. This is mainly used to find client
6 libraries (via 'rospack depends-on1 roslang').
7
8 </description>
9 <author>Brian Gerkey/gerkey@willowgarage.com</author>
10 <license>BSD</license>
11 <review status="Doc reviewed" notes="2010/01/10"/>
12 <url>http://ros.org/wiki/roslang</url>
13 </package>
14
0 <package>
1 <name>roslang</name>
2 <version>1.9.11</version>
3 <description brief="ROS client library base">
4
5 roslang is a common package that all
6 <a href="http://www.ros.org/wiki/Client%20Libraries">ROS client
7 libraries</a> depend on. This is mainly used to find client
8 libraries (via 'rospack depends-on1 roslang').
9
10 </description>
11 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
12 <license>BSD</license>
13
14 <url>http://ros.org/wiki/roslang</url>
15 <author email="gerkey@willowgarage.com">Brian Gerkey</author>
16
17 <build_depend>catkin</build_depend>
18 </package>
0 project(roslib)
1 find_package(catkin REQUIRED COMPONENTS rospack)
2 catkin_package()
3 catkin_package_export(
4 INCLUDE_DIRS include
5 LIBRARIES roslib
6 CFG_EXTRAS roslib-extras.cmake)
7
8 catkin_python_setup()
9
10 find_package(Boost REQUIRED COMPONENTS thread)
11
12 include_directories(include ${Boost_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS})
13 link_directories(${catkin_LIBRARY_DIRS})
14
15 # Avoid a boost warning that pops up when using msvc compiler
16 if(MSVC)
17 add_definitions(-D_SCL_SECURE_NO_WARNINGS)
18 endif()
19
20 add_library(roslib src/package.cpp)
21 target_link_libraries(roslib ${Boost_LIBRARIES} ${catkin_LIBRARIES})
22
23 if(NOT (APPLE OR WIN32 OR MINGW))
24 target_link_libraries(roslib rt)
25 endif()
26
27
28 #explicitly install library and includes
29 install(TARGETS roslib
30 ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
31 LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
32 )
33
34 install(DIRECTORY include/
35 DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}
36 FILES_MATCHING PATTERN "*.h")
37 install(PROGRAMS scripts/gendeps
38 DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
39
40 #integration tests
41
42 catkin_add_nosetests(test)
43
44 include_directories(${GTEST_INCLUDE_DIRS})
45 link_directories(${GTEST_LIBRARY_DIRS})
46
47 catkin_add_gtest(${PROJECT_NAME}-utest test/utest.cpp WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
48 if(TARGET ${PROJECT_NAME}-utest)
49 target_link_libraries(${PROJECT_NAME}-utest roslib ${Boost_LIBRARIES} ${catkin_LIBRARIES})
50 endif()
51
52 catkin_add_gtest(${PROJECT_NAME}-test_package test/package.cpp)
53 if(TARGET ${PROJECT_NAME}-test_package)
54 target_link_libraries(${PROJECT_NAME}-test_package roslib ${catkin_LIBRARIES})
55 endif()
0 # generated from ros/core/roslib/cmake/roslib-extras.cmake.in
1
2 # set path to gendeps executable
3 if (@BUILDSPACE@)
4 set(gendeps_exe @CMAKE_CURRENT_SOURCE_DIR@/scripts/gendeps)
5 else()
6 set(gendeps_exe @CMAKE_INSTALL_PREFIX@/lib/roslib/gendeps)
7 endif()
0 [epydoc]
1 name: roslib
2 modules: roslib
3 inheritance: included
4 url: http://ros.org/wiki/rospy
5 frames: yes
6 private: no
7 exclude: roslib.msg
8
0 /*
1 * Copyright (C) 2009, 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 ROSLIB_PACKAGE_H
28 #define ROSLIB_PACKAGE_H
29
30 #include <string>
31 #include <vector>
32 #include <map>
33
34 #if defined(__GNUC__)
35 # define ROS_DEPRECATED __attribute__((deprecated))
36 # define ROS_FORCE_INLINE __attribute__((always_inline))
37 #elif defined(MSVC)
38 # define ROS_DEPRECATED
39 # define ROS_FORCE_INLINE __forceinline
40 #else
41 # define ROS_DEPRECATED
42 # define ROS_FORCE_INLINE inline
43 #endif
44
45 /*
46 Windows import/export and gnu http://gcc.gnu.org/wiki/Visibility
47 macros.
48 */
49
50 #if defined(_MSC_VER)
51 # define ROS_HELPER_IMPORT __declspec(dllimport)
52 # define ROS_HELPER_EXPORT __declspec(dllexport)
53 #elif __GNUC__ >= 4
54 # define ROS_HELPER_IMPORT __attribute__ ((visibility("default")))
55 # define ROS_HELPER_EXPORT __attribute__ ((visibility("default")))
56 #else
57 # define ROS_HELPER_IMPORT
58 # define ROS_HELPER_EXPORT
59 #endif
60
61 // Ignore warnings about import/exports when deriving from std classes.
62 #ifdef _MSC_VER
63 # pragma warning(disable: 4251)
64 # pragma warning(disable: 4275)
65 #endif
66
67 #ifdef ROS_BUILD_SHARED_LIBS // ros is being built around shared libraries
68 # ifdef roslib_EXPORTS // we are building a shared lib/dll
69 # define ROSLIB_DECL ROS_HELPER_EXPORT
70 # else // we are using shared lib/dll
71 # define ROSLIB_DECL ROS_HELPER_IMPORT
72 # endif
73 #else // ros is being built around static libraries
74 # define ROSLIB_DECL
75 #endif
76
77 namespace ros
78 {
79 namespace package
80 {
81
82 typedef std::vector<std::string> V_string;
83 typedef std::map<std::string, std::string> M_string;
84
85 /**
86 * \brief Runs a rospack command of the form 'rospack <cmd>', returning the output as a single string
87 */
88 ROSLIB_DECL std::string command(const std::string& cmd);
89
90 /**
91 * \brief Runs a rospack command of the form 'rospack <cmd>', returning the output as a vector of strings, split by newlines
92 */
93 ROSLIB_DECL void command(const std::string& cmd, V_string& output);
94 /**
95 * \brief Returns the fully-qualified path to a package, or an empty string if the package is not found
96 */
97 ROSLIB_DECL std::string getPath(const std::string& package_name);
98 /**
99 * \brief Gets a list of all packages. Returns false if it could not run the command.
100 */
101 ROSLIB_DECL bool getAll(V_string& packages);
102
103 /**
104 * \brief Call the "rospack plugins" command, eg. "rospack plugins --attrib=<attribute> <package>". Returns a vector of strings which
105 * are export values
106 */
107 ROSLIB_DECL void getPlugins(const std::string& package, const std::string& attribute, V_string& plugins);
108
109 /**
110 * \brief Call the "rospack plugins" command, eg. "rospack plugins --attrib=<attribute> <package>". Returns a map of package name to
111 * export value.
112 */
113 ROSLIB_DECL void getPlugins(const std::string& package, const std::string& attribute, M_string& plugins);
114
115 } // namespace package
116 } // namespace ros
117
118 #endif
0 /**
1 \mainpage
2 \htmlinclude manifest.html
3
4
5 \b %roslib is the base library support for ROS <a href="http://ros.org/wiki/Client Libraries">client implementations</a> as well as ROS tools. It includes:
6
7 - common message definitions used in ROS clients (e.g. Header)
8 - a Python library for manipulating ROS system resources (e.g. .msg files, names)
9 - message and services generation callout scripts
10
11 \section codeapi Code API
12
13 - Time-related: ros::Time, ros::Duration, ros::Rate, ros::WallTime, ros::WallDuration, ros::WallRate. Also see the <a href="http://ros.org/wiki/roscpp/Overview/Time">roscpp Time overview</a>.
14 - Package-related: ros::package namespace
15 - Debug-related: ros::debug namespace
16
17 */
0 <package>
1 <description brief="ROS base library">
2
3 Base dependencies and support libraries for ROS. roslib contains many of the common data structures and tools that are shared across ROS client library implementations.
4
5 </description>
6 <author>Ken Conley/kwc@willowgarage.com, Morgan Quigley/mquigley@cs.stanford.edu, Josh Faust/jfaust@willowgarage.com</author>
7 <license>BSD</license>
8 <review status="Doc reviewed" notes="2010/01/12"/>
9 <url>http://ros.org/wiki/roslib</url>
10 <export>
11 <cpp cflags="`pkg-config --cflags roslib`"
12 lflags="`pkg-config --libs roslib`"/>
13 <rosdoc config="${prefix}/rosdoc.yaml" />
14 </export>
15
16 <rosdep name="python"/>
17 <rosdep name="boost"/>
18 </package>
0 <package>
1 <name>roslib</name>
2 <version>1.9.11</version>
3 <description brief="ROS base library">
4
5 Base dependencies and support libraries for ROS. roslib contains many of the common data structures and tools that are shared across ROS client library implementations.
6
7 </description>
8 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
9 <license>BSD</license>
10
11 <url>http://ros.org/wiki/roslib</url>
12 <author email="kwc@willowgarage.com">Ken Conley</author>
13 <author email="mquigley@cs.stanford.edu">Morgan Quigley</author>
14 <author email="jfaust@willowgarage.com">Josh Faust</author>
15
16 <build_depend>catkin</build_depend>
17 <build_depend>boost</build_depend>
18 <build_depend>python</build_depend>
19 <build_depend>rospack</build_depend>
20
21 <export>
22 <rosdoc config="${prefix}/rosdoc.yaml" />
23 </export>
24 </package>
0 - builder: epydoc
1 output_dir: python
2 config: epydoc.config
3 - builder: doxygen
4 name: C++ API
5 output_dir: c++
6 file_patterns: '*.c *.cpp *.h *.cc *.hh *.dox'
0 #! /usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id: gendeps 3009 2008-12-05 00:47:44Z sfkwc $
34 # $Author: sfkwc $
35
36 ## Script for determining the ROS messages a particular message
37 ## depends on. This is important for determining messages file that
38 ## need to be rebuilt.
39
40 import sys
41
42 import roslib.msgs
43 import roslib.srvs
44 import roslib.gentools
45
46 NAME='gendeps'
47
48 def usage(progname, stdout=sys.stdout):
49 print >> stdout, "%(progname)s msg-or-srv-file"%vars()
50
51 ## main method for gendeps command
52 ## @param argv [str]: sys args
53 ## @param stdout pipe: stdout pipe
54 ## @param stderr pipe: stderr pipe
55 def gendeps_main(argv, stdout, stderr):
56 from optparse import OptionParser
57 parser = OptionParser(usage="usage: %prog [options] [files...]", prog=NAME)
58 parser.add_option("-m", "--md5",
59 dest="md5", default=False,
60 action="store_true",
61 help="Generate md5 hash of files")
62 parser.add_option("-s", "--sha1",
63 dest="sha1", default=False,
64 action="store_true",
65 help="Generate SHA1 hash of files")
66 parser.add_option("-c", "--cat",
67 dest="cat_files", default=False,
68 action="store_true",
69 help="Generate concatenated list of files")
70 (options, args) = parser.parse_args(argv)
71
72 # get the file name
73 if len(args) != 2:
74 parser.error("you must specify one input file")
75 f = args[1]
76
77 # validate that options are compatible
78 if options.md5 and (options.sha1 or options.cat_files):
79 parser.error("md5 option is not compatible with other options")
80 if options.sha1 and (options.md5 or options.cat_files):
81 parser.error("sha1 option is not compatible with other options")
82 if options.cat_files and (options.md5 or options.sha1):
83 parser.error("cat option is not compatible with other options")
84
85 retval = roslib.gentools.get_file_dependencies(f, stdout=stdout, stderr=stderr)
86
87 if options.md5:
88 print >> stdout, roslib.gentools.compute_md5(retval)
89 elif options.sha1:
90 print >> stdout, roslib.gentools.compute_sha1(retval)
91 elif options.cat_files:
92 # this option is used for the message definition that is
93 # stored in exchanged in ROS handshakes and then stored in bag
94 # files
95 print >> stdout, roslib.gentools.compute_full_text(retval)
96 else:
97 print >> stdout, ' '.join(retval['files'].itervalues())
98
99 if __name__ == "__main__":
100 try:
101 gendeps_main(sys.argv, sys.stdout, sys.stderr)
102 except Exception, e:
103 print >> sys.stderr, e
104 sys.exit(1)
105
0 #!/usr/bin/env python
1
2 from distutils.core import setup
3 from catkin_pkg.package import parse_package_for_distutils
4
5 d = parse_package_for_distutils()
6 d['packages'] = ['roslib']
7 d['package_dir'] = {'': 'src'}
8 d['install_requires'] = ['rospkg']
9
10 setup(**d)
0 /*
1 * Copyright (C) 2009, 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 "ros/package.h"
28 #include "rospack/rospack.h"
29
30 #include <cstdio>
31 #include <iostream>
32
33 #include <boost/algorithm/string/split.hpp>
34 #include <boost/algorithm/string/join.hpp>
35 #include <boost/algorithm/string/classification.hpp>
36 #include <boost/thread/mutex.hpp>
37
38 namespace ros
39 {
40 namespace package
41 {
42
43 // Mutex used to lock calls into librospack, which is not thread-safe.
44 static boost::mutex librospack_mutex;
45
46 std::string command(const std::string& _cmd)
47 {
48 boost::mutex::scoped_lock lock(librospack_mutex);
49
50 rospack::ROSPack rp;
51 int ret;
52 try
53 {
54 ret = rp.run(_cmd);
55 if(ret == 0)
56 return rp.getOutput();
57 else {
58 if ( !rp.is_quiet() )
59 std::cerr << "ROSPack::run returned non-zero." << std::endl;
60 }
61 }
62 catch(std::runtime_error &e)
63 {
64 if ( !rp.is_quiet() )
65 std::cerr << "[rospack] " << e.what() << std::endl;
66 }
67 return std::string("");
68 }
69
70 void command(const std::string& cmd, V_string& output)
71 {
72 std::string out_string = command(cmd);
73 V_string full_list;
74 boost::split(full_list, out_string, boost::is_any_of("\r\n"));
75
76 // strip empties
77 V_string::iterator it = full_list.begin();
78 V_string::iterator end = full_list.end();
79 for (; it != end; ++it)
80 {
81 if (!it->empty())
82 {
83 output.push_back(*it);
84 }
85 }
86 }
87
88 std::string getPath(const std::string& package_name)
89 {
90 std::string path = command("find " + package_name);
91
92 // scrape any newlines out of it
93 for (size_t newline = path.find('\n'); newline != std::string::npos;
94 newline = path.find('\n'))
95 {
96 path.erase(newline, 1);
97 }
98
99 return path;
100 }
101
102 bool getAll(V_string& packages)
103 {
104 command("list-names", packages);
105
106 return true;
107 }
108
109 void getPlugins(const std::string& package, const std::string& attribute, V_string& plugins)
110 {
111 M_string plugins_map;
112 getPlugins(package, attribute, plugins_map);
113 M_string::iterator it = plugins_map.begin();
114 M_string::iterator end = plugins_map.end();
115 for (; it != end; ++it)
116 {
117 plugins.push_back(it->second);
118 }
119 }
120
121 void getPlugins(const std::string& package, const std::string& attribute, M_string& plugins)
122 {
123 V_string lines;
124 command("plugins --attrib=" + attribute + " " + package, lines);
125
126 V_string::iterator it = lines.begin();
127 V_string::iterator end = lines.end();
128 for (; it != end; ++it)
129 {
130 V_string tokens;
131 boost::split(tokens, *it, boost::is_any_of(" "));
132
133 if (tokens.size() >= 2)
134 {
135 std::string package = tokens[0];
136 std::string rest = boost::join(V_string(tokens.begin() + 1, tokens.end()), " ");
137 plugins[package] = rest;
138 }
139 }
140 }
141
142 } // namespace package
143 } // namespace ros
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 # Revision $Id: __init__.py 3544 2009-01-24 00:09:21Z sfkwc $
33
34 ## The ros module enables dynamic importing of any ROS python module.
35 ## The common syntax is 'from ros import foo', where foo is a ROS package
36 ## name.
37
38 import sys
39
40 import roslib
41
42 ## @internal
43 class Module(object):
44 def __init__(self, wrapped):
45 self.wrapped = wrapped
46
47 def __getattr__(self, name):
48 try:
49 return getattr(self.wrapped, name)
50 except AttributeError:
51 import roslib.packages
52 try:
53 roslib.load_manifest(name.split('.')[0])
54 except roslib.packages.InvalidROSPkgException as e:
55 raise ImportError("Cannot import module '%s': \n%s"%(name, str(e)))
56 return __import__(name)
57
58 ## rewrite our own entry in sys.modules so that dynamic loading
59 ## works.
60 sys.modules[__name__] = Module(sys.modules[__name__])
61
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 """
33 roslib is largely deprecated starting in the ROS Fuerte release.
34
35 roslib has a very important role in all Python code written for ROS:
36 it contains the L{load_manifest()} method, which updates the
37 PYTHONPATH based on a set of ROS Package manifest.xml files.
38
39 Beyond the important load_manifest() call, most of the rest of roslib
40 consists of low-level libraries that 99% of ROS users need not
41 interact with. These libraries are primarily to support higher-level
42 ROS Python libraries, such as the rospy client library, as well as
43 numerous ROS tools (e.g. rostopic).
44
45 """
46
47 __version__ = '1.7.0'
48
49 from roslib.launcher import load_manifest
50
51 # this import is necessary due to a bug in purge_build.py in our
52 # debian assets.
53 import roslib.stacks
54
55 _is_interactive = False
56 def set_interactive(interactive):
57 """
58 General API for a script specifying that it is being run in an
59 interactive environment. Many libraries may wish to change their
60 behavior based on being interactive (e.g. disabling signal
61 handlers on Ctrl-C).
62
63 @param interactive: True if current script is being run in an interactive shell
64 @type interactive: bool
65 """
66 global _is_interactive
67 _is_interactive = interactive
68
69 def is_interactive():
70 """
71 General API for a script specifying that it is being run in an
72 interactive environment. Many libraries may wish to change their
73 behavior based on being interactive (e.g. disabling signal
74 handlers on Ctrl-C).
75
76 @return: True if interactive flag has been set
77 @rtype: bool
78 """
79 return _is_interactive
80
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 #
32 # Revision $Id$
33 # $Author$
34
35 """
36 Provides the L{ROSLibException} class, which is common to many roslib libraries.
37 """
38
39 class ROSLibException(Exception):
40 """
41 Base class for exceptions in roslib
42 """
43 pass
0 #! /usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34 # $Author$
35
36 """
37 Library for supporting message and service generation for all ROS
38 client libraries. This is mainly responsible for calculating the
39 md5sums and message definitions of classes.
40 """
41
42 # NOTE: this should not contain any rospy-specific code. The rospy
43 # generator library is rospy.genpy.
44
45 import sys
46
47 try:
48 from cStringIO import StringIO # Python 2.x
49 except ImportError:
50 from io import StringIO # Python 3.x
51
52 import rospkg
53
54 import roslib.msgs
55 from roslib.msgs import MsgSpecException
56 import roslib.names
57 import roslib.srvs
58
59 # name of the Header type as gentools knows it
60 _header_type_name = 'std_msgs/Header'
61
62 def _add_msgs_depends(rospack, spec, deps, package_context):
63 """
64 Add the list of message types that spec depends on to depends.
65 @param spec: message to compute dependencies for
66 @type spec: roslib.msgs.MsgSpec/roslib.srvs.SrvSpec
67 @param deps [str]: list of dependencies. This list will be updated
68 with the dependencies of spec when the method completes
69 @type deps: [str]
70 @raise KeyError for invalid dependent types due to missing package dependencies.
71 """
72 valid_packages = ['', package_context]
73 try:
74 valid_packages = valid_packages + rospack.get_depends(package_context, implicit=True)
75 except rospkg.ResourceNotFound:
76 # this happens in dynamic generation situations where the
77 # package is not present. we soft fail here because we assume
78 # missing messages will be caught later during lookup.
79 pass
80
81 for t in spec.types:
82 t = roslib.msgs.base_msg_type(t)
83 if not roslib.msgs.is_builtin(t):
84 t_package, t_base = roslib.names.package_resource_name(t)
85
86 # special mapping for header
87 if t == roslib.msgs.HEADER:
88 # have to re-names Header
89 deps.append(_header_type_name)
90
91 if roslib.msgs.is_registered(t):
92 depspec = roslib.msgs.get_registered(t)
93 if t != roslib.msgs.HEADER:
94 if '/' in t:
95 deps.append(t)
96 else:
97 deps.append(package_context+'/'+t)
98
99 elif t_package in valid_packages:
100 # if we are allowed to load the message, load it.
101 key, depspec = roslib.msgs.load_by_type(t, package_context)
102 if t != roslib.msgs.HEADER:
103 deps.append(key)
104 roslib.msgs.register(key, depspec)
105 else:
106 # not allowed to load the message, so error.
107 raise KeyError(t)
108 _add_msgs_depends(rospack, depspec, deps, package_context)
109
110 def compute_md5_text(get_deps_dict, spec):
111 """
112 Compute the text used for md5 calculation. MD5 spec states that we
113 removes comments and non-meaningful whitespace. We also strip
114 packages names from type names. For convenience sake, constants are
115 reordered ahead of other declarations, in the order that they were
116 originally defined.
117
118 @return: text for ROS MD5-processing
119 @rtype: str
120 """
121 uniquedeps = get_deps_dict['uniquedeps']
122 package = get_deps_dict['package']
123 # #1554: need to suppress computation of files in dynamic generation case
124 compute_files = 'files' in get_deps_dict
125
126 buff = StringIO()
127
128 for c in spec.constants:
129 buff.write("%s %s=%s\n"%(c.type, c.name, c.val_text))
130 for type_, name in zip(spec.types, spec.names):
131 base_msg_type = roslib.msgs.base_msg_type(type_)
132 # md5 spec strips package names
133 if roslib.msgs.is_builtin(base_msg_type):
134 buff.write("%s %s\n"%(type_, name))
135 else:
136 # recursively generate md5 for subtype. have to build up
137 # dependency representation for subtype in order to
138 # generate md5
139
140 # - ugly special-case handling of Header
141 if base_msg_type == roslib.msgs.HEADER:
142 base_msg_type = _header_type_name
143
144 sub_pkg, _ = roslib.names.package_resource_name(base_msg_type)
145 sub_pkg = sub_pkg or package
146 sub_spec = roslib.msgs.get_registered(base_msg_type, package)
147 sub_deps = get_dependencies(sub_spec, sub_pkg, compute_files=compute_files)
148 sub_md5 = compute_md5(sub_deps)
149 buff.write("%s %s\n"%(sub_md5, name))
150
151 return buff.getvalue().strip() # remove trailing new line
152
153 def _compute_hash(get_deps_dict, hash):
154 """
155 subroutine of compute_md5()
156 @param get_deps_dict: dictionary returned by get_dependencies call
157 @type get_deps_dict: dict
158 @param hash: hash instance
159 @type hash: hash instance
160 """
161 # accumulate the hash
162 # - root file
163 from roslib.msgs import MsgSpec
164 from roslib.srvs import SrvSpec
165 spec = get_deps_dict['spec']
166 if isinstance(spec, MsgSpec):
167 hash.update(compute_md5_text(get_deps_dict, spec))
168 elif isinstance(spec, SrvSpec):
169 hash.update(compute_md5_text(get_deps_dict, spec.request))
170 hash.update(compute_md5_text(get_deps_dict, spec.response))
171 else:
172 raise Exception("[%s] is not a message or service"%spec)
173 return hash.hexdigest()
174
175 def _compute_hash_v1(get_deps_dict, hash):
176 """
177 subroutine of compute_md5_v1()
178 @param get_deps_dict: dictionary returned by get_dependencies call
179 @type get_deps_dict: dict
180 @param hash: hash instance
181 @type hash: hash instance
182 """
183 uniquedeps = get_deps_dict['uniquedeps']
184 spec = get_deps_dict['spec']
185 # accumulate the hash
186 # - root file
187 hash.update(spec.text)
188 # - dependencies
189 for d in uniquedeps:
190 hash.update(roslib.msgs.get_registered(d).text)
191 return hash.hexdigest()
192
193 def compute_md5_v1(get_deps_dict):
194 """
195 Compute original V1 md5 hash for message/service. This was replaced with V2 in ROS 0.6.
196 @param get_deps_dict: dictionary returned by get_dependencies call
197 @type get_deps_dict: dict
198 @return: md5 hash
199 @rtype: str
200 """
201 import hashlib
202 return _compute_hash_v1(get_deps_dict, hashlib.md5())
203
204 def compute_md5(get_deps_dict):
205 """
206 Compute md5 hash for message/service
207 @param get_deps_dict dict: dictionary returned by get_dependencies call
208 @type get_deps_dict: dict
209 @return: md5 hash
210 @rtype: str
211 """
212 try:
213 # md5 is deprecated in Python 2.6 in favor of hashlib, but hashlib is
214 # unavailable in Python 2.4
215 import hashlib
216 return _compute_hash(get_deps_dict, hashlib.md5())
217 except ImportError:
218 import md5
219 return _compute_hash(get_deps_dict, md5.new())
220
221 ## alias
222 compute_md5_v2 = compute_md5
223
224 def compute_full_text(get_deps_dict):
225 """
226 Compute full text of message/service, including text of embedded
227 types. The text of the main msg/srv is listed first. Embedded
228 msg/srv files are denoted first by an 80-character '=' separator,
229 followed by a type declaration line,'MSG: pkg/type', followed by
230 the text of the embedded type.
231
232 @param get_deps_dict dict: dictionary returned by get_dependencies call
233 @type get_deps_dict: dict
234 @return: concatenated text for msg/srv file and embedded msg/srv types.
235 @rtype: str
236 """
237 buff = StringIO()
238 sep = '='*80+'\n'
239
240 # write the text of the top-level type
241 buff.write(get_deps_dict['spec'].text)
242 buff.write('\n')
243 # append the text of the dependencies (embedded types)
244 for d in get_deps_dict['uniquedeps']:
245 buff.write(sep)
246 buff.write("MSG: %s\n"%d)
247 buff.write(roslib.msgs.get_registered(d).text)
248 buff.write('\n')
249 # #1168: remove the trailing \n separator that is added by the concatenation logic
250 return buff.getvalue()[:-1]
251
252 def get_file_dependencies(f, stdout=sys.stdout, stderr=sys.stderr):
253 """
254 Compute dependencies of the specified message/service file
255 @param f: message or service file to get dependencies for
256 @type f: str
257 @param stdout pipe: stdout pipe
258 @type stdout: file
259 @param stderr pipe: stderr pipe
260 @type stderr: file
261 @return: 'files': list of files that \a file depends on,
262 'deps': list of dependencies by type, 'spec': Msgs/Srvs
263 instance.
264 @rtype: dict
265 """
266 package = rospkg.get_package_name(f)
267 spec = None
268 if f.endswith(roslib.msgs.EXT):
269 _, spec = roslib.msgs.load_from_file(f)
270 elif f.endswith(roslib.srvs.EXT):
271 _, spec = roslib.srvs.load_from_file(f)
272 else:
273 raise Exception("[%s] does not appear to be a message or service"%spec)
274 return get_dependencies(spec, package, stdout, stderr)
275
276 def get_dependencies(spec, package, compute_files=True, stdout=sys.stdout, stderr=sys.stderr):
277 """
278 Compute dependencies of the specified Msgs/Srvs
279 @param spec: message or service instance
280 @type spec: L{roslib.msgs.MsgSpec}/L{roslib.srvs.SrvSpec}
281 @param package: package name
282 @type package: str
283 @param stdout: (optional) stdout pipe
284 @type stdout: file
285 @param stderr: (optional) stderr pipe
286 @type stderr: file
287 @param compute_files: (optional, default=True) compute file
288 dependencies of message ('files' key in return value)
289 @type compute_files: bool
290 @return: dict:
291 * 'files': list of files that \a file depends on
292 * 'deps': list of dependencies by type
293 * 'spec': Msgs/Srvs instance.
294 * 'uniquedeps': list of dependencies with duplicates removed,
295 * 'package': package that dependencies were generated relative to.
296 @rtype: dict
297 """
298
299 # #518: as a performance optimization, we're going to manually control the loading
300 # of msgs instead of doing package-wide loads.
301
302 #we're going to manipulate internal apis of msgs, so have to
303 #manually init
304 roslib.msgs._init()
305
306 deps = []
307 try:
308 rospack = rospkg.RosPack()
309 if isinstance(spec, roslib.msgs.MsgSpec):
310 _add_msgs_depends(rospack, spec, deps, package)
311 elif isinstance(spec, roslib.srvs.SrvSpec):
312 _add_msgs_depends(rospack, spec.request, deps, package)
313 _add_msgs_depends(rospack, spec.response, deps, package)
314 else:
315 raise MsgSpecException("spec does not appear to be a message or service")
316 except KeyError, e:
317 raise MsgSpecException("Cannot load type %s. Perhaps the package is missing a dependency."%(str(e)))
318
319 # convert from type names to file names
320
321 if compute_files:
322 files = {}
323 for d in set(deps):
324 d_pkg, t = roslib.names.package_resource_name(d)
325 d_pkg = d_pkg or package # convert '' -> local package
326 files[d] = roslib.msgs.msg_file(d_pkg, t)
327 else:
328 files = None
329
330 # create unique dependency list
331 uniquedeps = []
332 for d in deps:
333 if not d in uniquedeps:
334 uniquedeps.append(d)
335
336 if compute_files:
337 return { 'files': files, 'deps': deps, 'spec': spec, 'package': package, 'uniquedeps': uniquedeps }
338 else:
339 return { 'deps': deps, 'spec': spec, 'package': package, 'uniquedeps': uniquedeps }
340
341
342
0 #! /usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 """
34 Python path loader for python scripts and applications. Paths are
35 derived from dependency structure declared in ROS manifest files.
36 """
37
38 import os
39 import sys
40
41 import rospkg
42
43 # bootstrapped keeps track of which packages we've loaded so we don't
44 # update the path multiple times
45 _bootstrapped = []
46 # _rospack is our cache of ROS package data
47 _rospack = rospkg.RosPack()
48
49 def get_depends(package, rospack):
50 vals = rospack.get_depends(package, implicit=True)
51 return [v for v in vals if not rospack.get_manifest(v).is_catkin]
52
53 def load_manifest(package_name, bootstrap_version="0.7"):
54 """
55 Update the Python sys.path with package's dependencies
56
57 :param package_name: name of the package that load_manifest() is being called from, ``str``
58 """
59 if package_name in _bootstrapped:
60 return
61 sys.path = _generate_python_path(package_name, _rospack) + sys.path
62
63 def _append_package_paths(manifest_, paths, pkg_dir):
64 """
65 Added paths for package to paths
66 :param manifest_: package manifest, ``Manifest``
67 :param pkg_dir: package's filesystem directory path, ``str``
68 :param paths: list of paths, ``[str]``
69 """
70 exports = manifest_.get_export('python','path')
71 if exports:
72 for export in exports:
73 if ':' in export:
74 export = export.split(':')
75 else:
76 export = [export]
77 for e in export:
78 paths.append(e.replace('${prefix}', pkg_dir))
79 else:
80 dirs = [os.path.join(pkg_dir, d) for d in ['src', 'lib']]
81 paths.extend([d for d in dirs if os.path.isdir(d)])
82
83 def _generate_python_path(pkg, rospack):
84 """
85 Recursive subroutine for building dependency list and python path
86 :raises: :exc:`rospkg.ResourceNotFound` If an error occurs while attempting to load package or dependencies
87 """
88 if pkg in _bootstrapped:
89 return []
90
91 # short-circuit if this is a catkin-ized package
92 m = rospack.get_manifest(pkg)
93 if m.is_catkin:
94 _bootstrapped.append(pkg)
95 return []
96
97 packages = get_depends(pkg, rospack)
98 packages.append(pkg)
99
100 paths = []
101 try:
102 for p in packages:
103 m = rospack.get_manifest(p)
104 d = rospack.get_path(p)
105 _append_package_paths(m, paths, d)
106 _bootstrapped.append(p)
107 except:
108 if pkg in _bootstrapped:
109 _bootstrapped.remove(pkg)
110 raise
111 return paths
0 #! /usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34 # $Author$
35
36 """
37 Warning: do not use this library. It is unstable and most of the routines
38 here have been superceded by other libraries (e.g. rospkg). These
39 routines will likely be *deleted* in future releases.
40 """
41
42 import sys
43 import os
44 import getopt
45
46 import roslib.packages
47
48 MANIFEST_FILE = 'manifest.xml'
49
50 import roslib.manifestlib
51 # re-export symbols for backwards compatibility
52 from roslib.manifestlib import ManifestException, Depend, Export, ROSDep, VersionControl
53
54 class Manifest(roslib.manifestlib._Manifest):
55 """
56 Object representation of a ROS manifest file
57 """
58 __slots__ = []
59 def __init__(self):
60 """
61 Initialize new empty manifest.
62 """
63 super(Manifest, self).__init__('package')
64
65 def get_export(self, tag, attr):
66 """
67 @return: exports that match the specified tag and attribute, e.g. 'python', 'path'
68 @rtype: [L{Export}]
69 """
70 return [e.get(attr) for e in self.exports if e.tag == tag if e.get(attr) is not None]
71
72 def _manifest_file_by_dir(package_dir, required=True, env=None):
73 """
74 @param package_dir: path to package directory
75 @type package_dir: str
76 @param env: environment dictionary
77 @type env: dict
78 @param required: require that the directory exist
79 @type required: bool
80 @return: path to manifest file of package
81 @rtype: str
82 @raise InvalidROSPkgException: if required is True and manifest file cannot be located
83 """
84 if env is None:
85 env = os.environ
86 try:
87 p = os.path.join(package_dir, MANIFEST_FILE)
88 if not required and not os.path.exists(p):
89 return p
90 if not os.path.isfile(p):
91 raise roslib.packages.InvalidROSPkgException("""
92 Package '%(package_dir)s' is improperly configured: no manifest file is present.
93 """%locals())
94 return p
95 except roslib.packages.InvalidROSPkgException as e:
96 if required:
97 raise
98
99 def manifest_file(package, required=True, env=None):
100 """
101 @param package str: package name
102 @type package: str
103 @param env: override os.environ dictionary
104 @type env: dict
105 @param required: require that the directory exist
106 @type required: bool
107 @return: path to manifest file of package
108 @rtype: str
109 @raise InvalidROSPkgException: if required is True and manifest file cannot be located
110 """
111 # ros_root needs to be determined from the environment or else
112 # everything breaks when trying to launch nodes via ssh where the
113 # path isn't setup correctly.
114 if env is None:
115 env = os.environ
116 d = roslib.packages.get_pkg_dir(package, required, ros_root=env['ROS_ROOT'])
117 return _manifest_file_by_dir(d, required=required, env=env)
118
119 def load_manifest(package):
120 """
121 Load manifest for specified package.
122 @param pacakge: package name
123 @type package: str
124 @return: Manifest instance
125 @rtype: L{Manifest}
126 @raise InvalidROSPkgException: if package is unknown
127 """
128 return parse_file(manifest_file(package))
129
130 def parse_file(file):
131 """
132 Parse manifest.xml file
133 @param file: manifest.xml file path
134 @type file: str
135 @return: Manifest instance
136 @rtype: L{Manifest}
137 """
138 return roslib.manifestlib.parse_file(Manifest(), file)
139
140 def parse(string, filename='string'):
141 """
142 Parse manifest.xml string contents
143 @param string: manifest.xml contents
144 @type string: str
145 @return: Manifest instance
146 @rtype: L{Manifest}
147 """
148 v = roslib.manifestlib.parse(Manifest(), string, filename)
149 if v.version:
150 raise ManifestException("<version> tag is not valid in a package manifest.xml file")
151 return v
0 #! /usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34 # $Author$
35
36 """
37 Internal library for processing 'manifest' files, i.e. manifest.xml and stack.xml.
38 For external code apis, see L{roslib.manifest} and L{roslib.stack_manifest}.
39 """
40
41 import sys
42 import os
43 import xml.dom
44 import xml.dom.minidom as dom
45
46 import roslib.exceptions
47
48 # stack.xml and manifest.xml have the same internal tags right now
49 REQUIRED = ['author', 'license']
50 ALLOWXHTML = ['description']
51 OPTIONAL = ['logo', 'url', 'brief', 'description', 'status',
52 'notes', 'depend', 'rosdep', 'export', 'review',
53 'versioncontrol', 'platform', 'version', 'rosbuild2',
54 'catkin']
55 VALID = REQUIRED + OPTIONAL
56
57 class ManifestException(roslib.exceptions.ROSLibException): pass
58
59 def get_nodes_by_name(n, name):
60 return [t for t in n.childNodes if t.nodeType == t.ELEMENT_NODE and t.tagName == name]
61
62 def check_optional(name, allowXHTML=False, merge_multiple=False):
63 """
64 Validator for optional elements.
65 @raise ManifestException: if validation fails
66 """
67 def check(n, filename):
68 n = get_nodes_by_name(n, name)
69 if len(n) > 1 and not merge_multiple:
70 raise ManifestException("Invalid manifest file: must have a single '%s' element"%name)
71 if n:
72 values = []
73 for child in n:
74 if allowXHTML:
75 values.append(''.join([x.toxml() for x in child.childNodes]))
76 else:
77 values.append(_get_text(child.childNodes).strip())
78 return ', '.join(values)
79 return check
80
81 def check_required(name, allowXHTML=False, merge_multiple=False):
82 """
83 Validator for required elements.
84 @raise ManifestException: if validation fails
85 """
86 def check(n, filename):
87 n = get_nodes_by_name(n, name)
88 if not n:
89 #print >> sys.stderr, "Invalid manifest file[%s]: missing required '%s' element"%(filename, name)
90 return ''
91 if len(n) != 1 and not merge_multiple:
92 raise ManifestException("Invalid manifest file: must have only one '%s' element"%name)
93 values = []
94 for child in n:
95 if allowXHTML:
96 values.append(''.join([x.toxml() for x in child.childNodes]))
97 else:
98 values.append(_get_text(child.childNodes).strip())
99 return ', '.join(values)
100 return check
101
102 def check_platform(name):
103 """
104 Validator for manifest platform.
105 @raise ManifestException: if validation fails
106 """
107 def check(n, filename):
108 platforms = get_nodes_by_name(n, name)
109 try:
110 vals = [(p.attributes['os'].value, p.attributes['version'].value, p.getAttribute('notes')) for p in platforms]
111 except KeyError as e:
112 raise ManifestException("<platform> tag is missing required '%s' attribute"%str(e))
113 return [Platform(*v) for v in vals]
114 return check
115
116 def check_depends(name):
117 """
118 Validator for manifest depends.
119 @raise ManifestException: if validation fails
120 """
121 def check(n, filename):
122 nodes = get_nodes_by_name(n, name)
123 # TDS 20110419: this is a hack.
124 # rosbuild2 has a <depend thirdparty="depname"/> tag,
125 # which is confusing this subroutine with
126 # KeyError: 'package'
127 # for now, explicitly don't consider thirdparty depends
128 depends = [e.attributes for e in nodes if 'thirdparty' not in e.attributes.keys()]
129 try:
130 packages = [d['package'].value for d in depends]
131 except KeyError:
132 raise ManifestException("Invalid manifest file: depends is missing 'package' attribute")
133
134 return [Depend(p) for p in packages]
135 return check
136
137 def check_stack_depends(name):
138 """
139 Validator for stack depends.
140 @raise ManifestException: if validation fails
141 """
142 def check(n, filename):
143 nodes = get_nodes_by_name(n, name)
144 depends = [e.attributes for e in nodes]
145 packages = [d['stack'].value for d in depends]
146 return [StackDepend(p) for p in packages]
147 return check
148
149 def check_rosdeps(name):
150 """
151 Validator for stack rosdeps.
152 @raise ManifestException: if validation fails
153 """
154 def check(n, filename):
155 nodes = get_nodes_by_name(n, name)
156 rosdeps = [e.attributes for e in nodes]
157 names = [d['name'].value for d in rosdeps]
158 return [ROSDep(n) for n in names]
159 return check
160
161 def _attrs(node):
162 attrs = {}
163 for k in node.attributes.keys():
164 attrs[k] = node.attributes.get(k).value
165 return attrs
166
167 def check_exports(name):
168 def check(n, filename):
169 ret_val = []
170 for e in get_nodes_by_name(n, name):
171 elements = [c for c in e.childNodes if c.nodeType == c.ELEMENT_NODE]
172 ret_val.extend([Export(t.tagName, _attrs(t), _get_text(t.childNodes)) for t in elements])
173 return ret_val
174 return check
175
176 def check_versioncontrol(name):
177 def check(n, filename):
178 e = get_nodes_by_name(n, name)
179 if not e:
180 return None
181 # note: 'url' isn't actually required, but as we only support type=svn it implicitly is for now
182 return VersionControl(e[0].attributes['type'].value, e[0].attributes['url'].value)
183 return check
184
185 def check(name, merge_multiple=False):
186 if name == 'depend':
187 return check_depends('depend')
188 elif name == 'export':
189 return check_exports('export')
190 elif name == 'versioncontrol':
191 return check_versioncontrol('versioncontrol')
192 elif name == 'rosdep':
193 return check_rosdeps('rosdep')
194 elif name == 'platform':
195 return check_platform('platform')
196 elif name in REQUIRED:
197 if name in ALLOWXHTML:
198 return check_required(name, True, merge_multiple)
199 return check_required(name, merge_multiple=merge_multiple)
200 elif name in OPTIONAL:
201 if name in ALLOWXHTML:
202 return check_optional(name, True, merge_multiple)
203 return check_optional(name, merge_multiple=merge_multiple)
204
205 class Export(object):
206 """
207 Manifest 'export' tag
208 """
209
210 def __init__(self, tag, attrs, str):
211 """
212 Create new export instance.
213 @param tag: name of the XML tag
214 @type tag: str
215 @param attrs: dictionary of XML attributes for this export tag
216 @type attrs: dict
217 @param str: string value contained by tag, if any
218 @type str: str
219 """
220 self.tag = tag
221 self.attrs = attrs
222 self.str = str
223
224 def get(self, attr):
225 """
226 @return: value of attribute or None if attribute not set
227 @rtype: str
228 """
229 return self.attrs.get(attr, None)
230 def xml(self):
231 """
232 @return: export instance represented as manifest XML
233 @rtype: str
234 """
235 attrs = ' '.join([' %s="%s"'%(k,v) for k,v in self.attrs.items()]) #py3k
236 if self.str:
237 return '<%s%s>%s</%s>'%(self.tag, attrs, self.str, self.tag)
238 else:
239 return '<%s%s />'%(self.tag, attrs)
240
241 class Platform(object):
242 """
243 Manifest 'platform' tag
244 """
245 __slots__ = ['os', 'version', 'notes']
246
247 def __init__(self, os, version, notes=None):
248 """
249 Create new depend instance.
250 @param os: OS name. must be non-empty
251 @type os: str
252 @param version: OS version. must be non-empty
253 @type version: str
254 @param notes: (optional) notes about platform support
255 @type notes: str
256 """
257 if not os:
258 raise ValueError("bad 'os' attribute")
259 if not version:
260 raise ValueError("bad 'version' attribute")
261 self.os = os
262 self.version = version
263 self.notes = notes
264
265 def __str__(self):
266 return "%s %s"%(self.os, self.version)
267 def __repr__(self):
268 return "%s %s"%(self.os, self.version)
269 def __eq__(self, obj):
270 """
271 Override equality test. notes *are* considered in the equality test.
272 """
273 if not isinstance(obj, Platform):
274 return False
275 return self.os == obj.os and self.version == obj.version and self.notes == obj.notes
276 def xml(self):
277 """
278 @return: instance represented as manifest XML
279 @rtype: str
280 """
281 if self.notes is not None:
282 return '<platform os="%s" version="%s" notes="%s"/>'%(self.os, self.version, self.notes)
283 else:
284 return '<platform os="%s" version="%s"/>'%(self.os, self.version)
285
286 class Depend(object):
287 """
288 Manifest 'depend' tag
289 """
290 __slots__ = ['package']
291
292 def __init__(self, package):
293 """
294 Create new depend instance.
295 @param package: package name. must be non-empty
296 @type package: str
297 """
298 if not package:
299 raise ValueError("bad 'package' attribute")
300 self.package = package
301 def __str__(self):
302 return self.package
303 def __repr__(self):
304 return self.package
305 def __eq__(self, obj):
306 if not isinstance(obj, Depend):
307 return False
308 return self.package == obj.package
309 def xml(self):
310 """
311 @return: depend instance represented as manifest XML
312 @rtype: str
313 """
314 return '<depend package="%s" />'%self.package
315
316 class StackDepend(object):
317 """
318 Stack Manifest 'depend' tag
319 """
320 __slots__ = ['stack', 'annotation']
321
322 def __init__(self, stack):
323 """
324 @param stack: stack name. must be non-empty
325 @type stack: str
326 """
327 if not stack:
328 raise ValueError("bad 'stack' attribute")
329 self.stack = stack
330 self.annotation = None
331
332 def __str__(self):
333 return self.stack
334 def __repr__(self):
335 return self.stack
336 def __eq__(self, obj):
337 if not isinstance(obj, StackDepend):
338 return False
339 return self.stack == obj.stack
340 def xml(self):
341 """
342 @return: stack depend instance represented as stack manifest XML
343 @rtype: str
344 """
345 if self.annotation:
346 return '<depend stack="%s" /> <!-- %s -->'%(self.stack, self.annotation)
347 else:
348 return '<depend stack="%s" />'%self.stack
349
350 class ROSDep(object):
351 """
352 Manifest 'rosdep' tag
353 """
354 __slots__ = ['name',]
355
356 def __init__(self, name):
357 """
358 Create new rosdep instance.
359 @param name: dependency name. Must be non-empty.
360 @type name: str
361 """
362 if not name:
363 raise ValueError("bad 'name' attribute")
364 self.name = name
365 def xml(self):
366 """
367 @return: rosdep instance represented as manifest XML
368 @rtype: str
369 """
370 return '<rosdep name="%s" />'%self.name
371
372 class VersionControl(object):
373 """
374 Manifest 'versioncontrol' tag
375 """
376 __slots__ = ['type', 'url']
377
378 def __init__(self, type_, url):
379 """
380 @param type_: version control type (e.g. 'svn'). must be non empty
381 @type type_: str
382 @param url: URL associated with version control. must be non empty
383 @type url: str
384 """
385 if not type_ or not isinstance(type_, basestring):
386 raise ValueError("bad 'type' attribute")
387 if not url is None and not isinstance(url, basestring):
388 raise ValueError("bad 'url' attribute")
389 self.type = type_
390 self.url = url
391 def xml(self):
392 """
393 @return: versioncontrol instance represented as manifest XML
394 @rtype: str
395 """
396 if self.url:
397 return '<versioncontrol type="%s" url="%s" />'%(self.type, self.url)
398 else:
399 return '<versioncontrol type="%s" />'%self.type
400
401 class _Manifest(object):
402 """
403 Object representation of a ROS manifest file
404 """
405 __slots__ = ['description', 'brief', \
406 'author', 'license', 'license_url', 'url', \
407 'depends', 'rosdeps','platforms',\
408 'logo', 'exports', 'version',\
409 'versioncontrol', 'status', 'notes',\
410 'unknown_tags',\
411 '_type']
412 def __init__(self, _type='package'):
413 self.description = self.brief = self.author = \
414 self.license = self.license_url = \
415 self.url = self.logo = self.status = \
416 self.version = self.notes = ''
417 self.depends = []
418 self.rosdeps = []
419 self.exports = []
420 self.platforms = []
421 self._type = _type
422
423 # store unrecognized tags during parsing
424 self.unknown_tags = []
425
426 def __str__(self):
427 return self.xml()
428 def get_export(self, tag, attr):
429 """
430 @return: exports that match the specified tag and attribute, e.g. 'python', 'path'
431 @rtype: [L{Export}]
432 """
433 return [e.get(attr) for e in self.exports if e.tag == tag if e.get(attr) is not None]
434 def xml(self):
435 """
436 @return: Manifest instance as ROS XML manifest
437 @rtype: str
438 """
439 if not self.brief:
440 desc = " <description>%s</description>"%self.description
441 else:
442 desc = ' <description brief="%s">%s</description>'%(self.brief, self.description)
443 author = " <author>%s</author>"%self.author
444 if self.license_url:
445 license = ' <license url="%s">%s</license>'%(self.license_url, self.license)
446 else:
447 license = " <license>%s</license>"%self.license
448 versioncontrol = url = logo = exports = version = ""
449 if self.url:
450 url = " <url>%s</url>"%self.url
451 if self.version:
452 version = " <version>%s</version>"%self.version
453 if self.logo:
454 logo = " <logo>%s</logo>"%self.logo
455 depends = '\n'.join([" %s"%d.xml() for d in self.depends])
456 rosdeps = '\n'.join([" %s"%rd.xml() for rd in self.rosdeps])
457 platforms = '\n'.join([" %s"%p.xml() for p in self.platforms])
458 if self.exports:
459 exports = ' <export>\n' + '\n'.join([" %s"%e.xml() for e in self.exports]) + ' </export>'
460 if self.versioncontrol:
461 versioncontrol = " %s"%self.versioncontrol.xml()
462 if self.status or self.notes:
463 review = ' <review status="%s" notes="%s" />'%(self.status, self.notes)
464
465
466 fields = filter(lambda x: x,
467 [desc, author, license, review, url, logo, depends,
468 rosdeps, platforms, exports, versioncontrol, version])
469 return "<%s>\n"%self._type + "\n".join(fields) + "\n</%s>"%self._type
470
471 def _get_text(nodes):
472 """
473 DOM utility routine for getting contents of text nodes
474 """
475 return "".join([n.data for n in nodes if n.nodeType == n.TEXT_NODE])
476
477 def parse_file(m, file):
478 """
479 Parse manifest file (package, stack)
480 @param m: field to populate
481 @type m: L{_Manifest}
482 @param file: manifest.xml file path
483 @type file: str
484 @return: return m, populated with parsed fields
485 @rtype: L{_Manifest}
486 """
487 if not file:
488 raise ValueError("Missing manifest file argument")
489 if not os.path.isfile(file):
490 raise ValueError("Invalid/non-existent manifest file: %s"%file)
491 with open(file, 'r') as f:
492 text = f.read()
493 try:
494 return parse(m, text, file)
495 except ManifestException as e:
496 raise ManifestException("Invalid manifest file [%s]: %s"%(os.path.abspath(file), e))
497
498 def parse(m, string, filename='string'):
499 """
500 Parse manifest.xml string contents
501 @param string: manifest.xml contents
502 @type string: str
503 @param m: field to populate
504 @type m: L{_Manifest}
505 @return: return m, populated with parsed fields
506 @rtype: L{_Manifest}
507 """
508 try:
509 d = dom.parseString(string)
510 except Exception as e:
511 raise ManifestException("invalid XML: %s"%e)
512
513 p = get_nodes_by_name(d, m._type)
514 if len(p) != 1:
515 raise ManifestException("manifest must have a single '%s' element"%m._type)
516 p = p[0]
517 m.description = check('description')(p, filename)
518 m.brief = ''
519 try:
520 tag = get_nodes_by_name(p, 'description')[0]
521 m.brief = tag.getAttribute('brief') or ''
522 except:
523 # means that 'description' tag is missing
524 pass
525 #TODO: figure out how to multiplex
526 if m._type == 'package':
527 m.depends = check_depends('depend')(p, filename)
528 elif m._type == 'stack':
529 m.depends = check_stack_depends('depend')(p, filename)
530 elif m._type == 'app':
531 # not implemented yet
532 pass
533 m.rosdeps = check('rosdep')(p, filename)
534 m.platforms = check('platform')(p, filename)
535 m.exports = check('export')(p, filename)
536 m.versioncontrol = check('versioncontrol')(p,filename)
537 m.license = check('license')(p, filename)
538 m.license_url = ''
539 try:
540 tag = get_nodes_by_name(p, 'license')[0]
541 m.license_url = tag.getAttribute('url') or ''
542 except:
543 pass #manifest is missing required 'license' tag
544
545 m.status='unreviewed'
546 try:
547 tag = get_nodes_by_name(p, 'review')[0]
548 m.status=tag.getAttribute('status') or ''
549 except:
550 pass #manifest is missing optional 'review status' tag
551
552 m.notes=''
553 try:
554 tag = get_nodes_by_name(p, 'review')[0]
555 m.notes=tag.getAttribute('notes') or ''
556 except:
557 pass #manifest is missing optional 'review notes' tag
558
559 m.author = check('author', True)(p, filename)
560 m.url = check('url')(p, filename)
561 m.version = check('version')(p, filename)
562 m.logo = check('logo')(p, filename)
563
564 # do some validation on what we just parsed
565 if m._type == 'stack':
566 if m.exports:
567 raise ManifestException("stack manifests are not allowed to have exports")
568 if m.rosdeps:
569 raise ManifestException("stack manifests are not allowed to have rosdeps")
570
571 # store unrecognized tags
572 m.unknown_tags = [e for e in p.childNodes if e.nodeType == e.ELEMENT_NODE and e.tagName not in VALID]
573 return m
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 # Revision $Id$
33
34 """
35 Support library for Python autogenerated message files. This defines
36 the Message base class used by genmsg_py as well as support
37 libraries for type checking and retrieving message classes by type
38 name.
39 """
40
41 import os
42 import sys
43 import rospkg
44 import roslib
45
46 import genmsg
47 import genpy.message #for wrapping get_message_class, get_service_class
48
49 # forward a bunch of old symbols from genpy for backwards compat
50 from genpy import Message, DeserializationError, SerializationError, \
51 Time, Duration, TVal
52 from genpy.message import get_printable_message_args, fill_message_args
53 from genpy.message import check_type, strify_message
54
55 def _get_message_or_service_class(type_str, message_type, reload_on_error=False):
56 ## parse package and local type name for import
57 package, base_type = genmsg.package_resource_name(message_type)
58 if not package:
59 if base_type == 'Header':
60 package = 'std_msgs'
61 else:
62 raise ValueError("message type is missing package name: %s"%str(message_type))
63 pypkg = val = None
64 try:
65 # bootstrap our sys.path
66 roslib.launcher.load_manifest(package)
67 # import the package and return the class
68 pypkg = __import__('%s.%s'%(package, type_str))
69 val = getattr(getattr(pypkg, type_str), base_type)
70 except rospkg.ResourceNotFound:
71 val = None
72 except ImportError:
73 val = None
74 except AttributeError:
75 val = None
76
77 # this logic is mainly to support rosh, so that a user doesn't
78 # have to exit a shell just because a message wasn't built yet
79 if val is None and reload_on_error:
80 try:
81 if pypkg:
82 reload(pypkg)
83 val = getattr(getattr(pypkg, type_str), base_type)
84 except:
85 val = None
86 return val
87
88 ## cache for get_message_class
89 _message_class_cache = {}
90
91 ## cache for get_service_class
92 _service_class_cache = {}
93
94 def get_message_class(message_type, reload_on_error=False):
95 if message_type in _message_class_cache:
96 return _message_class_cache[message_type]
97 # try w/o bootstrapping
98 cls = genpy.message.get_message_class(message_type, reload_on_error=reload_on_error)
99 if cls is None:
100 # try old loader w/ bootstrapping
101 cls = _get_message_or_service_class('msg', message_type, reload_on_error=reload_on_error)
102 if cls:
103 _message_class_cache[message_type] = cls
104 return cls
105
106 def get_service_class(service_type, reload_on_error=False):
107 if service_type in _service_class_cache:
108 return _service_class_cache[service_type]
109 cls = genpy.message.get_service_class(service_type, reload_on_error=reload_on_error)
110 # try w/o bootstrapping
111 if cls is None:
112 # try old loader w/ bootstrapping
113 cls = _get_message_or_service_class('srv', service_type, reload_on_error=reload_on_error)
114 if cls:
115 _service_class_cache[service_type] = cls
116 return cls
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 # Revision $Id$
33 # $Author$
34
35 from __future__ import print_function
36
37 """
38 Warning: do not use this library. It is unstable and most of the routines
39 here have been superceded by other libraries (e.g. genmsg). These
40 routines will likely be *deleted* in future releases.
41 """
42
43 try:
44 from cStringIO import StringIO # Python 2.x
45 except ImportError:
46 from io import StringIO # Python 3.x
47
48 import os
49 import sys
50 import string
51
52 import rospkg
53
54 import roslib.manifest
55 import roslib.packages
56 import roslib.names
57 import roslib.resources
58
59 VERBOSE = False
60
61 ## @return: True if msg-related scripts should print verbose output
62 def is_verbose():
63 return VERBOSE
64
65 ## set whether msg-related scripts should print verbose output
66 def set_verbose(v):
67 global VERBOSE
68 VERBOSE = v
69
70 EXT = '.msg'
71 SEP = '/' #e.g. std_msgs/String
72 ## character that designates a constant assignment rather than a field
73 CONSTCHAR = '='
74 COMMENTCHAR = '#'
75
76 class MsgSpecException(Exception): pass
77
78 #TODOXXX: unit test
79 def base_msg_type(type_):
80 """
81 Compute the base data type, e.g. for arrays, get the underlying array item type
82 @param type_: ROS msg type (e.g. 'std_msgs/String')
83 @type type_: str
84 @return: base type
85 @rtype: str
86 """
87 if type_ is None:
88 return None
89 if '[' in type_:
90 return type_[:type_.find('[')]
91 return type_
92
93 def resolve_type(type_, package_context):
94 """
95 Resolve type name based on current package context.
96
97 NOTE: in ROS Diamondback, 'Header' resolves to
98 'std_msgs/Header'. In previous releases, it resolves to
99 'roslib/Header' (REP 100).
100
101 e.g.::
102 resolve_type('String', 'std_msgs') -> 'std_msgs/String'
103 resolve_type('String[]', 'std_msgs') -> 'std_msgs/String[]'
104 resolve_type('std_msgs/String', 'foo') -> 'std_msgs/String'
105 resolve_type('uint16', 'std_msgs') -> 'uint16'
106 resolve_type('uint16[]', 'std_msgs') -> 'uint16[]'
107 """
108 bt = base_msg_type(type_)
109 if bt in BUILTIN_TYPES:
110 return type_
111 elif bt == 'Header':
112 return 'std_msgs/Header'
113 elif SEP in type_:
114 return type_
115 else:
116 return "%s%s%s"%(package_context, SEP, type_)
117
118 #NOTE: this assumes that we aren't going to support multi-dimensional
119
120 def parse_type(type_):
121 """
122 Parse ROS message field type
123 @param type_: ROS field type
124 @type type_: str
125 @return: base_type, is_array, array_length
126 @rtype: str, bool, int
127 @raise MsgSpecException: if type_ cannot be parsed
128 """
129 if not type_:
130 raise MsgSpecException("Invalid empty type")
131 if '[' in type_:
132 var_length = type_.endswith('[]')
133 splits = type_.split('[')
134 if len(splits) > 2:
135 raise MsgSpecException("Currently only support 1-dimensional array types: %s"%type_)
136 if var_length:
137 return type_[:-2], True, None
138 else:
139 try:
140 length = int(splits[1][:-1])
141 return splits[0], True, length
142 except ValueError:
143 raise MsgSpecException("Invalid array dimension: [%s]"%splits[1][:-1])
144 else:
145 return type_, False, None
146
147 ################################################################################
148 # name validation
149
150 def is_valid_msg_type(x):
151 """
152 @return: True if the name is a syntatically legal message type name
153 @rtype: bool
154 """
155 if not x or len(x) != len(x.strip()):
156 return False
157 base = base_msg_type(x)
158 if not roslib.names.is_legal_resource_name(base):
159 return False
160 #parse array indicies
161 x = x[len(base):]
162 state = 0
163 i = 0
164 for c in x:
165 if state == 0:
166 if c != '[':
167 return False
168 state = 1 #open
169 elif state == 1:
170 if c == ']':
171 state = 0 #closed
172 else:
173 try:
174 string.atoi(c)
175 except:
176 return False
177 return state == 0
178
179 def is_valid_constant_type(x):
180 """
181 @return: True if the name is a legal constant type. Only simple types are allowed.
182 @rtype: bool
183 """
184 return x in PRIMITIVE_TYPES
185
186 def is_valid_msg_field_name(x):
187 """
188 @return: True if the name is a syntatically legal message field name
189 @rtype: bool
190 """
191 return roslib.names.is_legal_resource_base_name(x)
192
193 # msg spec representation ##########################################
194
195 class Constant(object):
196 """
197 Container class for holding a Constant declaration
198 """
199 __slots__ = ['type', 'name', 'val', 'val_text']
200
201 def __init__(self, type_, name, val, val_text):
202 """
203 @param type_: constant type
204 @type type_: str
205 @param name: constant name
206 @type name: str
207 @param val: constant value
208 @type val: str
209 @param val_text: Original text definition of \a val
210 @type val_text: str
211 """
212 if type is None or name is None or val is None or val_text is None:
213 raise ValueError('Constant must have non-None parameters')
214 self.type = type_
215 self.name = name.strip() #names are always stripped of whitespace
216 self.val = val
217 self.val_text = val_text
218
219 def __eq__(self, other):
220 if not isinstance(other, Constant):
221 return False
222 return self.type == other.type and self.name == other.name and self.val == other.val
223
224 def __repr__(self):
225 return "%s %s=%s"%(self.type, self.name, self.val)
226
227 def __str__(self):
228 return "%s %s=%s"%(self.type, self.name, self.val)
229
230 def _strify_spec(spec, buff=None, indent=''):
231 """
232 Convert spec into a string representation. Helper routine for MsgSpec.
233 @param indent: internal use only
234 @type indent: str
235 @param buff: internal use only
236 @type buff: StringIO
237 @return: string representation of spec
238 @rtype: str
239 """
240 if buff is None:
241 buff = StringIO()
242 for c in spec.constants:
243 buff.write("%s%s %s=%s\n"%(indent, c.type, c.name, c.val_text))
244 for type_, name in zip(spec.types, spec.names):
245 buff.write("%s%s %s\n"%(indent, type_, name))
246 base_type = base_msg_type(type_)
247 if not base_type in BUILTIN_TYPES:
248 subspec = get_registered(base_type)
249 _strify_spec(subspec, buff, indent + ' ')
250 return buff.getvalue()
251
252 class Field(object):
253 """
254 Container class for storing information about a single field in a MsgSpec
255
256 Contains:
257 name
258 type
259 base_type
260 is_array
261 array_len
262 is_builtin
263 is_header
264 """
265
266 def __init__(self, name, type):
267 self.name = name
268 self.type = type
269 (self.base_type, self.is_array, self.array_len) = parse_type(type)
270 self.is_header = is_header_type(self.base_type)
271 self.is_builtin = is_builtin(self.base_type)
272
273 def __repr__(self):
274 return "[%s, %s, %s, %s, %s]"%(self.name, self.type, self.base_type, self.is_array, self.array_len)
275
276 class MsgSpec(object):
277 """
278 Container class for storing loaded msg description files. Field
279 types and names are stored in separate lists with 1-to-1
280 correspondence. MsgSpec can also return an md5 of the source text.
281 """
282
283 def __init__(self, types, names, constants, text, full_name = '', short_name = '', package = ''):
284 """
285 @param types: list of field types, in order of declaration
286 @type types: [str]
287 @param names: list of field names, in order of declaration
288 @type names: [str]
289 @param constants: Constant declarations
290 @type constants: [L{Constant}]
291 @param text: text of declaration
292 @type text: str
293 @raise MsgSpecException: if spec is invalid (e.g. fields with the same name)
294 """
295 self.types = types
296 if len(set(names)) != len(names):
297 raise MsgSpecException("Duplicate field names in message: %s"%names)
298 self.names = names
299 self.constants = constants
300 assert len(self.types) == len(self.names), "len(%s) != len(%s)"%(self.types, self.names)
301 #Header.msg support
302 if (len(self.types)):
303 self.header_present = self.types[0] == HEADER and self.names[0] == 'header'
304 else:
305 self.header_present = False
306 self.text = text
307 self.full_name = full_name
308 self.short_name = short_name
309 self.package = package
310 self._parsed_fields = [Field(name, type) for (name, type) in zip(self.names, self.types)]
311
312 def fields(self):
313 """
314 @return: zip list of types and names (e.g. [('int32', 'x'), ('int32', 'y')]
315 @rtype: [(str,str),]
316 """
317 return list(zip(self.types, self.names)) #py3k
318
319 def parsed_fields(self):
320 """
321 @return: list of Field classes
322 @rtype: [Field,]
323 """
324 return self._parsed_fields
325
326 def has_header(self):
327 """
328 @return: True if msg decription contains a 'Header header'
329 declaration at the beginning
330 @rtype: bool
331 """
332 return self.header_present
333 def __eq__(self, other):
334 if not other or not isinstance(other, MsgSpec):
335 return False
336 return self.types == other.types and self.names == other.names and \
337 self.constants == other.constants and self.text == other.text
338 def __ne__(self, other):
339 if not other or not isinstance(other, MsgSpec):
340 return True
341 return not self.__eq__(other)
342
343 def __repr__(self):
344 if self.constants:
345 return "MsgSpec[%s, %s, %s]"%(repr(self.constants), repr(self.types), repr(self.names))
346 else:
347 return "MsgSpec[%s, %s]"%(repr(self.types), repr(self.names))
348
349 def __str__(self):
350 return _strify_spec(self)
351
352 # msg spec loading utilities ##########################################
353
354 def reinit():
355 """
356 Reinitialize roslib.msgs. This API is for message generators
357 (e.g. genpy) that need to re-initialize the registration table.
358 """
359 global _initialized , _loaded_packages
360 # unset the initialized state and unregister everything
361 _initialized = False
362 del _loaded_packages[:]
363 REGISTERED_TYPES.clear()
364 _init()
365
366 _initialized = False
367 def _init():
368 #lazy-init
369 global _initialized
370 if _initialized:
371 return
372
373 fname = '%s%s'%(HEADER, EXT)
374 std_msgs_dir = roslib.packages.get_pkg_dir('std_msgs')
375 if std_msgs_dir is None:
376 raise MsgSpecException("Unable to locate roslib: %s files cannot be loaded"%EXT)
377
378 header = os.path.join(std_msgs_dir, 'msg', fname)
379 if not os.path.isfile(header):
380 sys.stderr.write("ERROR: cannot locate %s. Expected to find it at '%s'\n"%(fname, header))
381 return False
382
383 # register Header under both contexted and de-contexted name
384 _, spec = load_from_file(header, '')
385 register(HEADER, spec)
386 register('std_msgs/'+HEADER, spec)
387 # backwards compat, REP 100
388 register('roslib/'+HEADER, spec)
389 for k, spec in EXTENDED_BUILTINS.items():
390 register(k, spec)
391
392 _initialized = True
393
394 # .msg file routines ##############################################################
395
396 def _msg_filter(f):
397 """
398 Predicate for filtering directory list. matches message files
399 @param f: filename
400 @type f: str
401 """
402 return os.path.isfile(f) and f.endswith(EXT)
403
404 # also used by doxymaker
405 def list_msg_types(package, include_depends):
406 """
407 List all messages in the specified package
408 @param package str: name of package to search
409 @param include_depends bool: if True, will also list messages in package dependencies
410 @return [str]: message type names
411 """
412 types = roslib.resources.list_package_resources(package, include_depends, 'msg', _msg_filter)
413 return [x[:-len(EXT)] for x in types]
414
415 def msg_file(package, type_):
416 """
417 Determine the file system path for the specified .msg
418 resource. .msg resource does not have to exist.
419
420 @param package: name of package .msg file is in
421 @type package: str
422 @param type_: type name of message, e.g. 'Point2DFloat32'
423 @type type_: str
424 @return: file path of .msg file in specified package
425 @rtype: str
426 """
427 return roslib.packages.resource_file(package, 'msg', type_+EXT)
428
429 def get_pkg_msg_specs(package):
430 """
431 List all messages that a package contains.
432
433 @param package: package to load messages from
434 @type package: str
435 @return: list of message type names and specs for package, as well as a list
436 of message names that could not be processed.
437 @rtype: [(str, L{MsgSpec}), [str]]
438 """
439 _init()
440 types = list_msg_types(package, False)
441 specs = [] #no fancy list comprehension as we want to show errors
442 failures = []
443 for t in types:
444 try:
445 typespec = load_from_file(msg_file(package, t), package)
446 specs.append(typespec)
447 except Exception as e:
448 failures.append(t)
449 print("ERROR: unable to load %s, %s"%(t, e))
450 return specs, failures
451
452 def load_package_dependencies(package, load_recursive=False):
453 """
454 Register all messages that the specified package depends on.
455
456 @param load_recursive: (optional) if True, load all dependencies,
457 not just direct dependencies. By default, this is false to
458 prevent packages from incorrectly inheriting dependencies.
459 @type load_recursive: bool
460 """
461 global _loaded_packages
462 _init()
463 if VERBOSE:
464 print("Load dependencies for package", package)
465
466 if not load_recursive:
467 manifest_file = roslib.manifest.manifest_file(package, True)
468 m = roslib.manifest.parse_file(manifest_file)
469 depends = [d.package for d in m.depends] # #391
470 else:
471 depends = rospkg.RosPack().get_depends(package, implicit=True)
472
473 msgs = []
474 failures = []
475 for d in depends:
476 if VERBOSE:
477 print("Load dependency", d)
478 #check if already loaded
479 # - we are dependent on manifest.getAll returning first-order dependencies first
480 if d in _loaded_packages or d == package:
481 continue
482 _loaded_packages.append(d)
483 specs, failed = get_pkg_msg_specs(d)
484 msgs.extend(specs)
485 failures.extend(failed)
486 for key, spec in msgs:
487 register(key, spec)
488
489 def load_package(package):
490 """
491 Load package into the local registered namespace. All messages found
492 in the package will be registered if they are successfully
493 loaded. This should only be done with one package (i.e. the 'main'
494 package) per Python instance.
495
496 @param package: package name
497 @type package: str
498 """
499 global _loaded_packages
500 _init()
501 if VERBOSE:
502 print("Load package", package)
503
504 #check if already loaded
505 # - we are dependent on manifest.getAll returning first-order dependencies first
506 if package in _loaded_packages:
507 if VERBOSE:
508 print("Package %s is already loaded"%package)
509 return
510
511 _loaded_packages.append(package)
512 specs, failed = get_pkg_msg_specs(package)
513 if VERBOSE:
514 print("Package contains the following messages: %s"%specs)
515 for key, spec in specs:
516 #register spec under both local and fully-qualified key
517 register(key, spec)
518 register(package + roslib.names.PRN_SEPARATOR + key, spec)
519
520 def _convert_val(type_, val):
521 """
522 Convert constant value declaration to python value. Does not do
523 type-checking, so ValueError or other exceptions may be raised.
524
525 @param type_: ROS field type
526 @type type_: str
527 @param val: string representation of constant
528 @type val: str:
529 @raise ValueError: if unable to convert to python representation
530 @raise MsgSpecException: if value exceeds specified integer width
531 """
532 if type_ in ['float32','float64']:
533 return float(val)
534 elif type_ in ['string']:
535 return val.strip() #string constants are always stripped
536 elif type_ in ['int8', 'uint8', 'int16','uint16','int32','uint32','int64','uint64', 'char', 'byte']:
537 # bounds checking
538 bits = [('int8', 8), ('uint8', 8), ('int16', 16),('uint16', 16),\
539 ('int32', 32),('uint32', 32), ('int64', 64),('uint64', 64),\
540 ('byte', 8), ('char', 8)]
541 b = [b for t, b in bits if t == type_][0]
542 import math
543 if type_[0] == 'u' or type_ == 'char':
544 lower = 0
545 upper = int(math.pow(2, b)-1)
546 else:
547 upper = int(math.pow(2, b-1)-1)
548 lower = -upper - 1 #two's complement min
549 val = int(val) #python will autocast to long if necessary
550 if val > upper or val < lower:
551 raise MsgSpecException("cannot coerce [%s] to %s (out of bounds)"%(val, type_))
552 return val
553 elif type_ == 'bool':
554 # TODO: need to nail down constant spec for bool
555 return True if eval(val) else False
556 raise MsgSpecException("invalid constant type: [%s]"%type_)
557
558 def load_by_type(msgtype, package_context=''):
559 """
560 Load message specification for specified type
561
562 @param package_context: package name to use for the type name or
563 '' to use the local (relative) naming convention.
564 @type package_context: str
565 @return: Message type name and message specification
566 @rtype: (str, L{MsgSpec})
567 """
568 pkg, basetype = roslib.names.package_resource_name(msgtype)
569 pkg = pkg or package_context # convert '' -> local package
570 try:
571 m_f = msg_file(pkg, basetype)
572 except roslib.packages.InvalidROSPkgException:
573 raise MsgSpecException("Cannot locate message type [%s], package [%s] does not exist"%(msgtype, pkg))
574 return load_from_file(m_f, pkg)
575
576 def load_from_string(text, package_context='', full_name='', short_name=''):
577 """
578 Load message specification from a string.
579 @param text: .msg text
580 @type text: str
581 @param package_context: package name to use for the type name or
582 '' to use the local (relative) naming convention.
583 @type package_context: str
584 @return: Message specification
585 @rtype: L{MsgSpec}
586 @raise MsgSpecException: if syntax errors or other problems are detected in file
587 """
588 types = []
589 names = []
590 constants = []
591 for orig_line in text.split('\n'):
592 l = orig_line.split(COMMENTCHAR)[0].strip() #strip comments
593 if not l:
594 continue #ignore empty lines
595 splits = [s for s in [x.strip() for x in l.split(" ")] if s] #split type/name, filter out empties
596 type_ = splits[0]
597 if not is_valid_msg_type(type_):
598 raise MsgSpecException("%s is not a legal message type"%type_)
599 if CONSTCHAR in l:
600 if not is_valid_constant_type(type_):
601 raise MsgSpecException("%s is not a legal constant type"%type_)
602 if type_ == 'string':
603 # strings contain anything to the right of the equals sign, there are no comments allowed
604 idx = orig_line.find(CONSTCHAR)
605 name = orig_line[orig_line.find(' ')+1:idx]
606 val = orig_line[idx+1:]
607 else:
608 splits = [x.strip() for x in ' '.join(splits[1:]).split(CONSTCHAR)] #resplit on '='
609 if len(splits) != 2:
610 raise MsgSpecException("Invalid declaration: %s"%l)
611 name = splits[0]
612 val = splits[1]
613 try:
614 val_converted = _convert_val(type_, val)
615 except Exception as e:
616 raise MsgSpecException("Invalid declaration: %s"%e)
617 constants.append(Constant(type_, name, val_converted, val.strip()))
618 else:
619 if len(splits) != 2:
620 raise MsgSpecException("Invalid declaration: %s"%l)
621 name = splits[1]
622 if not is_valid_msg_field_name(name):
623 raise MsgSpecException("%s is not a legal message field name"%name)
624 if package_context and not SEP in type_:
625 if not base_msg_type(type_) in RESERVED_TYPES:
626 #print "rewrite", type_, "to", "%s/%s"%(package_context, type_)
627 type_ = "%s/%s"%(package_context, type_)
628 types.append(type_)
629 names.append(name)
630 return MsgSpec(types, names, constants, text, full_name, short_name, package_context)
631
632 def load_from_file(file_path, package_context=''):
633 """
634 Convert the .msg representation in the file to a MsgSpec instance.
635 This does *not* register the object.
636 @param file_path: path of file to load from
637 @type file_path: str:
638 @param package_context: package name to prepend to type name or
639 '' to use local (relative) naming convention.
640 @type package_context: str
641 @return: Message type name and message specification
642 @rtype: (str, L{MsgSpec})
643 @raise MsgSpecException: if syntax errors or other problems are detected in file
644 """
645 if VERBOSE:
646 if package_context:
647 print("Load spec from", file_path, "into package [%s]"%package_context)
648 else:
649 print("Load spec from", file_path)
650
651 file_name = os.path.basename(file_path)
652 type_ = file_name[:-len(EXT)]
653 base_type_ = type_
654 # determine the type name
655 if package_context:
656 while package_context.endswith(SEP):
657 package_context = package_context[:-1] #strip message separators
658 type_ = "%s%s%s"%(package_context, SEP, type_)
659 if not roslib.names.is_legal_resource_name(type_):
660 raise MsgSpecException("%s: [%s] is not a legal type name"%(file_path, type_))
661
662 f = open(file_path, 'r')
663 try:
664 try:
665 text = f.read()
666 return (type_, load_from_string(text, package_context, type_, base_type_))
667 except MsgSpecException as e:
668 raise MsgSpecException('%s: %s'%(file_name, e))
669 finally:
670 f.close()
671
672 # data structures and builtins specification ###########################
673
674 # adjustable constants, in case we change our minds
675 HEADER = 'Header'
676 TIME = 'time'
677 DURATION = 'duration'
678
679 def is_header_type(type_):
680 """
681 @param type_: message type name
682 @type type_: str
683 @return: True if \a type_ refers to the ROS Header type
684 @rtype: bool
685 """
686 # for backwards compatibility, include roslib/Header. REP 100
687 return type_ in [HEADER, 'std_msgs/Header', 'roslib/Header']
688
689 # time and duration types are represented as aggregate data structures
690 # for the purposes of serialization from the perspective of
691 # roslib.msgs. genmsg_py will do additional special handling is required
692 # to convert them into rospy.msg.Time/Duration instances.
693
694 ## time as msg spec. time is unsigned
695 TIME_MSG = "uint32 secs\nuint32 nsecs"
696 ## duration as msg spec. duration is just like time except signed
697 DURATION_MSG = "int32 secs\nint32 nsecs"
698
699 ## primitive types are those for which we allow constants, i.e. have primitive representation
700 PRIMITIVE_TYPES = ['int8','uint8','int16','uint16','int32','uint32','int64','uint64','float32','float64',
701 'string',
702 'bool',
703 # deprecated:
704 'char','byte']
705 BUILTIN_TYPES = PRIMITIVE_TYPES + [TIME, DURATION]
706
707 def is_builtin(msg_type_name):
708 """
709 @param msg_type_name: name of message type
710 @type msg_type_name: str
711 @return: True if msg_type_name is a builtin/primitive type
712 @rtype: bool
713 """
714 return msg_type_name in BUILTIN_TYPES
715
716 ## extended builtins are builtin types that can be represented as MsgSpec instances
717 EXTENDED_BUILTINS = { TIME : load_from_string(TIME_MSG), DURATION: load_from_string(DURATION_MSG) }
718
719 RESERVED_TYPES = BUILTIN_TYPES + [HEADER]
720
721 REGISTERED_TYPES = { }
722 _loaded_packages = [] #keep track of packages so that we only load once (note: bug #59)
723
724 def is_registered(msg_type_name):
725 """
726 @param msg_type_name: name of message type
727 @type msg_type_name: str
728 @return: True if msg spec for specified msg type name is
729 registered. NOTE: builtin types are not registered.
730 @rtype: bool
731 """
732 return msg_type_name in REGISTERED_TYPES
733
734 def get_registered(msg_type_name, default_package=None):
735 """
736 @param msg_type_name: name of message type
737 @type msg_type_name: str
738 @return: msg spec for msg type name
739 @rtype: L{MsgSpec}
740 """
741 if msg_type_name in REGISTERED_TYPES:
742 return REGISTERED_TYPES[msg_type_name]
743 elif default_package:
744 # if msg_type_name has no package specifier, try with default package resolution
745 p, n = roslib.names.package_resource_name(msg_type_name)
746 if not p:
747 return REGISTERED_TYPES[roslib.names.resource_name(default_package, msg_type_name)]
748 raise KeyError(msg_type_name)
749
750 def register(msg_type_name, msg_spec):
751 """
752 Load MsgSpec into the type dictionary
753
754 @param msg_type_name: name of message type
755 @type msg_type_name: str
756 @param msg_spec: spec to load
757 @type msg_spec: L{MsgSpec}
758 """
759 if VERBOSE:
760 print("Register msg %s"%msg_type_name)
761 REGISTERED_TYPES[msg_type_name] = msg_spec
762
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 # Revision $Id$
33
34 """
35 Warning: do not use this library. It is unstable and most of the routines
36 here have been superceded by other libraries (e.g. genmsg). These
37 routines will likely be *deleted* in future releases.
38 """
39
40 import os
41 import sys
42
43 #TODO: deprecate PRN_SEPARATOR
44 PRN_SEPARATOR = '/'
45 TYPE_SEPARATOR = PRN_SEPARATOR #alias
46 SEP = '/'
47 GLOBALNS = '/'
48 PRIV_NAME = '~'
49 REMAP = ":="
50 ANYTYPE = '*'
51
52 if sys.hexversion > 0x03000000: #Python3
53 def isstring(s):
54 return isinstance(s, str) #Python 3.x
55 else:
56 def isstring(s):
57 """
58 Small helper version to check an object is a string in a way that works
59 for both Python 2 and 3
60 """
61 return isinstance(s, basestring) #Python 2.x
62
63 def get_ros_namespace(env=None, argv=None):
64 """
65 @param env: environment dictionary (defaults to os.environ)
66 @type env: dict
67 @param argv: command-line arguments (defaults to sys.argv)
68 @type argv: [str]
69 @return: ROS namespace of current program
70 @rtype: str
71 """
72 #we force command-line-specified namespaces to be globally scoped
73 if argv is None:
74 argv = sys.argv
75 for a in argv:
76 if a.startswith('__ns:='):
77 return make_global_ns(a[len('__ns:='):])
78 if env is None:
79 env = os.environ
80 return make_global_ns(env.get('ROS_NAMESPACE', GLOBALNS))
81
82 def make_caller_id(name):
83 """
84 Resolve a local name to the caller ID based on ROS environment settings (i.e. ROS_NAMESPACE)
85
86 @param name: local name to calculate caller ID from, e.g. 'camera', 'node'
87 @type name: str
88 @return: caller ID based on supplied local name
89 @rtype: str
90 """
91 return make_global_ns(ns_join(get_ros_namespace(), name))
92
93 def make_global_ns(name):
94 """
95 Convert name to a global name with a trailing namespace separator.
96
97 @param name: ROS resource name. Cannot be a ~name.
98 @type name: str
99 @return str: name as a global name, e.g. 'foo' -> '/foo/'.
100 This does NOT resolve a name.
101 @rtype: str
102 @raise ValueError: if name is a ~name
103 """
104 if is_private(name):
105 raise ValueError("cannot turn [%s] into a global name"%name)
106 if not is_global(name):
107 name = SEP + name
108 if name[-1] != SEP:
109 name = name + SEP
110 return name
111
112 def is_global(name):
113 """
114 Test if name is a global graph resource name.
115
116 @param name: must be a legal name in canonical form
117 @type name: str
118 @return: True if name is a globally referenced name (i.e. /ns/name)
119 @rtype: bool
120 """
121 return name and name[0] == SEP
122
123 def is_private(name):
124 """
125 Test if name is a private graph resource name.
126
127 @param name: must be a legal name in canonical form
128 @type name: str
129 @return bool: True if name is a privately referenced name (i.e. ~name)
130 """
131 return name and name[0] == PRIV_NAME
132
133 def namespace(name):
134 """
135 Get the namespace of name. The namespace is returned with a
136 trailing slash in order to favor easy concatenation and easier use
137 within the global context.
138
139 @param name: name to return the namespace of. Must be a legal
140 name. NOTE: an empty name will return the global namespace.
141 @type name: str
142 @return str: Namespace of name. For example, '/wg/node1' returns '/wg/'. The
143 global namespace is '/'.
144 @rtype: str
145 @raise ValueError: if name is invalid
146 """
147 "map name to its namespace"
148 if name is None:
149 raise ValueError('name')
150 if not isstring(name):
151 raise TypeError('name')
152 if not name:
153 return SEP
154 elif name[-1] == SEP:
155 name = name[:-1]
156 return name[:name.rfind(SEP)+1] or SEP
157
158 def ns_join(ns, name):
159 """
160 Join a namespace and name. If name is unjoinable (i.e. ~private or
161 /global) it will be returned without joining
162
163 @param ns: namespace ('/' and '~' are both legal). If ns is the empty string, name will be returned.
164 @type ns: str
165 @param name str: a legal name
166 @return str: name concatenated to ns, or name if it is
167 unjoinable.
168 @rtype: str
169 """
170 if is_private(name) or is_global(name):
171 return name
172 if ns == PRIV_NAME:
173 return PRIV_NAME + name
174 if not ns:
175 return name
176 if ns[-1] == SEP:
177 return ns + name
178 return ns + SEP + name
179
180 def load_mappings(argv):
181 """
182 Load name mappings encoded in command-line arguments. This will filter
183 out any parameter assignment mappings (see roslib.param.load_param_mappings()).
184
185 @param argv: command-line arguments
186 @type argv: [str]
187 @return: name->name remappings.
188 @rtype: dict {str: str}
189 """
190 mappings = {}
191 for arg in argv:
192 if REMAP in arg:
193 try:
194 src, dst = [x.strip() for x in arg.split(REMAP)]
195 if src and dst:
196 if len(src) > 1 and src[0] == '_' and src[1] != '_':
197 #ignore parameter assignment mappings
198 pass
199 else:
200 mappings[src] = dst
201 except:
202 sys.stderr.write("ERROR: Invalid remapping argument '%s'\n"%arg)
203 return mappings
204
205 #######################################################################
206 # RESOURCE NAMES
207 # resource names refer to entities in a file system
208
209 def resource_name(res_pkg_name, name, my_pkg=None):
210 """
211 Convert package name + resource into a fully qualified resource name
212
213 @param res_pkg_name: name of package resource is located in
214 @type res_pkg_name: str
215 @param name: resource base name
216 @type name: str
217 @param my_pkg: name of package resource is being referred to
218 in. If specified, name will be returned in local form if
219 res_pkg_name is my_pkg
220 @type my_pkg: str
221 @return: name for resource
222 @rtype: str
223 """
224 if res_pkg_name != my_pkg:
225 return res_pkg_name+PRN_SEPARATOR+name
226 return name
227
228 def resource_name_base(name):
229 """
230 pkg/typeName -> typeName, typeName -> typeName
231
232 Convert fully qualified resource name into the package-less resource name
233 @param name: package resource name, e.g. 'std_msgs/String'
234 @type name: str
235 @return: resource name sans package-name scope
236 @rtype: str
237 """
238
239 return name[name.rfind(PRN_SEPARATOR)+1:]
240
241 def resource_name_package(name):
242 """
243 pkg/typeName -> pkg, typeName -> None
244
245 @param name: package resource name, e.g. 'std_msgs/String'
246 @type name: str
247 @return: package name of resource
248 @rtype: str
249 """
250
251 if not PRN_SEPARATOR in name:
252 return None
253 return name[:name.find(PRN_SEPARATOR)]
254
255 def package_resource_name(name):
256 """
257 Split a name into its package and resource name parts, e.g. 'std_msgs/String -> std_msgs, String'
258
259 @param name: package resource name, e.g. 'std_msgs/String'
260 @type name: str
261 @return: package name, resource name
262 @rtype: str
263 @raise ValueError: if name is invalid
264 """
265 if PRN_SEPARATOR in name:
266 val = tuple(name.split(PRN_SEPARATOR))
267 if len(val) != 2:
268 raise ValueError("invalid name [%s]"%name)
269 else:
270 return val
271 else:
272 return '', name
273
274 def _is_safe_name(name, type_name):
275 #windows long-file name length is 255
276 if not isstring(name) or not name or len(name) > 255:
277 return False
278 return is_legal_resource_name(name)
279
280 ################################################################################
281 # NAME VALIDATORS
282
283 import re
284 #ascii char followed by (alphanumeric, _, /)
285 RESOURCE_NAME_LEGAL_CHARS_P = re.compile('^[A-Za-z][\w_\/]*$')
286 def is_legal_resource_name(name):
287 """
288 Check if name is a legal ROS name for filesystem resources
289 (alphabetical character followed by alphanumeric, underscore, or
290 forward slashes). This constraint is currently not being enforced,
291 but may start getting enforced in later versions of ROS.
292
293 @param name: Name
294 @type name: str
295 """
296 # resource names can be unicode due to filesystem
297 if name is None:
298 return False
299 m = RESOURCE_NAME_LEGAL_CHARS_P.match(name)
300 # '//' check makes sure there isn't double-slashes
301 return m is not None and m.group(0) == name and not '//' in name
302
303 #~,/, or ascii char followed by (alphanumeric, _, /)
304 NAME_LEGAL_CHARS_P = re.compile('^[\~\/A-Za-z][\w_\/]*$')
305 def is_legal_name(name):
306 """
307 Check if name is a legal ROS name for graph resources
308 (alphabetical character followed by alphanumeric, underscore, or
309 forward slashes). This constraint is currently not being enforced,
310 but may start getting enforced in later versions of ROS.
311
312 @param name: Name
313 @type name: str
314 """
315 # should we enforce unicode checks?
316 if name is None:
317 return False
318 # empty string is a legal name as it resolves to namespace
319 if name == '':
320 return True
321 m = NAME_LEGAL_CHARS_P.match(name)
322 return m is not None and m.group(0) == name and not '//' in name
323
324 BASE_NAME_LEGAL_CHARS_P = re.compile('^[A-Za-z][\w_]*$') #ascii char followed by (alphanumeric, _)
325 def is_legal_base_name(name):
326 """
327 Validates that name is a legal base name for a graph resource. A base name has
328 no namespace context, e.g. "node_name".
329 """
330 if name is None:
331 return False
332 m = BASE_NAME_LEGAL_CHARS_P.match(name)
333 return m is not None and m.group(0) == name
334
335 BASE_RESOURCE_NAME_LEGAL_CHARS_P = re.compile('^[A-Za-z][\w_]*$') #ascii char followed by (alphanumeric, _)
336 def is_legal_resource_base_name(name):
337 """
338 Validates that name is a legal resource base name. A base name has
339 no package context, e.g. "String".
340 """
341 # resource names can be unicode due to filesystem
342 if name is None:
343 return False
344 m = BASE_NAME_LEGAL_CHARS_P.match(name)
345 return m is not None and m.group(0) == name
346
347 def canonicalize_name(name):
348 """
349 Put name in canonical form. Extra slashes '//' are removed and
350 name is returned without any trailing slash, e.g. /foo/bar
351 @param name: ROS name
352 @type name: str
353 """
354 if not name or name == SEP:
355 return name
356 elif name[0] == SEP:
357 return '/' + '/'.join([x for x in name.split(SEP) if x])
358 else:
359 return '/'.join([x for x in name.split(SEP) if x])
360
361 def resolve_name(name, namespace_, remappings=None):
362 """
363 Resolve a ROS name to its global, canonical form. Private ~names
364 are resolved relative to the node name.
365
366 @param name: name to resolve.
367 @type name: str
368 @param namespace_: node name to resolve relative to.
369 @type namespace_: str
370 @param remappings: Map of resolved remappings. Use None to indicate no remapping.
371 @return: Resolved name. If name is empty/None, resolve_name
372 returns parent namespace_. If namespace_ is empty/None,
373 @rtype: str
374 """
375 if not name: #empty string resolves to parent of the namespace_
376 return namespace(namespace_)
377
378 name = canonicalize_name(name)
379 if name[0] == SEP: #global name
380 resolved_name = name
381 elif is_private(name): #~name
382 # #3044: be careful not to accidentally make rest of name global
383 resolved_name = canonicalize_name(namespace_ + SEP + name[1:])
384 else: #relative
385 resolved_name = namespace(namespace_) + name
386
387 #Mappings override general namespace-based resolution
388 # - do this before canonicalization as remappings are meant to
389 # match the name as specified in the code
390 if remappings and resolved_name in remappings:
391 return remappings[resolved_name]
392 else:
393 return resolved_name
394
395 def anonymous_name(id):
396 """
397 Generate a ROS-legal 'anonymous' name
398
399 @param id: prefix for anonymous name
400 @type id: str
401 """
402 import socket, random
403 name = "%s_%s_%s_%s"%(id, socket.gethostname(), os.getpid(), random.randint(0, sys.maxsize))
404 # RFC 952 allows hyphens, IP addrs can have '.'s, both
405 # of which are illegal for ROS names. For good
406 # measure, screen ipv6 ':'.
407 name = name.replace('.', '_')
408 name = name.replace('-', '_')
409 return name.replace(':', '_')
410
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 # Revision $Id$
33
34 """
35 Warning: do not use this library. It is unstable and most of the routines
36 here have been superceded by other libraries (e.g. rosgraph). These
37 routines will likely be *deleted* in future releases.
38 """
39
40 import os
41 import socket
42 import struct
43 import sys
44 import platform
45
46 try:
47 from cStringIO import StringIO #Python 2.x
48 python3 = 0
49 except ImportError:
50 from io import BytesIO #Python 3.x
51 python3 = 1
52
53 try:
54 import urllib.parse as urlparse
55 except ImportError:
56 import urlparse
57
58 #TODO: change this to rosgraph equivalents once we have ported this module
59 ROS_IP = 'ROS_IP'
60 ROS_HOSTNAME = 'ROS_HOSTNAME'
61
62 SIOCGIFCONF = 0x8912
63 SIOCGIFADDR = 0x8915
64 if platform.system() == 'FreeBSD':
65 SIOCGIFADDR = 0xc0206921
66 if platform.architecture()[0] == '64bit':
67 SIOCGIFCONF = 0xc0106924
68 else:
69 SIOCGIFCONF = 0xc0086924
70
71 if 0:
72 # disabling netifaces as it accounts for 50% of startup latency
73 try:
74 import netifaces
75 _use_netifaces = True
76 except:
77 # NOTE: in rare cases, I've seen Python fail to extract the egg
78 # cache when launching multiple python nodes. Thus, we do
79 # except-all instead of except ImportError (kwc).
80 _use_netifaces = False
81 else:
82 _use_netifaces = False
83
84 def _is_unix_like_platform():
85 """
86 @return: true if the platform conforms to UNIX/POSIX-style APIs
87 @rtype: bool
88 """
89 #return platform.system() in ['Linux', 'Mac OS X', 'Darwin']
90 return platform.system() in ['Linux', 'FreeBSD']
91
92 def get_address_override():
93 """
94 @return: ROS_IP/ROS_HOSTNAME override or None
95 @rtype: str
96 @raise ValueError: if ROS_IP/ROS_HOSTNAME/__ip/__hostname are invalidly specified
97 """
98 # #998: check for command-line remappings first
99 for arg in sys.argv:
100 if arg.startswith('__hostname:=') or arg.startswith('__ip:='):
101 try:
102 _, val = arg.split(':=')
103 return val
104 except: #split didn't unpack properly
105 raise ValueError("invalid ROS command-line remapping argument '%s'"%arg)
106
107 # check ROS_HOSTNAME and ROS_IP environment variables, which are
108 # aliases for each other
109 if ROS_HOSTNAME in os.environ:
110 return os.environ[ROS_HOSTNAME]
111 elif ROS_IP in os.environ:
112 return os.environ[ROS_IP]
113 return None
114
115 def is_local_address(hostname):
116 """
117 @param hostname: host name/address
118 @type hostname: str
119 @return True: if hostname maps to a local address, False otherwise. False conditions include invalid hostnames.
120 """
121 try:
122 reverse_ip = socket.gethostbyname(hostname)
123 except socket.error:
124 return False
125 # 127. check is due to #1260
126 if reverse_ip not in get_local_addresses() and not reverse_ip.startswith('127.'):
127 return False
128 return True
129
130 def get_local_address():
131 """
132 @return: default local IP address (e.g. eth0). May be overriden by ROS_IP/ROS_HOSTNAME/__ip/__hostname
133 @rtype: str
134 """
135 override = get_address_override()
136 if override:
137 return override
138 addrs = get_local_addresses()
139 if len(addrs) == 1:
140 return addrs[0]
141 for addr in addrs:
142 # pick first non 127/8 address
143 if not addr.startswith('127.'):
144 return addr
145 else: # loopback
146 return '127.0.0.1'
147
148 # cache for performance reasons
149 _local_addrs = None
150 def get_local_addresses():
151 """
152 @return: known local addresses. Not affected by ROS_IP/ROS_HOSTNAME
153 @rtype: [str]
154 """
155 # cache address data as it can be slow to calculate
156 global _local_addrs
157 if _local_addrs is not None:
158 return _local_addrs
159
160 local_addrs = None
161 if _use_netifaces:
162 # #552: netifaces is a more robust package for looking up
163 # #addresses on multiple platforms (OS X, Unix, Windows)
164 local_addrs = []
165 # see http://alastairs-place.net/netifaces/
166 for i in netifaces.interfaces():
167 try:
168 local_addrs.extend([d['addr'] for d in netifaces.ifaddresses(i)[netifaces.AF_INET]])
169 except KeyError: pass
170 elif _is_unix_like_platform():
171 # unix-only branch
172 # adapted from code from Rosen Diankov (rdiankov@cs.cmu.edu)
173 # and from ActiveState recipe
174
175 import fcntl
176 import array
177
178 ifsize = 32
179 if platform.system() == 'Linux' and platform.architecture()[0] == '64bit':
180 ifsize = 40 # untested
181
182 # 32 interfaces allowed, far more than ROS can sanely deal with
183
184 max_bytes = 32 * ifsize
185 # according to http://docs.python.org/library/fcntl.html, the buffer limit is 1024 bytes
186 buff = array.array('B', '\0' * max_bytes)
187 # serialize the buffer length and address to ioctl
188 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
189 info = fcntl.ioctl(sock.fileno(), SIOCGIFCONF,
190 struct.pack('iL', max_bytes, buff.buffer_info()[0]))
191 retbytes = struct.unpack('iL', info)[0]
192 buffstr = buff.tostring()
193 if platform.system() == 'Linux':
194 local_addrs = [socket.inet_ntoa(buffstr[i+20:i+24]) for i in range(0, retbytes, ifsize)]
195 else:
196 # in FreeBSD, ifsize is variable: 16 + (16 or 28 or 56) bytes
197 # When ifsize is 32 bytes, it contains the interface name and address,
198 # else it contains the interface name and other information
199 # This means the buffer must be traversed in its entirety
200 local_addrs = []
201 bufpos = 0
202 while bufpos < retbytes:
203 bufpos += 16
204 ifreqsize = ord(buffstr[bufpos])
205 if ifreqsize == 16:
206 local_addrs += [socket.inet_ntoa(buffstr[bufpos+4:bufpos+8])]
207 bufpos += ifreqsize
208 else:
209 # cross-platform branch, can only resolve one address
210 local_addrs = [socket.gethostbyname(socket.gethostname())]
211 _local_addrs = local_addrs
212 return local_addrs
213
214
215 def get_bind_address(address=None):
216 """
217 @param address: (optional) address to compare against
218 @type address: str
219 @return: address TCP/IP sockets should use for binding. This is
220 generally 0.0.0.0, but if \a address or ROS_IP/ROS_HOSTNAME is set
221 to localhost it will return 127.0.0.1
222 @rtype: str
223 """
224 if address is None:
225 address = get_address_override()
226 if address and \
227 (address == 'localhost' or address.startswith('127.')):
228 #localhost or 127/8
229 return '127.0.0.1' #loopback
230 else:
231 return '0.0.0.0'
232
233 # #528: semi-complicated logic for determining XML-RPC URI
234 def get_host_name():
235 """
236 Determine host-name for use in host-name-based addressing (e.g. XML-RPC URIs):
237 - if ROS_IP/ROS_HOSTNAME is set, use that address
238 - if the hostname returns a non-localhost value, use that
239 - use whatever L{get_local_address()} returns
240 """
241 hostname = get_address_override()
242 if not hostname:
243 try:
244 hostname = socket.gethostname()
245 except:
246 pass
247 if not hostname or hostname == 'localhost' or hostname.startswith('127.'):
248 hostname = get_local_address()
249 return hostname
250
251 def create_local_xmlrpc_uri(port):
252 """
253 Determine the XMLRPC URI for local servers. This handles the search
254 logic of checking ROS environment variables, the known hostname,
255 and local interface IP addresses to determine the best possible
256 URI.
257
258 @param port: port that server is running on
259 @type port: int
260 @return: XMLRPC URI
261 @rtype: str
262 """
263 #TODO: merge logic in roslib.xmlrpc with this routine
264 # in the future we may not want to be locked to http protocol nor root path
265 return 'http://%s:%s/'%(get_host_name(), port)
266
267
268 ## handshake utils ###########################################
269
270 class ROSHandshakeException(Exception):
271 """
272 Exception to represent errors decoding handshake
273 """
274 pass
275
276 def decode_ros_handshake_header(header_str):
277 """
278 Decode serialized ROS handshake header into a Python dictionary
279
280 header is a list of string key=value pairs, each prefixed by a
281 4-byte length field. It is preceeded by a 4-byte length field for
282 the entire header.
283
284 @param header_str: encoded header string. May contain extra data at the end.
285 @type header_str: str
286 @return: key value pairs encoded in \a header_str
287 @rtype: {str: str}
288 """
289 (size, ) = struct.unpack('<I', header_str[0:4])
290 size += 4 # add in 4 to include size of size field
291 header_len = len(header_str)
292 if size > header_len:
293 raise ROSHandshakeException("Incomplete header. Expected %s bytes but only have %s"%((size+4), header_len))
294
295 d = {}
296 start = 4
297 while start < size:
298 (field_size, ) = struct.unpack('<I', header_str[start:start+4])
299 if field_size == 0:
300 raise ROSHandshakeException("Invalid 0-length handshake header field")
301 start += field_size + 4
302 if start > size:
303 raise ROSHandshakeException("Invalid line length in handshake header: %s"%size)
304 line = header_str[start-field_size:start]
305
306 #python3 compatibility
307 if python3 == 1:
308 line = line.decode()
309
310 idx = line.find("=")
311 if idx < 0:
312 raise ROSHandshakeException("Invalid line in handshake header: [%s]"%line)
313 key = line[:idx]
314 value = line[idx+1:]
315 d[key.strip()] = value
316 return d
317
318 def read_ros_handshake_header(sock, b, buff_size):
319 """
320 Read in tcpros header off the socket \a sock using buffer \a b.
321
322 @param sock: socket must be in blocking mode
323 @type sock: socket
324 @param b: buffer to use
325 @type b: StringIO for Python2, BytesIO for Python 3
326 @param buff_size: incoming buffer size to use
327 @type buff_size: int
328 @return: key value pairs encoded in handshake
329 @rtype: {str: str}
330 @raise ROSHandshakeException: If header format does not match expected
331 """
332 header_str = None
333 while not header_str:
334 d = sock.recv(buff_size)
335 if not d:
336 raise ROSHandshakeException("connection from sender terminated before handshake header received. %s bytes were received. Please check sender for additional details."%b.tell())
337 b.write(d)
338 btell = b.tell()
339 if btell > 4:
340 # most likely we will get the full header in the first recv, so
341 # not worth tiny optimizations possible here
342 bval = b.getvalue()
343 (size,) = struct.unpack('<I', bval[0:4])
344 if btell - 4 >= size:
345 header_str = bval
346
347 # memmove the remnants of the buffer back to the start
348 leftovers = bval[size+4:]
349 b.truncate(len(leftovers))
350 b.seek(0)
351 b.write(leftovers)
352 header_recvd = True
353
354 # process the header
355 return decode_ros_handshake_header(bval)
356
357 def encode_ros_handshake_header(header):
358 """
359 Encode ROS handshake header as a byte string. Each header
360 field is a string key value pair. The encoded header is
361 prefixed by a length field, as is each field key/value pair.
362 key/value pairs a separated by a '=' equals sign.
363
364 FORMAT: (4-byte length + [4-byte field length + field=value ]*)
365
366 @param header: header field keys/values
367 @type header: dict
368 @return: header encoded as byte string
369 @rtype: str
370 """
371 fields = ["%s=%s"%(k,v) for k,v in header.items()]
372
373 # in the usual configuration, the error 'TypeError: can't concat bytes to str' appears:
374 if python3 == 0:
375 #python 2
376 s = ''.join(["%s%s"%(struct.pack('<I', len(f)), f) for f in fields])
377 return struct.pack('<I', len(s)) + s
378 else:
379 #python 3
380 s = b''.join([(struct.pack('<I', len(f)) + f.encode("utf-8")) for f in fields])
381 return struct.pack('<I', len(s)) + s
382
383 def write_ros_handshake_header(sock, header):
384 """
385 Write ROS handshake header header to socket sock
386 @param sock: socket to write to (must be in blocking mode)
387 @type sock: socket.socket
388 @param header: header field keys/values
389 @type header: {str : str}
390 @return: Number of bytes sent (for statistics)
391 @rtype: int
392 """
393 s = encode_ros_handshake_header(header)
394 sock.sendall(s)
395 return len(s) #STATS
396
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 # Revision $Id$
33 # $Author$
34
35 """
36 Warning: do not use this library. It is unstable and most of the routines
37 here have been superceded by other libraries (e.g. rospkg). These
38 routines will likely be *deleted* in future releases.
39 """
40
41 import os
42 import sys
43 import stat
44 import string
45
46 from subprocess import Popen, PIPE
47
48 from catkin.find_in_workspaces import find_in_workspaces as catkin_find
49 import rospkg
50
51 import roslib.manifest
52
53 SRC_DIR = 'src'
54
55 # aliases
56 ROS_PACKAGE_PATH = rospkg.environment.ROS_PACKAGE_PATH
57 ROS_ROOT = rospkg.environment.ROS_ROOT
58
59 class ROSPkgException(Exception):
60 """
61 Base class of package-related errors.
62 """
63 pass
64 class InvalidROSPkgException(ROSPkgException):
65 """
66 Exception that indicates that a ROS package does not exist
67 """
68 pass
69 class MultipleNodesException(ROSPkgException):
70 """
71 Exception that indicates that multiple ROS nodes by the same name are in the same package.
72 """
73 pass
74
75 # TODO: go through the code and eliminate unused methods -- there's far too many combos here
76
77 MANIFEST_FILE = 'manifest.xml'
78
79 #
80 # Map package/directory structure
81 #
82
83 def get_dir_pkg(d):
84 """
85 Get the package that the directory is contained within. This is
86 determined by finding the nearest parent manifest.xml file. This
87 isn't 100% reliable, but symlinks can fool any heuristic that
88 relies on ROS_ROOT.
89 @param d: directory path
90 @type d: str
91 @return: (package_directory, package) of the specified directory, or None,None if not in a package
92 @rtype: (str, str)
93 """
94 #TODO: the realpath is going to create issues with symlinks, most likely
95
96 parent = os.path.dirname(os.path.realpath(d))
97 #walk up until we hit ros root or ros/pkg
98 while not os.path.exists(os.path.join(d, MANIFEST_FILE)) and parent != d:
99 d = parent
100 parent = os.path.dirname(d)
101 if os.path.exists(os.path.join(d, MANIFEST_FILE)):
102 pkg = os.path.basename(os.path.abspath(d))
103 return d, pkg
104 return None, None
105
106 _pkg_dir_cache = {}
107
108 def get_pkg_dir(package, required=True, ros_root=None, ros_package_path=None):
109 """
110 Locate directory package is stored in. This routine uses an
111 internal cache.
112
113 NOTE: cache does *not* rebuild if packages are relocated after
114 this process is initiated.
115
116 @param package: package name
117 @type package: str
118 @param required: if True, an exception will be raised if the
119 package directory cannot be located.
120 @type required: bool
121 @param ros_root: if specified, override ROS_ROOT
122 @type ros_root: str
123 @param ros_package_path: if specified, override ROS_PACKAGE_PATH
124 @type ros_package_path: str
125 @return: directory containing package or None if package cannot be found and required is False.
126 @rtype: str
127 @raise InvalidROSPkgException: if required is True and package cannot be located
128 """
129
130 #UNIXONLY
131 #TODO: replace with non-rospack-based solution (e.g. os.walk())
132 try:
133 penv = os.environ.copy()
134 if ros_root:
135 ros_root = rospkg.environment._resolve_path(ros_root)
136 penv[ROS_ROOT] = ros_root
137 elif ROS_ROOT in os.environ:
138 # record setting for _pkg_dir_cache
139 ros_root = os.environ[ROS_ROOT]
140
141 # determine rospack exe name
142 rospack = 'rospack'
143
144 if ros_package_path is not None:
145 ros_package_path = rospkg.environment._resolve_paths(ros_package_path)
146 penv[ROS_PACKAGE_PATH] = ros_package_path
147 elif ROS_PACKAGE_PATH in os.environ:
148 # record setting for _pkg_dir_cache
149 ros_package_path = os.environ[ROS_PACKAGE_PATH]
150
151 # update cache if we haven't. NOTE: we only get one cache
152 if not _pkg_dir_cache:
153 _read_rospack_cache(_pkg_dir_cache, ros_root, ros_package_path)
154
155 # now that we've resolved the args, check the cache
156 if package in _pkg_dir_cache:
157 dir_, rr, rpp = _pkg_dir_cache[package]
158 if rr == ros_root and rpp == ros_package_path:
159 if os.path.isfile(os.path.join(dir_, MANIFEST_FILE)):
160 return dir_
161 else:
162 # invalidate cache
163 _invalidate_cache(_pkg_dir_cache)
164
165 rpout, rperr = Popen([rospack, 'find', package], \
166 stdout=PIPE, stderr=PIPE, env=penv).communicate()
167
168 pkg_dir = (rpout or '').strip()
169 #python3.1 popen returns as bytes
170 if (isinstance(pkg_dir, bytes)):
171 pkg_dir = pkg_dir.decode()
172 if not pkg_dir:
173 raise InvalidROSPkgException("Cannot locate installation of package %s: %s. ROS_ROOT[%s] ROS_PACKAGE_PATH[%s]"%(package, rperr.strip(), ros_root, ros_package_path))
174
175 if not os.path.exists(pkg_dir):
176 raise InvalidROSPkgException("Cannot locate installation of package %s: [%s] is not a valid path. ROS_ROOT[%s] ROS_PACKAGE_PATH[%s]"%(package, pkg_dir, ros_root, ros_package_path))
177 elif not os.path.isdir(pkg_dir):
178 raise InvalidROSPkgException("Package %s is invalid: file [%s] is in the way"%(package, pkg_dir))
179 # don't update cache: this should only be updated from
180 # rospack_cache as it will corrupt package list otherwise.
181 #_pkg_dir_cache[package] = (pkg_dir, ros_root, ros_package_path)
182 return pkg_dir
183 except OSError as e:
184 if required:
185 raise InvalidROSPkgException("Environment configuration is invalid: cannot locate rospack (%s)"%e)
186 return None
187 except Exception as e:
188 if required:
189 raise
190 return None
191
192 def _get_pkg_subdir_by_dir(package_dir, subdir, required=True, env=None):
193 """
194 @param required: if True, will attempt to create the subdirectory
195 if it does not exist. An exception will be raised if this fails.
196 @type required: bool
197 @param package_dir: directory of package
198 @type package_dir: str
199 @param subdir: name of subdirectory to locate
200 @type subdir: str
201 @param env: override os.environ dictionary
202 @type env: dict
203 @param required: if True, directory must exist
204 @type required: bool
205 @return: Package subdirectory if package exist, otherwise None.
206 @rtype: str
207 @raise InvalidROSPkgException: if required is True and directory does not exist
208 """
209 if env is None:
210 env = os.environ
211 try:
212 if not package_dir:
213 raise Exception("Cannot create a '%(subdir)s' directory in %(package_dir)s: package %(package) cannot be located"%locals())
214 d = os.path.join(package_dir, subdir)
215 if required and os.path.isfile(d):
216 raise Exception("""Package '%(package)s' is improperly configured:
217 file %(d)s is preventing the creation of a directory"""%locals())
218 elif required and not os.path.isdir(d):
219 try:
220 os.makedirs(d) #lazy create
221 except error:
222 raise Exception("""Package '%(package)s' is improperly configured:
223 Cannot create a '%(subdir)s' directory in %(package_dir)s.
224 Please check permissions and try again.
225 """%locals())
226 return d
227 except Exception as e:
228 if required:
229 raise
230 return None
231
232 def get_pkg_subdir(package, subdir, required=True, env=None):
233 """
234 @param required: if True, will attempt to create the subdirectory
235 if it does not exist. An exception will be raised if this fails.
236 @type required: bool
237 @param package: name of package
238 @type package: str
239 @param env: override os.environ dictionary
240 @type env: dict
241 @param required: if True, directory must exist
242 @type required: bool
243 @return: Package subdirectory if package exist, otherwise None.
244 @rtype: str
245 @raise InvalidROSPkgException: if required is True and directory does not exist
246 """
247 if env is None:
248 env = os.environ
249 pkg_dir = get_pkg_dir(package, required, ros_root=env[ROS_ROOT])
250 return _get_pkg_subdir_by_dir(pkg_dir, subdir, required, env)
251
252 #
253 # Map ROS resources to files
254 #
255
256 def resource_file(package, subdir, resource_name):
257 """
258 @param subdir: name of subdir -- these should be one of the
259 string constants, e.g. MSG_DIR
260 @type subdir: str
261 @return: path to resource in the specified subdirectory of the
262 package, or None if the package does not exists
263 @rtype: str
264 @raise roslib.packages.InvalidROSPkgException: If package does not exist
265 """
266 d = get_pkg_subdir(package, subdir, False)
267 if d is None:
268 raise InvalidROSPkgException(package)
269 return os.path.join(d, resource_name)
270
271 def _update_rospack_cache(env=None):
272 """
273 Internal routine to update global package directory cache
274
275 @return: True if cache is valid
276 @rtype: bool
277 """
278 if env is None:
279 env = os.environ
280 cache = _pkg_dir_cache
281 if cache:
282 return True
283 ros_root = env[ROS_ROOT]
284 ros_package_path = env.get(ROS_PACKAGE_PATH, '')
285 return _read_rospack_cache(cache, ros_root, ros_package_path)
286
287 def _invalidate_cache(cache):
288 # I've only made this a separate routine because roslib.packages should really be using
289 # the roslib.stacks cache implementation instead with the separate cache marker
290 cache.clear()
291
292 def _read_rospack_cache(cache, ros_root, ros_package_path):
293 """
294 Read in rospack_cache data into cache. On-disk cache specifies a
295 ROS_ROOT and ROS_PACKAGE_PATH, which must match the requested
296 environment.
297
298 @param cache: empty dictionary to store package list in.
299 If no cache argument provided, will use internal _pkg_dir_cache
300 and will return cached answers if available.
301 The format of the cache is {package_name: dir_path, ros_root, ros_package_path}.
302 @type cache: {str: str, str, str}
303 @param ros_package_path: ROS_ROOT value
304 @type ros_root: str
305 @param ros_package_path: ROS_PACKAGE_PATH value or '' if not specified
306 @type ros_package_path: str
307 @return: True if on-disk cache matches and was loaded, false otherwise
308 @rtype: bool
309 """
310 try:
311 with open(os.path.join(rospkg.get_ros_home(), 'rospack_cache')) as f:
312 for l in f.readlines():
313 l = l[:-1]
314 if not len(l):
315 continue
316 if l[0] == '#':
317 # check that the cache matches our env
318 if l.startswith('#ROS_ROOT='):
319 if not l[len('#ROS_ROOT='):] == ros_root:
320 return False
321 elif l.startswith('#ROS_PACKAGE_PATH='):
322 if not l[len('#ROS_PACKAGE_PATH='):] == ros_package_path:
323 return False
324 else:
325 cache[os.path.basename(l)] = l, ros_root, ros_package_path
326 return True
327 except:
328 pass
329
330 def list_pkgs_by_path(path, packages=None, cache=None, env=None):
331 """
332 List ROS packages within the specified path.
333
334 Optionally, a cache dictionary can be provided, which will be
335 updated with the package->path mappings. list_pkgs_by_path() does
336 NOT returned cached results -- it only updates the cache.
337
338 @param path: path to list packages in
339 @type path: str
340 @param packages: list of packages to append to. If package is
341 already present in packages, it will be ignored.
342 @type packages: [str]
343 @param cache: (optional) package path cache to update. Maps package name to directory path.
344 @type cache: {str: str}
345 @return: complete list of package names in ROS environment. Same as packages parameter.
346 @rtype: [str]
347 """
348 if packages is None:
349 packages = []
350 if env is None:
351 env = os.environ
352 # record settings for cache
353 ros_root = env[ROS_ROOT]
354 ros_package_path = env.get(ROS_PACKAGE_PATH, '')
355
356 path = os.path.abspath(path)
357 for d, dirs, files in os.walk(path, topdown=True):
358 if MANIFEST_FILE in files:
359 package = os.path.basename(d)
360 if package not in packages:
361 packages.append(package)
362 if cache is not None:
363 cache[package] = d, ros_root, ros_package_path
364 del dirs[:]
365 continue #leaf
366 elif 'rospack_nosubdirs' in files:
367 del dirs[:]
368 continue #leaf
369 #small optimization
370 elif '.svn' in dirs:
371 dirs.remove('.svn')
372 elif '.git' in dirs:
373 dirs.remove('.git')
374
375 for sub_d in dirs:
376 # followlinks=True only available in Python 2.6, so we
377 # have to implement manually
378 sub_p = os.path.join(d, sub_d)
379 if os.path.islink(sub_p):
380 packages.extend(list_pkgs_by_path(sub_p, cache=cache))
381
382 return packages
383
384 def find_node(pkg, node_type, rospack=None):
385 """
386 Warning: unstable API due to catkin.
387
388 Locate the executable that implements the node
389
390 :param node_type: type of node, ``str``
391 :returns: path to node or None if node is not in the package ``str``
392 :raises: :exc:rospkg.ResourceNotFound` If package does not exist
393 """
394
395 if rospack is None:
396 rospack = rospkg.RosPack()
397 return find_resource(pkg, node_type, filter_fn=_executable_filter, rospack=rospack)
398
399 def _executable_filter(test_path):
400 s = os.stat(test_path)
401 return (s.st_mode & (stat.S_IRUSR | stat.S_IXUSR) == (stat.S_IRUSR | stat.S_IXUSR))
402
403 def _find_resource(d, resource_name, filter_fn=None):
404 """
405 subroutine of find_resource
406 """
407 matches = []
408 # TODO: figure out how to generalize find_resource to take multiple resource name options
409 if sys.platform in ['win32', 'cygwin']:
410 # Windows logic requires more file patterns to resolve and is
411 # not case-sensitive, so leave it separate
412
413 # in the near-term, just hack in support for .exe/.bat. In the long
414 # term this needs to:
415 #
416 # * parse PATHEXT to generate matches
417 # * perform case-insensitive compares against potential
418 # matches, in path-ext order
419
420 # - We still have to look for bare node_type as user may have
421 # specified extension manually
422 resource_name = resource_name.lower()
423 patterns = [resource_name, resource_name+'.exe', resource_name+'.bat']
424 for p, dirs, files in os.walk(d):
425 # case insensitive
426 files = [f.lower() for f in files]
427 for name in patterns:
428 if name in files:
429 test_path = os.path.join(p, name)
430 if filter_fn is not None:
431 if filter_fn(test_path):
432 matches.append(test_path)
433 else:
434 matches.append(test_path)
435 # remove .svn/.git/etc
436 to_prune = [x for x in dirs if x.startswith('.')]
437 for x in to_prune:
438 dirs.remove(x)
439 else: #UNIX
440 for p, dirs, files in os.walk(d):
441 if resource_name in files:
442 test_path = os.path.join(p, resource_name)
443 if filter_fn is not None:
444 if filter_fn(test_path):
445 matches.append(test_path)
446 else:
447 matches.append(test_path)
448 # remove .svn/.git/etc
449 to_prune = [x for x in dirs if x.startswith('.')]
450 for x in to_prune:
451 dirs.remove(x)
452 return [os.path.abspath(m) for m in matches]
453
454 # TODO: this routine really belongs in rospkg, but the catkin-isms really, really don't
455 # belong in rospkg. With more thought, they can probably be abstracted out so as
456 # to no longer be catkin-specific.
457 def find_resource(pkg, resource_name, filter_fn=None, rospack=None):
458 """
459 Warning: unstable API due to catkin.
460
461 Locate the file named resource_name in package, optionally
462 matching specified filter. find_resource() will return a list of
463 matches, but only for a given scope. If the resource is found in
464 the binary build directory, it will only return matches in that
465 directory; it will not return matches from the ROS_PACKAGE_PATH as
466 well in this case.
467
468 :param filter: function that takes in a path argument and
469 returns True if the it matches the desired resource, ``fn(str)``
470 :param rospack: `rospkg.RosPack` instance to use
471 :returns: lists of matching paths for resource within a given scope, ``[str]``
472 :raises: :exc:`rospkg.ResourceNotFound` If package does not exist
473 """
474
475 # New resource-location policy in Fuerte, induced by the new catkin
476 # build system:
477 # (1) Use catkin_find to find libexec and share locations, look
478 # recursively there. If the resource is found, done.
479 # Else continue:
480 # (2) If ROS_PACKAGE_PATH is set, look recursively there. If the
481 # resource is found, done. Else raise
482 #
483 # NOTE: package *must* exist on ROS_PACKAGE_PATH no matter what
484
485 if rospack is None:
486 rospack = rospkg.RosPack()
487
488 # lookup package as it *must* exist
489 pkg_path = rospack.get_path(pkg)
490
491 # if found in binary dir, start with that. in any case, use matches
492 # from ros_package_path
493 matches = []
494 for search_dirs in ['libexec', 'share']:
495 try:
496 search_paths, _ = catkin_find(search_dirs=[search_dirs], project=pkg)
497 for search_path in search_paths:
498 matches.extend(_find_resource(search_path, resource_name, filter_fn=filter_fn))
499 except RuntimeError:
500 pass
501 matches.extend(_find_resource(pkg_path, resource_name, filter_fn=filter_fn))
502 # Uniquify the results, in case we found the same file twice
503 return list(set(matches))
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
33 """
34 Warning: do not use this library. It is unstable and most of the routines
35 here have been superceded by other libraries (e.g. rospkg). These
36 routines will likely be *deleted* in future releases.
37 """
38
39 import os
40
41 import roslib.manifest
42 import roslib.names
43 import roslib.packages
44
45 def _get_manifest_by_dir(package_dir):
46 """
47 Helper routine for loading Manifest instances
48 @param package_dir: package directory location
49 @type package_dir: str
50 @return: manifest for package
51 @rtype: Manifest
52 """
53 f = os.path.join(package_dir, roslib.manifest.MANIFEST_FILE)
54 if f:
55 return roslib.manifest.parse_file(f)
56 else:
57 return None
58
59 def list_package_resources_by_dir(package_dir, include_depends, subdir, rfilter=os.path.isfile):
60 """
61 List resources in a package directory within a particular
62 subdirectory. This is useful for listing messages, services, etc...
63 @param package_dir: package directory location
64 @type package_dir: str
65 @param subdir: name of subdirectory
66 @type subdir: str
67 @param include_depends: if True, include resources in dependencies as well
68 @type include_depends: bool
69 @param rfilter: resource filter function that returns true if filename is the desired resource type
70 @type rfilter: fn(filename)->bool
71 """
72 package = os.path.basename(package_dir)
73 resources = []
74 dir = roslib.packages._get_pkg_subdir_by_dir(package_dir, subdir, False)
75 if os.path.isdir(dir):
76 resources = [roslib.names.resource_name(package, f, my_pkg=package) \
77 for f in os.listdir(dir) if rfilter(os.path.join(dir, f))]
78 else:
79 resources = []
80 if include_depends:
81 depends = _get_manifest_by_dir(package_dir).depends
82 dirs = [roslib.packages.get_pkg_subdir(d.package, subdir, False) for d in depends]
83 for (dep, dir_) in zip(depends, dirs): #py3k
84 if not dir_ or not os.path.isdir(dir_):
85 continue
86 resources.extend(\
87 [roslib.names.resource_name(dep.package, f, my_pkg=package) \
88 for f in os.listdir(dir_) if rfilter(os.path.join(dir_, f))])
89 return resources
90
91 def list_package_resources(package, include_depends, subdir, rfilter=os.path.isfile):
92 """
93 List resources in a package within a particular subdirectory. This is useful for listing
94 messages, services, etc...
95 @param package: package name
96 @type package: str
97 @param subdir: name of subdirectory
98 @type subdir: str
99 @param include_depends: if True, include resources in dependencies as well
100 @type include_depends: bool
101 @param rfilter: resource filter function that returns true if filename is the desired resource type
102 @type rfilter: fn(filename)->bool
103 """
104 package_dir = roslib.packages.get_pkg_dir(package)
105 return list_package_resources_by_dir(package_dir, include_depends, subdir, rfilter)
106
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 # Revision $Id$
33
34
35 """
36 Warning: do not use this library. It is unstable and most of the routines
37 here have been superceded by other libraries (e.g. rospkg). These
38 routines will likely be *deleted* in future releases.
39 """
40
41 import os
42 import sys
43
44 # Global, usually set in setup
45 ROS_ROOT = "ROS_ROOT"
46 ROS_MASTER_URI = "ROS_MASTER_URI"
47 ROS_PACKAGE_PATH = "ROS_PACKAGE_PATH"
48 ROS_HOME = "ROS_HOME"
49
50 # Build-related
51 ROS_BINDEPS_PATH = "ROS_BINDEPS_PATH"
52 ROS_BOOST_ROOT = "ROS_BOOST_ROOT"
53
54 # Per session
55 ## hostname/address to bind XML-RPC services to.
56 ROS_IP ="ROS_IP"
57 ROS_HOSTNAME ="ROS_HOSTNAME"
58 ROS_NAMESPACE ="ROS_NAMESPACE"
59 ## directory in which log files are written
60 ROS_LOG_DIR ="ROS_LOG_DIR"
61 ## directory in which test result files are written
62 CATKIN_TEST_RESULTS_DIR = "CATKIN_TEST_RESULTS_DIR"
63
64 class ROSEnvException(Exception):
65 """Base class of roslib.rosenv errors."""
66 pass
67
68 import warnings
69 warnings.warn("roslib.rosenv is deprecated, please use rospkg or rosgraph.rosenv", stacklevel=2)
70
71 def get_ros_root(required=True, env=None):
72 """
73 @param required: (default True). If True, ROS_ROOT must be set and point to a valid directory.
74 @type required: bool
75 @param env: override environment dictionary
76 @type env: dict
77 @raise ROSEnvException: if required is True and ROS_ROOT is not set
78 """
79 if env is None:
80 env = os.environ
81 p = None
82 try:
83 if ROS_ROOT not in env:
84 raise ROSEnvException("""
85 The %(ROS_ROOT)s environment variable has not been set.
86 Please set to the location of your ROS installation
87 before continuing.
88 """%globals())
89
90 return env[ROS_ROOT]
91 except Exception as e:
92 if required:
93 raise
94 return p
95
96 def get_ros_package_path(required=False, env=None):
97 """
98 @param required: (default False) if True, ROS_PACKAGE_PATH must be
99 set and point to a valid directory.
100 @type required: bool
101 @raise ROSEnvException: if ROS_PACKAGE_PATH is not set and \a
102 required is True
103 """
104 if env is None:
105 env = os.environ
106 try:
107 return env[ROS_PACKAGE_PATH]
108 except KeyError as e:
109 if required:
110 raise ROSEnvException("%s has not been configured"%ROS_PACKAGE_PATH)
111
112 def get_master_uri(required=True, env=None, argv=None):
113 """
114 Get the ROS_MASTER_URI setting from the command-line args or
115 environment, command-line args takes precedence.
116 @param required: if True, enables exception raising
117 @type required: bool
118 @param env: override environment dictionary
119 @type env: dict
120 @param argv: override sys.argv
121 @type argv: [str]
122 @raise ROSEnvException: if ROS_MASTER_URI value is invalidly
123 specified or if required and ROS_MASTER_URI is not set
124 """
125 if env is None:
126 env = os.environ
127 if argv is None:
128 argv = sys.argv
129 try:
130 for arg in argv:
131 if arg.startswith('__master:='):
132 val = None
133 try:
134 _, val = arg.split(':=')
135 except:
136 pass
137
138 # we ignore required here because there really is no
139 # correct return value as the configuration is bad
140 # rather than unspecified
141 if not val:
142 raise ROSEnvException("__master remapping argument '%s' improperly specified"%arg)
143 return val
144 return env[ROS_MASTER_URI]
145 except KeyError as e:
146 if required:
147 raise ROSEnvException("%s has not been configured"%ROS_MASTER_URI)
148
149 def get_ros_home(env=None):
150 """
151 Get directory location of '.ros' directory (aka ROS home).
152 possible locations for this. The ROS_LOG_DIR environment variable
153 has priority. If that is not set, then ROS_HOME/log is used. If
154 ROS_HOME is not set, $HOME/.ros/log is used.
155
156 @param env: override os.environ dictionary
157 @type env: dict
158 @return: path to use use for log file directory
159 @rtype: str
160 """
161 if env is None:
162 env = os.environ
163 if ROS_HOME in env:
164 return env[ROS_HOME]
165 else:
166 #slightly more robust than $HOME
167 return os.path.join(os.path.expanduser('~'), '.ros')
168
169 def get_log_dir(env=None):
170 """
171 Get directory to use for writing log files. There are multiple
172 possible locations for this. The ROS_LOG_DIR environment variable
173 has priority. If that is not set, then ROS_HOME/log is used. If
174 ROS_HOME is not set, $HOME/.ros/log is used.
175
176 @param env: override os.environ dictionary
177 @type env: dict
178 @return: path to use use for log file directory
179 @rtype: str
180 """
181 if env is None:
182 env = os.environ
183 if ROS_LOG_DIR in env:
184 return env[ROS_LOG_DIR]
185 else:
186 return os.path.join(get_ros_home(env), 'log')
187
188 def get_test_results_dir(env=None):
189 """
190 Get directory to use for writing test result files. There are multiple
191 possible locations for this. The CATKIN_TEST_RESULTS_DIR environment variable
192 has priority. If that is set, CATKIN_TEST_RESULTS_DIR is returned.
193 If CATKIN_TEST_RESULTS_DIR is not set, then ROS_HOME/test_results is used. If
194 ROS_HOME is not set, $HOME/.ros/test_results is used.
195
196 @param env: environment dictionary (defaults to os.environ)
197 @type env: dict
198 @return: path to use use for log file directory
199 @rtype: str
200 """
201 if env is None:
202 env = os.environ
203
204 if CATKIN_TEST_RESULTS_DIR in env:
205 return env[CATKIN_TEST_RESULTS_DIR]
206 else:
207 return os.path.join(get_ros_home(env), 'test_results')
208
209 # this is a copy of the roslogging utility. it's been moved here as it is a common
210 # routine for programs using accessing ROS directories
211 def makedirs_with_parent_perms(p):
212 """
213 Create the directory using the permissions of the nearest
214 (existing) parent directory. This is useful for logging, where a
215 root process sometimes has to log in the user's space.
216 @param p: directory to create
217 @type p: str
218 """
219 p = os.path.abspath(p)
220 parent = os.path.dirname(p)
221 # recurse upwards, checking to make sure we haven't reached the
222 # top
223 if not os.path.exists(p) and p and parent != p:
224 makedirs_with_parent_perms(parent)
225 s = os.stat(parent)
226 os.mkdir(p)
227
228 # if perms of new dir don't match, set anew
229 s2 = os.stat(p)
230 if s.st_uid != s2.st_uid or s.st_gid != s2.st_gid:
231 os.chown(p, s.st_uid, s.st_gid)
232 if s.st_mode != s2.st_mode:
233 os.chmod(p, s.st_mode)
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 # Revision $Id$
33 # $Author$
34
35 """
36 Warning: do not use this library. It is unstable and most of the routines
37 here have been superceded by other libraries (e.g. rospkg). These
38 routines will likely be *deleted* in future releases.
39 """
40
41 import os
42 import sys
43 import subprocess
44 import roslib.exceptions
45 import rospkg
46
47 if sys.hexversion > 0x03000000: #Python3
48 python3 = True
49 else:
50 python3 = False
51
52 import warnings
53 warnings.warn("roslib.rospack is deprecated, please use rospkg", stacklevel=2)
54
55 def rospackexec(args):
56 """
57 @return: result of executing rospack command (via subprocess). string will be strip()ed.
58 @rtype: str
59 @raise roslib.exceptions.ROSLibException: if rospack command fails
60 """
61 rospack_bin = 'rospack'
62 if python3:
63 val = subprocess.Popen([rospack_bin] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
64 val = val.decode().strip()
65 else:
66 val = (subprocess.Popen([rospack_bin] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] or '').strip()
67 if val.startswith('rospack:'): #rospack error message
68 raise roslib.exceptions.ROSLibException(val)
69 return val
70
71 def rospack_depends_on_1(pkg):
72 """
73 @param pkg: package name
74 @type pkg: str
75 @return: A list of the names of the packages which depend directly on pkg
76 @rtype: list
77 """
78 return rospackexec(['depends-on1', pkg]).split()
79
80 def rospack_depends_on(pkg):
81 """
82 @param pkg: package name
83 @type pkg: str
84 @return: A list of the names of the packages which depend on pkg
85 @rtype: list
86 """
87 return rospackexec(['depends-on', pkg]).split()
88
89 def rospack_depends_1(pkg):
90 """
91 @param pkg: package name
92 @type pkg: str
93 @return: A list of the names of the packages which pkg directly depends on
94 @rtype: list
95 """
96 return rospackexec(['deps1', pkg]).split()
97
98 def rospack_depends(pkg):
99 """
100 @param pkg: package name
101 @type pkg: str
102 @return: A list of the names of the packages which pkg depends on
103 @rtype: list
104 """
105 return rospackexec(['deps', pkg]).split()
106
107 def rospack_plugins(pkg):
108 """
109 @param pkg: package name
110 @type pkg: str
111 @return: A list of the names of the packages which provide a plugin for pkg
112 @rtype: list
113 """
114 val = rospackexec(['plugins', '--attrib=plugin', pkg])
115 if val:
116 return [tuple(x.split(' ')) for x in val.split('\n')]
117 else:
118 return []
119
120 def rosstackexec(args):
121 """
122 @return: result of executing rosstack command (via subprocess). string will be strip()ed.
123 @rtype: str
124 @raise roslib.exceptions.ROSLibException: if rosstack command fails
125 """
126 rosstack_bin = 'rosstack'
127 if python3:
128 val = subprocess.Popen([rosstack_bin] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
129 val = val.decode().strip()
130 else:
131 val = (subprocess.Popen([rosstack_bin] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] or '').strip()
132 if val.startswith('rosstack:'): #rospack error message
133 raise roslib.exceptions.ROSLibException(val)
134 return val
135
136 def rosstack_depends_on(s):
137 """
138 @param s: stack name
139 @type s: str
140 @return: A list of the names of the stacks which depend on s
141 @rtype: list
142 """
143 return rosstackexec(['depends-on', s]).split()
144
145 def rosstack_depends_on_1(s):
146 """
147 @param s: stack name
148 @type s: str
149 @return: A list of the names of the stacks which depend directly on s
150 @rtype: list
151 """
152 return rosstackexec(['depends-on1', s]).split()
153
154 def rosstack_depends(s):
155 """
156 @param s: stack name
157 @type s: str
158 @return: A list of the names of the stacks which s depends on
159 @rtype: list
160 """
161 return rosstackexec(['depends', s]).split()
162
163 def rosstack_depends_1(s):
164 """
165 @param s: stack name
166 @type s: str
167 @return: A list of the names of the stacks which s depends on directly
168 @rtype: list
169 """
170 return rosstackexec(['depends1', s]).split()
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 # Revision $Id$
33 # $Author$
34
35 """
36 Warning: do not use this library. It is unstable and most of the routines
37 here have been superceded by other libraries (e.g. rospkg). These
38 routines will likely be *deleted* in future releases.
39 """
40
41 import os
42 import sys
43
44 import roslib.names
45
46 ## caller ID for master calls where caller ID is not vital
47 _GLOBAL_CALLER_ID = '/script'
48
49
50 import warnings
51 def deprecated(func):
52 """This is a decorator which can be used to mark functions
53 as deprecated. It will result in a warning being emmitted
54 when the function is used."""
55 def newFunc(*args, **kwargs):
56 warnings.warn("Call to deprecated function %s." % func.__name__,
57 category=DeprecationWarning, stacklevel=2)
58 return func(*args, **kwargs)
59 newFunc.__name__ = func.__name__
60 newFunc.__doc__ = func.__doc__
61 newFunc.__dict__.update(func.__dict__)
62 return newFunc
63
64 @deprecated
65 def script_resolve_name(script_name, name):
66 """
67 Name resolver for scripts. Supports ROS_NAMESPACE. Does not
68 support remapping arguments.
69 @param name: name to resolve
70 @type name: str
71 @param script_name: name of script. script_name must not
72 contain a namespace.
73 @type script_name: str
74 @return: resolved name
75 @rtype: str
76 """
77 if not name: #empty string resolves to namespace
78 return roslib.names.get_ros_namespace()
79 #Check for global name: /foo/name resolves to /foo/name
80 if roslib.names.is_global(name):
81 return name
82 #Check for private name: ~name resolves to /caller_id/name
83 elif roslib.names.is_private(name):
84 return ns_join(roslib.names.make_caller_id(script_name), name[1:])
85 return roslib.names.get_ros_namespace() + name
86
87 @deprecated
88 def get_master():
89 """
90 Get an XMLRPC handle to the Master. It is recommended to use the
91 `rosgraph.masterapi` library instead, as it provides many
92 conveniences.
93
94 @return: XML-RPC proxy to ROS master
95 @rtype: xmlrpclib.ServerProxy
96 @raises ValueError if master URI is invalid
97 """
98 try:
99 import xmlrpc.client as xmlrpcclient #Python 3.x
100 except ImportError:
101 import xmlrpclib as xmlrpcclient #Python 2.x
102
103 # changed this to not look as sys args and remove dependency on roslib.rosenv for cleaner cleanup
104 uri = os.environ['ROS_MASTER_URI']
105 return xmlrpcclient.ServerProxy(uri)
106
107 @deprecated
108 def get_param_server():
109 """
110 @return: ServerProxy XML-RPC proxy to ROS parameter server
111 @rtype: xmlrpclib.ServerProxy
112 """
113 return get_master()
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 # Revision $Id$
33 # $Author$
34 """
35 ROS Service Description Language Spec
36 Implements U{http://ros.org/wiki/srv}
37 """
38
39 import os
40 import sys
41 import re
42
43 try:
44 from cStringIO import StringIO # Python 2.x
45 except ImportError:
46 from io import StringIO # Python 3.x
47
48 import roslib.msgs
49 import roslib.names
50 import roslib.packages
51 import roslib.resources
52
53 # don't directly use code from this, though we do depend on the
54 # manifest.Depend data type
55 import roslib.manifest
56
57 ## file extension
58 EXT = '.srv' #alias
59 SEP = '/' #e.g. std_msgs/String
60 ## input/output deliminator
61 IODELIM = '---'
62 COMMENTCHAR = roslib.msgs.COMMENTCHAR
63
64 VERBOSE = False
65 ## @return: True if msg-related scripts should print verbose output
66 def is_verbose():
67 return VERBOSE
68
69 ## set whether msg-related scripts should print verbose output
70 def set_verbose(v):
71 global VERBOSE
72 VERBOSE = v
73
74 class SrvSpecException(Exception): pass
75
76 # msg spec representation ##########################################
77
78 class SrvSpec(object):
79
80 def __init__(self, request, response, text, full_name = '', short_name = '', package = ''):
81 self.request = request
82 self.response = response
83 self.text = text
84 self.full_name = full_name
85 self.short_name = short_name
86 self.package = package
87
88 def __eq__(self, other):
89 if not other or not isinstance(other, SrvSpec):
90 return False
91 return self.request == other.request and \
92 self.response == other.response and \
93 self.text == other.text and \
94 self.full_name == other.full_name and \
95 self.short_name == other.short_name and \
96 self.package == other.package
97
98 def __ne__(self, other):
99 if not other or not isinstance(other, SrvSpec):
100 return True
101 return not self.__eq__(other)
102
103 def __repr__(self):
104 return "SrvSpec[%s, %s]"%(repr(self.request), repr(self.response))
105
106 # srv spec loading utilities ##########################################
107
108 ## @internal
109 ## predicate for filtering directory list. matches message files
110 def _srv_filter(f):
111 return os.path.isfile(f) and f.endswith(EXT)
112
113 # also used by doxymaker
114 def list_srv_types(package, include_depends):
115 """
116 list all services in the specified package
117 @param package: name of package to search
118 @type package: str
119 @param include_depends: if True, will also list services in package dependencies
120 @type include_depends: bool
121 @return: service type names
122 @rtype: [str]
123 """
124 types = roslib.resources.list_package_resources(package, include_depends, 'srv', _srv_filter)
125 return [x[:-len(EXT)] for x in types]
126
127 def srv_file(package, type_):
128 """
129 @param package: name of package .srv file is in
130 @type package: str
131 @param type_: type name of service
132 @type type_: str
133 @return: file path of .srv file in specified package
134 @rtype: str
135 """
136 return roslib.packages.resource_file(package, 'srv', type_+EXT)
137
138 def get_pkg_srv_specs(package):
139 """
140 List all messages that a package contains
141 @param depend: roslib.manifest.Depend object representing package
142 to load messages from
143 @type depend: Depend
144 @return: list of message type names and specs for package, as well as a list
145 of message names that could not be processed.
146 @rtype: [(str,roslib.MsgSpec), [str]]
147 """
148 #almost identical to roslib.msgs.get_pkg_msg_specs
149 types = list_srv_types(package, False)
150 specs = [] #no fancy list comprehension as we want to show errors
151 failures = []
152 for t in types:
153 try:
154 spec = load_from_file(srv_file(package, t), package)
155 specs.append(spec)
156 except Exception as e:
157 failures.append(t)
158 sys.stderr.write("ERROR: unable to load %s\n"%(t))
159 return specs, failures
160
161 def load_from_string(text, package_context='', full_name='', short_name=''):
162 """
163 @param text: .msg text
164 @type text: str
165 @param package_context: context to use for msgTypeName, i.e. the package name,
166 or '' to use local naming convention.
167 @type package_context: str
168 @return: Message type name and message specification
169 @rtype: roslib.MsgSpec
170 @raise roslib.MsgSpecException: if syntax errors or other problems are detected in file
171 """
172 text_in = StringIO()
173 text_out = StringIO()
174 accum = text_in
175 for l in text.split('\n'):
176 l = l.split(COMMENTCHAR)[0].strip() #strip comments
177 if l.startswith(IODELIM): #lenient, by request
178 accum = text_out
179 else:
180 accum.write(l+'\n')
181 # create separate roslib.msgs objects for each half of file
182
183 msg_in = roslib.msgs.load_from_string(text_in.getvalue(), package_context, '%sRequest'%(full_name), '%sRequest'%(short_name))
184 msg_out = roslib.msgs.load_from_string(text_out.getvalue(), package_context, '%sResponse'%(full_name), '%sResponse'%(short_name))
185 return SrvSpec(msg_in, msg_out, text, full_name, short_name, package_context)
186
187 def load_from_file(file_name, package_context=''):
188 """
189 Convert the .srv representation in the file to a SrvSpec instance.
190 @param file_name: name of file to load from
191 @type file_name: str
192 @param package_context: context to use for type name, i.e. the package name,
193 or '' to use local naming convention.
194 @type package_context: str
195 @return: Message type name and message specification
196 @rtype: (str, L{SrvSpec})
197 @raise SrvSpecException: if syntax errors or other problems are detected in file
198 """
199 if VERBOSE:
200 if package_context:
201 sys.stdout.write("Load spec from %s into namespace [%s]\n"%(file_name, package_context))
202 else:
203 sys.stdout.write("Load spec from %s\n"%(file_name))
204 base_file_name = os.path.basename(file_name)
205 type_ = base_file_name[:-len(EXT)]
206 base_type_ = type_
207 # determine the type name
208 if package_context:
209 while package_context.endswith(SEP):
210 package_context = package_context[:-1] #strip message separators
211 type_ = "%s%s%s"%(package_context, SEP, type_)
212 if not roslib.names.is_legal_resource_name(type_):
213 raise SrvSpecException("%s: %s is not a legal service type name"%(file_name, type_))
214
215 f = open(file_name, 'r')
216 try:
217 text = f.read()
218 return (type_, load_from_string(text, package_context, type_, base_type_))
219 finally:
220 f.close()
221
222
223
224
0 #! /usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34 # $Author$
35
36 """
37 Warning: do not use this library. It is unstable and most of the routines
38 here have been superceded by other libraries (e.g. rospkg). These
39 routines will likely be *deleted* in future releases.
40 """
41
42 import sys
43 import os
44 import getopt
45
46 STACK_FILE = 'stack.xml'
47
48 import roslib.manifestlib
49 # re-export symbols so that external code does not have to import manifestlib as well
50 from roslib.manifestlib import ManifestException, StackDepend
51
52 class StackManifest(roslib.manifestlib._Manifest):
53 """
54 Object representation of a ROS manifest file
55 """
56 __slots__ = []
57 def __init__(self):
58 """
59 Create an empty stack manifest instance.
60 """
61 super(StackManifest, self).__init__('stack')
62
63 def _stack_file_by_dir(stack_dir, required=True):
64 """
65 @param stack_dir: path to stack directory
66 @type stack_dir: str
67 @param required: require that the directory exist
68 @type required: bool
69 @return: path to manifest file of stack
70 @rtype: str
71 @raise InvalidROSPkgException: if required is True and manifest file cannot be located
72 """
73 try:
74 p = os.path.join(stack_dir, STACK_FILE)
75 if not required and not os.path.exists(p):
76 return p
77 if not os.path.isfile(p):
78 raise roslib.stacks.InvalidROSStackException("""
79 Stack '%(stack_dir)s' is improperly configured: no manifest file is present.
80 """%locals())
81 return p
82 except roslib.stacks.InvalidROSStackException as e:
83 if required:
84 raise
85
86 def stack_file(stack, required=True):
87 """
88 @param stack: stack name
89 @type stack: str
90 @param required: require that the directory exist
91 @type required: bool
92 @return: path to manifest file of stack
93 @rtype: str
94 @raise InvalidROSPkgException: if required is True and manifest file cannot be located
95 """
96 d = roslib.stacks.get_stack_dir(stack)
97 return _stack_file_by_dir(d, required)
98
99 def parse_file(file):
100 """
101 Parse stack.xml file
102 @param file: stack.xml file path
103 @param file: str
104 @return: StackManifest instance
105 @rtype: L{StackManifest}
106 """
107 return roslib.manifestlib.parse_file(StackManifest(), file)
108
109 def parse(string, filename='string'):
110 """
111 Parse stack.xml string contents
112 @param string: stack.xml contents
113 @type string: str
114 @return: StackManifest instance
115 @rtype: L{StackManifest}
116 """
117 s = roslib.manifestlib.parse(StackManifest(), string, filename)
118 #TODO: validate
119 return s
0 #! /usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34
35 """
36 Warning: do not use this library. It is unstable and most of the routines
37 here have been superceded by other libraries (e.g. rospkg). These
38 routines will likely be *deleted* in future releases.
39 """
40
41 import os
42 import sys
43 import re
44
45 import roslib.packages
46 import roslib.stack_manifest
47
48 import rospkg
49
50 ROS_ROOT=rospkg.environment.ROS_ROOT
51 ROS_PACKAGE_PATH=rospkg.environment.ROS_PACKAGE_PATH
52
53 STACK_FILE = 'stack.xml'
54 ROS_STACK = 'ros'
55
56 class ROSStackException(Exception): pass
57 class InvalidROSStackException(ROSStackException): pass
58
59 def stack_of(pkg, env=None):
60 """
61 @param env: override environment variables
62 @type env: {str: str}
63 @return: name of stack that pkg is in, or None if pkg is not part of a stack
64 @rtype: str
65 @raise roslib.packages.InvalidROSPkgException: if pkg cannot be located
66 """
67 if env is None:
68 env = os.environ
69 pkg_dir = roslib.packages.get_pkg_dir(pkg, ros_root=env[ROS_ROOT], ros_package_path=env.get(ROS_PACKAGE_PATH, None))
70 d = pkg_dir
71 while d and os.path.dirname(d) != d:
72 stack_file = os.path.join(d, STACK_FILE)
73 if os.path.exists(stack_file):
74 #TODO: need to resolve issues regarding whether the
75 #stack.xml or the directory defines the stack name
76 return os.path.basename(d)
77 d = os.path.dirname(d)
78
79 def get_stack_dir(stack, env=None):
80 """
81 Get the directory of a ROS stack. This will initialize an internal
82 cache and return cached results if possible.
83
84 This routine is not thread-safe to os.environ changes.
85
86 @param env: override environment variables
87 @type env: {str: str}
88 @param stack: name of ROS stack to locate on disk
89 @type stack: str
90 @return: directory of stack.
91 @rtype: str
92 @raise InvalidROSStackException: if stack cannot be located.
93 """
94 _init_rosstack(env=env)
95 try:
96 return _rosstack.get_path(stack)
97 except rospkg.ResourceNotFound:
98 # preserve old signature
99 raise InvalidROSStackException(stack)
100
101 _rosstack = None
102 _ros_paths = None
103
104 def _init_rosstack(env=None):
105 global _rosstack, _ros_paths
106 if env is None:
107 env = os.environ
108 ros_paths = rospkg.get_ros_paths(env)
109 if ros_paths != _ros_paths:
110 _ros_paths = ros_paths
111 _rosstack = rospkg.RosStack(ros_paths)
112
113 def list_stacks(env=None):
114 """
115 Get list of all ROS stacks. This uses an internal cache.
116
117 This routine is not thread-safe to os.environ changes.
118
119 @param env: override environment variables
120 @type env: {str: str}
121 @return: complete list of stacks names in ROS environment
122 @rtype: [str]
123 """
124 _init_rosstack(env=env)
125 return _rosstack.list()
126
127 def list_stacks_by_path(path, stacks=None, cache=None):
128 """
129 List ROS stacks within the specified path.
130
131 Optionally, a cache dictionary can be provided, which will be
132 updated with the stack->path mappings. list_stacks_by_path() does
133 NOT returned cached results -- it only updates the cache.
134
135 @param path: path to list stacks in
136 @type path: str
137 @param stacks: list of stacks to append to. If stack is
138 already present in stacks, it will be ignored.
139 @type stacks: [str]
140 @param cache: (optional) stack path cache to update. Maps stack name to directory path.
141 @type cache: {str: str}
142 @return: complete list of stack names in ROS environment. Same as stacks parameter.
143 @rtype: [str]
144 """
145 if stacks is None:
146 stacks = []
147 MANIFEST_FILE = rospkg.MANIFEST_FILE
148 basename = os.path.basename
149 for d, dirs, files in os.walk(path, topdown=True):
150 if STACK_FILE in files:
151 stack = basename(d)
152 if stack not in stacks:
153 stacks.append(stack)
154 if cache is not None:
155 cache[stack] = d
156 del dirs[:]
157 continue #leaf
158 elif MANIFEST_FILE in files:
159 del dirs[:]
160 continue #leaf
161 elif 'rospack_nosubdirs' in files:
162 del dirs[:]
163 continue #leaf
164 # remove hidden dirs (esp. .svn/.git)
165 [dirs.remove(di) for di in dirs if di[0] == '.']
166 for sub_d in dirs:
167 # followlinks=True only available in Python 2.6, so we
168 # have to implement manually
169 sub_p = os.path.join(d, sub_d)
170 if os.path.islink(sub_p):
171 stacks.extend(list_stacks_by_path(sub_p, cache=cache))
172 return stacks
173
174 # #2022
175 def expand_to_packages(names, env=None):
176 """
177 Expand names into a list of packages. Names can either be of packages or stacks.
178
179 @param names: names of stacks or packages
180 @type names: [str]
181 @return: ([packages], [not_found]). expand_packages() returns two
182 lists. The first is of packages names. The second is a list of
183 names for which no matching stack or package was found. Lists may have duplicates.
184 @rtype: ([str], [str])
185 """
186 if env is None:
187 env = os.environ
188 ros_paths = rospkg.get_ros_paths(env)
189 rospack = rospkg.RosPack(ros_paths)
190 rosstack = rospkg.RosStack(ros_paths)
191 return rospkg.expand_to_packages(names, rospack, rosstack)
192
193 def get_stack_version(stack, env=None):
194 """
195 @param env: override environment variables
196 @type env: {str: str}
197
198 @return: version number of stack, or None if stack is unversioned.
199 @rtype: str
200 """
201 _init_rosstack(env=env)
202 return _rosstack.get_stack_version(stack)
203
204 def get_stack_version_by_dir(stack_dir):
205 """
206 Get stack version where stack_dir points to root directory of stack.
207
208 @param env: override environment variables
209 @type env: {str: str}
210
211 @return: version number of stack, or None if stack is unversioned.
212 @rtype: str
213 """
214 # REP 109: check for <version> tag first, then CMakeLists.txt
215 manifest_filename = os.path.join(stack_dir, STACK_FILE)
216 if os.path.isfile(manifest_filename):
217 m = roslib.stack_manifest.parse_file(manifest_filename)
218 if m.version:
219 return m.version
220
221 cmake_filename = os.path.join(stack_dir, 'CMakeLists.txt')
222 if os.path.isfile(cmake_filename):
223 with open(cmake_filename) as f:
224 return _get_cmake_version(f.read())
225 else:
226 return None
227
228 def _get_cmake_version(text):
229 for l in text.split('\n'):
230 if l.strip().startswith('rosbuild_make_distribution'):
231 x_re = re.compile(r'[()]')
232 lsplit = x_re.split(l.strip())
233 if len(lsplit) < 2:
234 raise ReleaseException("couldn't find version number in CMakeLists.txt:\n\n%s"%l)
235 return lsplit[1]
0 #!/usr/bin/env python
1 # this node only exists to test find_node functionality
2
3 print "hello"
0 <package>
1 <description brief="a brief description">Line 1
2 Line 2
3 </description>
4 <version>1.2.4</version> <!-- bad -->
5 <author>The authors
6 go here</author>
7 <license>Public Domain
8 with other stuff</license>
9 <url>http://pr.willowgarage.com/package/</url>
10 <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
11 <depend package="pkgname" />
12 <depend package="common"/>
13 <export>
14 <cpp cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lros"/>
15 <cpp os="osx" cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lrosthread -framework CoreServices"/>
16 </export>
17 <rosdep name="python" />
18 <rosdep name="bar" />
19 <rosdep name="baz" />
20 </package>
0 <notpackage>
1 <description brief="a brief description">Line 1
2 Line 2
3 </description>
4 <author>The authors
5 go here</author>
6 <license>Public Domain
7 with other stuff</license>
8 <url>http://pr.willowgarage.com/package/</url>
9 <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
10 <depend package="pkgname" />
11 <depend package="common"/>
12 <export>
13 <cpp cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lros"/>
14 <cpp os="osx" cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lrosthread -framework CoreServices"/>
15 </export>
16 <rosdep name="python" />
17 <rosdep name="bar" />
18 <rosdep name="baz" />
19 </notpackage>
0 <package>
1 <description brief="a brief description">Line 1
2 </description>
3 <description brief="a brief second description">Second description
4 </description>
5
6 <author>The authors
7 go here</author>
8 <license>Public Domain
9 with other stuff</license>
10 </package>
0 <package>
1 <description brief="a brief description">Line 1
2 Line 2
3 </description>
4 <author>The authors
5 go here</author>
6 <license>Public Domain
7 with other stuff</license>
8 <url>http://pr.willowgarage.com/package/</url>
9 <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
10 <depend package="pkgname" />
11 <depend package="common"/>
12 <export>
13 <cpp cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lros"/>
14 <cpp os="osx" cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lrosthread -framework CoreServices"/>
15 </export>
16 <rosdep name="python" />
17 <rosdep name="bar" />
18 <rosdep name="baz" />
19 </package>
0 <stack>
1 <description brief="a brief description">Line 1
2 Line 2
3 </description>
4 <author>The authors
5 go here</author>
6 <license>Public Domain
7 with other stuff</license>
8 <url>http://ros.org/stack/</url>
9 <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
10 <depend stack="stackname" />
11 <depend stack="common"/>
12 </stack>
0 <stack>
1 <description brief="a brief description">Line 1
2 Line 2
3 </description>
4 <author>The authors
5 go here</author>
6 <license>Public Domain
7 with other stuff</license>
8 <url>http://ros.org/stack/</url>
9 <version>1.2.3</version>
10 <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
11 <depend stack="stackname" />
12 <depend stack="common"/>
13 </stack>
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 #include <vector>
30
31 #include <gtest/gtest.h>
32 #include <ros/package.h>
33 #include <sys/time.h>
34
35 using namespace ros;
36
37 TEST(Package, getPath)
38 {
39 std::string path = package::getPath("roslib");
40 printf("Path: %s\n", path.c_str());
41 ASSERT_FALSE(path.empty());
42 }
43
44 TEST(Package, getAll)
45 {
46 using package::V_string;
47 V_string packages;
48 ASSERT_TRUE(package::getAll(packages));
49 ASSERT_FALSE(packages.empty());
50
51 V_string::iterator it = packages.begin();
52 V_string::iterator end = packages.end();
53 for (; it != end; ++it)
54 {
55 printf("Package: %s\n", it->c_str());
56 }
57 }
58
59 int main(int argc, char **argv){
60 testing::InitGoogleTest(&argc, argv);
61 return RUN_ALL_TESTS();
62 }
63
0 <package>
1 <description brief="bar">
2
3 bar
4
5 </description>
6 <author>Ken Conley</author>
7 <license>BSD</license>
8 <review status="unreviewed" notes=""/>
9 <url>http://ros.org/wiki/bar</url>
10
11 </package>
12
13
0 <package>
1 <description brief="foo">
2
3 foo
4
5 </description>
6 <author>Ken Conley</author>
7 <license>BSD</license>
8 <review status="unreviewed" notes=""/>
9 <url>http://ros.org/wiki/foo</url>
10
11 </package>
12
13
0 <package>
1 <description brief="foo">
2
3 foo
4
5 </description>
6 <author>Ken Conley</author>
7 <license>BSD</license>
8 <review status="unreviewed" notes=""/>
9 <url>http://ros.org/wiki/foo</url>
10
11 </package>
12
13
0 <package>
1 <description brief="roslib tests">
2 Unit tests verifying that roslib is operating as expected.
3 </description>
4 <author>none</author>
5 <license>BSD</license>
6 <platform os="test_os" version="test_version"/>
7 </package>
0 [nosetests]
1 with-xunit=1
2 with-coverage=1
3 cover-package=roslib
4 tests=test_roslib_manifest.py,test_roslib_names.py,test_roslib_packages.py,test_roslib.py, test_roslib_rosenv.py, test_roslib_stack_manifest.py, test_roslib_stacks.py, test_roslib_exceptions.py, test_roslib_manifestlib.py
5
0 cmake_minimum_required(VERSION 2.4.6)
1 include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
2 set(ROSPACK_MAKEDIST true)
3 rosbuild_make_distribution(1.5.0-cmake)
0 <stack>
1 <description brief="bar">
2 bar
3 </description>
4 <author>Ken Conley</author>
5 <license>BSD</license>
6 </stack>
7
8
0 cmake_minimum_required(VERSION 2.4.6)
1 include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
2 set(ROSPACK_MAKEDIST true)
3 rosbuild_make_distribution(1.5.0-cmake)
0 <package>
1 <description brief="foo_pkg">
2
3 foo_pkg
4
5 </description>
6 <author>Ken Conley</author>
7 <license>BSD</license>
8 <review status="unreviewed" notes=""/>
9 <url>http://ros.org/wiki/foo_pkg</url>
10
11 </package>
12
13
0 <package>
1 <description brief="foo_pkg_2">
2
3 foo_pkg_2
4
5 </description>
6 <author>Ken Conley</author>
7 <license>BSD</license>
8 <review status="unreviewed" notes=""/>
9 <url>http://ros.org/wiki/foo_pkg_2</url>
10
11 </package>
12
13
0 <stack>
1 <name>foo</name>
2 <description brief="foo">
3 foo
4 </description>
5 <author>Ken Conley</author>
6 <version>
7 1.6.0-manifest
8 </version>
9 <license>BSD</license>
10 <copyright/>
11 </stack>
12
13
0 <package>
1 <description brief="foo">
2 foo
3 </description>
4 <author>Ken Conley</author>
5 <license>BSD</license>
6 </package>
7
8
0 ../stack_tests/s1
0 <package>
1 <description brief="roslib tests">
2 Fake
3 </description>
4 <author>No one</author>
5 <license>BSD</license>
6 <review status="test" notes=""/>
7
8 <depend package="roslib" />
9 <depend package="rosunit" />
10 </package>
0 <stack>
1 <description brief="bar">
2 bar
3 </description>
4 <author>Ken Conley</author>
5 <license>BSD</license>
6 </stack>
7
8
0 <stack>
1 <description brief="bar">
2 bar
3 </description>
4 <author>Ken Conley</author>
5 <license>BSD</license>
6 </stack>
7
8
0 <package>
1 <description brief="roslib tests">
2 Fake
3 </description>
4 <author>No one</author>
5 <license>BSD</license>
6 <review status="test" notes=""/>
7
8 <depend package="roslib" />
9 <depend package="rosunit" />
10 </package>
0 <stack>
1 <description brief="foo">
2 foo
3 </description>
4 <author>Ken Conley</author>
5 <license>BSD</license>
6 </stack>
7
8
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
32 import os
33 import sys
34
35 def test_load_manifest():
36 # this is a bit of a noop as it's a prerequisite of running with rosunit
37 import roslib
38 roslib.load_manifest('roslib')
39
40 def test_interactive():
41 import roslib
42
43 # make sure that it's part of high-level API
44 assert not roslib.is_interactive(), "interactive should be false by default"
45 for v in [True, False]:
46 roslib.set_interactive(v)
47 assert v == roslib.is_interactive()
48
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
32 import os
33 import sys
34
35 def test_exceptions():
36 from roslib.exceptions import ROSLibException
37 assert isinstance(ROSLibException(), Exception)
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
32 import os
33 import sys
34 import unittest
35
36 import roslib
37
38 def get_test_path():
39 return os.path.abspath(os.path.dirname(__file__))
40
41 class RoslibManifestTest(unittest.TestCase):
42
43 def test_ManifestException(self):
44 from roslib.manifest import ManifestException
45 self.assert_(isinstance(ManifestException(), Exception))
46
47 def test_Depend(self):
48 from roslib.manifestlib import Depend, ManifestException
49 for bad in [None, '']:
50 try:
51 Depend(bad)
52 self.fail("should have failed on [%s]"%bad)
53 except ValueError: pass
54
55 d = Depend('roslib')
56 self.assertEquals('roslib', str(d))
57 self.assertEquals('roslib', repr(d))
58
59 self.assertEquals('<depend package="roslib" />',d.xml())
60 self.assertEquals(d, Depend('roslib'))
61 self.assertNotEquals(d, Depend('roslib2'))
62 self.assertNotEquals(d, 1)
63
64 def test_ROSDep(self):
65 from roslib.manifest import ROSDep, ManifestException
66 for bad in [None, '']:
67 try:
68 rd = ROSDep(bad)
69 self.fail("should have failed on [%s]"%bad)
70 except ValueError: pass
71
72 rd = ROSDep('python')
73 self.assertEquals('<rosdep name="python" />',rd.xml())
74
75 def test_VersionControl(self):
76 from roslib.manifest import VersionControl, ManifestException
77 ros_svn = 'https://ros.svn.sf.net/svnroot'
78
79 bad = [
80 (None, ros_svn),
81 ]
82 for type_, url in bad:
83 try:
84 VersionControl(type_,url)
85 self.fail("should have failed on [%s] [%s]"%(type_, url))
86 except ValueError: pass
87
88 tests = [
89 ('svn', ros_svn, '<versioncontrol type="svn" url="%s" />'%ros_svn),
90 ('cvs', None, '<versioncontrol type="cvs" />'),
91 ]
92 for type_, url, xml in tests:
93 vc = VersionControl(type_, url)
94 self.assertEquals(type_, vc.type)
95 self.assertEquals(url, vc.url)
96 self.assertEquals(xml, vc.xml())
97
98 def _subtest_parse_example1(self, m):
99 from roslib.manifest import Manifest
100 self.assert_(isinstance(m, Manifest))
101 self.assertEquals("a brief description", m.brief)
102 self.assertEquals("Line 1\nLine 2", m.description.strip())
103 self.assertEquals("The authors\ngo here", m.author.strip())
104 self.assertEquals("Public Domain\nwith other stuff", m.license.strip())
105 self.assertEquals("http://pr.willowgarage.com/package/", m.url)
106 self.assertEquals("http://www.willowgarage.com/files/willowgarage/robot10.jpg", m.logo)
107 dpkgs = [d.package for d in m.depends]
108 self.assertEquals(set(['pkgname', 'common']), set(dpkgs))
109 rdpkgs = [d.name for d in m.rosdeps]
110 self.assertEquals(set(['python', 'bar', 'baz']), set(rdpkgs))
111
112 def test_parse_example1_file(self):
113 from roslib.manifest import parse_file, Manifest
114 p = os.path.join(get_test_path(), 'manifest_tests', 'example1.xml')
115 self._subtest_parse_example1(parse_file(p))
116
117 def test_parse_example1_string(self):
118 from roslib.manifest import parse, Manifest
119 self._subtest_parse_example1(parse(EXAMPLE1))
120
121 def test_Manifest_str(self):
122 # just make sure it doesn't crash
123 from roslib.manifest import parse
124 str(parse(EXAMPLE1))
125
126 def test_Manifest_xml(self):
127 from roslib.manifest import parse
128 m = parse(EXAMPLE1)
129 self._subtest_parse_example1(m)
130 # verify roundtrip
131 m2 = parse(m.xml())
132 self._subtest_parse_example1(m2)
133
134
135 def test_parse_bad_file(self):
136 from roslib.manifest import parse_file, Manifest
137 # have to import from ManifestException due to weirdness when run in --cov mode
138 from roslib.manifestlib import ManifestException
139 base_p = os.path.join(get_test_path(), 'manifest_tests')
140 for b in ['bad1.xml', 'bad2.xml', 'bad3.xml']:
141 p = os.path.join(base_p, b)
142 try:
143 parse_file(p)
144 self.fail("parse should have failed on bad manifest")
145 except ManifestException, e:
146 print str(e)
147 self.assert_(b in str(e), "file name should be in error message: %s"%(str(e)))
148
149 EXAMPLE1 = """<package>
150 <description brief="a brief description">Line 1
151 Line 2
152 </description>
153 <author>The authors
154 go here</author>
155 <license>Public Domain
156 with other stuff</license>
157 <url>http://pr.willowgarage.com/package/</url>
158 <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
159 <depend package="pkgname" />
160 <depend package="common"/>
161 <export>
162 <cpp cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lros"/>
163 <cpp os="osx" cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lrosthread -framework CoreServices"/>
164 </export>
165 <rosdep name="python" />
166 <rosdep name="bar" />
167 <rosdep name="baz" />
168 <rosbuild2>
169 <depend thirdparty="thisshouldbeokay"/>
170 </rosbuild2>
171 </package>"""
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
32 import os
33 import struct
34 import sys
35 import unittest
36
37 import roslib
38
39 class RoslibManifestlibTest(unittest.TestCase):
40
41 def test_ManifestException(self):
42 from roslib.manifestlib import ManifestException
43 self.assert_(isinstance(ManifestException(), Exception))
44
45 def test_Platform(self):
46 from roslib.manifestlib import Platform, ManifestException
47 for bad in [None, '']:
48 try:
49 Platform(bad, '1')
50 self.fail("should have failed on [%s]"%bad)
51 except ValueError: pass
52 try:
53 Platform('ubuntu', bad)
54 self.fail("should have failed on [%s]"%bad)
55 except ValueError: pass
56
57 p = Platform('ubuntu', '8.04')
58 self.assertEquals('ubuntu 8.04', str(p))
59 self.assertEquals('ubuntu 8.04', repr(p))
60
61 self.assertEquals('<platform os="ubuntu" version="8.04"/>',p.xml())
62 self.assertEquals(p, Platform('ubuntu', '8.04'))
63 self.assertEquals(p, Platform('ubuntu', '8.04', notes=None))
64 self.assertNotEquals(p, Platform('ubuntu', '8.04', 'some notes'))
65 self.assertNotEquals(p, 'foo')
66 self.assertNotEquals(p, 1)
67
68 # note: probably actually "osx"
69 p = Platform('OS X', '10.6', 'macports')
70 self.assertEquals('OS X 10.6', str(p))
71 self.assertEquals('OS X 10.6', repr(p))
72
73 self.assertEquals('<platform os="OS X" version="10.6" notes="macports"/>',p.xml())
74 self.assertEquals(p, p)
75 self.assertEquals(p, Platform('OS X', '10.6', 'macports'))
76 self.assertNotEquals(p, Platform('OS X', '10.6'))
77 self.assertNotEquals(p, 'foo')
78 self.assertNotEquals(p, 1)
79
80
81 def test_Depend(self):
82 from roslib.manifestlib import Depend, StackDepend, ManifestException
83 for bad in [None, '']:
84 try:
85 Depend(bad)
86 self.fail("should have failed on [%s]"%bad)
87 except ValueError: pass
88
89 d = Depend('roslib')
90 self.assertEquals('roslib', str(d))
91 self.assertEquals('roslib', repr(d))
92
93 self.assertEquals('<depend package="roslib" />',d.xml())
94 self.assertEquals(d, Depend('roslib'))
95 self.assertNotEquals(d, StackDepend('roslib'))
96 self.assertNotEquals(d, Depend('roslib2'))
97 self.assertNotEquals(d, 1)
98
99 def test_StackDepend(self):
100 from roslib.manifestlib import Depend, StackDepend, ManifestException
101 for bad in [None, '']:
102 try:
103 StackDepend(bad)
104 self.fail("should have failed on [%s]"%bad)
105 except ValueError: pass
106
107 d = StackDepend('common')
108 self.assertEquals('common', str(d))
109 self.assertEquals('common', repr(d))
110
111 self.assertEquals('<depend stack="common" />',d.xml())
112 self.assertEquals(d, StackDepend('common'))
113 self.assertNotEquals(d, Depend('common'))
114 self.assertNotEquals(d, StackDepend('common2'))
115 self.assertNotEquals(d, 1)
116
117 def test_ROSDep(self):
118 from roslib.manifestlib import ROSDep, ManifestException
119 for bad in [None, '']:
120 try:
121 rd = ROSDep(bad)
122 self.fail("should have failed on [%s]"%bad)
123 except ValueError: pass
124
125 rd = ROSDep('python')
126 self.assertEquals('<rosdep name="python" />',rd.xml())
127
128 def test_VersionControl(self):
129 from roslib.manifestlib import VersionControl, ManifestException
130 ros_svn = 'https://ros.svn.sf.net/svnroot'
131
132 bad = [
133 (None, ros_svn),
134 ]
135 for type_, url in bad:
136 try:
137 VersionControl(type_,url)
138 self.fail("should have failed on [%s] [%s]"%(type_, url))
139 except ValueError: pass
140
141 tests = [
142 ('svn', ros_svn, '<versioncontrol type="svn" url="%s" />'%ros_svn),
143 ('cvs', None, '<versioncontrol type="cvs" />'),
144 ]
145 for type_, url, xml in tests:
146 vc = VersionControl(type_, url)
147 self.assertEquals(type_, vc.type)
148 self.assertEquals(url, vc.url)
149 self.assertEquals(xml, vc.xml())
150
151 def _subtest_parse_example1(self, m):
152 from roslib.manifestlib import _Manifest
153 self.assert_(isinstance(m, _Manifest))
154 self.assertEquals("a brief description", m.brief)
155 self.assertEquals("Line 1\nLine 2", m.description.strip())
156 self.assertEquals("The authors\ngo here", m.author.strip())
157 self.assertEquals("Public Domain\nwith other stuff", m.license.strip())
158 self.assertEquals("http://pr.willowgarage.com/package/", m.url)
159 self.assertEquals("http://www.willowgarage.com/files/willowgarage/robot10.jpg", m.logo)
160 dpkgs = [d.package for d in m.depends]
161 self.assertEquals(set(['pkgname', 'common']), set(dpkgs))
162 rdpkgs = [d.name for d in m.rosdeps]
163 self.assertEquals(set(['python', 'bar', 'baz']), set(rdpkgs))
164 for p in m.platforms:
165 if p.os == 'ubuntu':
166 self.assertEquals("8.04", p.version)
167 self.assertEquals('', p.notes)
168 elif p.os == 'OS X':
169 self.assertEquals("10.6", p.version)
170 self.assertEquals("macports", p.notes)
171 else:
172 self.fail("unknown platform "+str(p))
173
174 def _subtest_parse_stack_example1(self, m):
175 from roslib.manifestlib import _Manifest
176 self.assert_(isinstance(m, _Manifest))
177 self.assertEquals('stack', m._type)
178 self.assertEquals("a brief description", m.brief)
179 self.assertEquals("Line 1\nLine 2", m.description.strip())
180 self.assertEquals("The authors\ngo here", m.author.strip())
181 self.assertEquals("Public Domain\nwith other stuff", m.license.strip())
182 self.assertEquals("http://ros.org/stack/", m.url)
183 self.assertEquals("http://www.willowgarage.com/files/willowgarage/robot10.jpg", m.logo)
184 dpkgs = [d.stack for d in m.depends]
185 self.assertEquals(set(['stackname', 'common']), set(dpkgs))
186 self.assertEquals([], m.rosdeps)
187 self.assertEquals([], m.exports)
188
189 def _subtest_parse_stack_version(self, m):
190 self.assertEquals("1.2.3", m.version)
191
192 def test_parse_example1_file(self):
193 from roslib.manifestlib import parse_file, _Manifest
194 p = os.path.join(get_test_path(), 'manifest_tests', 'example1.xml')
195 self._subtest_parse_example1(parse_file(_Manifest(), p))
196
197 p = os.path.join(get_test_path(), 'manifest_tests', 'stack_example1.xml')
198 self._subtest_parse_stack_example1(parse_file(_Manifest('stack'), p))
199
200 p = os.path.join(get_test_path(), 'manifest_tests', 'stack_version.xml')
201 self._subtest_parse_stack_version(parse_file(_Manifest('stack'), p))
202
203 def test_parse_example1_string(self):
204 from roslib.manifestlib import parse, _Manifest
205 self._subtest_parse_example1(parse(_Manifest(), EXAMPLE1))
206 self._subtest_parse_stack_example1(parse(_Manifest('stack'), STACK_EXAMPLE1))
207
208 def test__Manifest(self):
209 from roslib.manifestlib import _Manifest
210 m = _Manifest()
211 # check defaults
212 self.assertEquals('package', m._type)
213 m = _Manifest('stack')
214 self.assertEquals('stack', m._type)
215
216 def test_Manifest_str(self):
217 # just make sure it doesn't crash
218 from roslib.manifestlib import parse, _Manifest
219 str(parse(_Manifest(), EXAMPLE1))
220
221 def test_Manifest_xml(self):
222 from roslib.manifestlib import parse, _Manifest
223 m = _Manifest()
224 parse(m, EXAMPLE1)
225 self._subtest_parse_example1(m)
226 # verify roundtrip
227 m2 = _Manifest()
228 parse(m2, m.xml())
229 self._subtest_parse_example1(m2)
230
231 # bad file examples should be more like the roslaunch tests where there is just 1 thing wrong
232 def test_parse_bad_file(self):
233 from roslib.manifestlib import parse_file, _Manifest, ManifestException
234 base_p = os.path.join(get_test_path(), 'manifest_tests')
235 m = _Manifest()
236 for b in ['bad1.xml', 'bad2.xml', 'bad3.xml']:
237 p = os.path.join(base_p, b)
238 try:
239 parse_file(m, p)
240 self.fail("parse should have failed on bad manifest")
241 except ManifestException, e:
242 print str(e)
243 self.assert_(b in str(e), "file name should be in error message [%s]"%(str(e)))
244
245 EXAMPLE1 = """<package>
246 <description brief="a brief description">Line 1
247 Line 2
248 </description>
249 <author>The authors
250 go here</author>
251 <license>Public Domain
252 with other stuff</license>
253 <url>http://pr.willowgarage.com/package/</url>
254 <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
255 <depend package="pkgname" />
256 <depend package="common"/>
257 <export>
258 <cpp cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lros"/>
259 <cpp os="osx" cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lrosthread -framework CoreServices"/>
260 </export>
261 <rosdep name="python" />
262 <rosdep name="bar" />
263 <rosdep name="baz" />
264 <platform os="ubuntu" version="8.04" />
265 <platform os="OS X" version="10.6" notes="macports" />
266 <rosbuild2>
267 <depend thirdparty="thisshouldbeokay"/>
268 </rosbuild2>
269 </package>"""
270
271 STACK_EXAMPLE1 = """<stack>
272 <description brief="a brief description">Line 1
273 Line 2
274 </description>
275 <author>The authors
276 go here</author>
277 <license>Public Domain
278 with other stuff</license>
279 <url>http://ros.org/stack/</url>
280 <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
281 <depend stack="stackname" />
282 <depend stack="common"/>
283 </stack>"""
284
285 STACK_INVALID1 = """<stack>
286 <description brief="a brief description">Line 1</description>
287 <author>The authors</author>
288 <license>Public Domain</license>
289 <rosdep name="python" />
290 </stack>"""
291
292 STACK_INVALID2 = """<stack>
293 <description brief="a brief description">Line 1</description>
294 <author>The authors</author>
295 <license>Public Domain</license>
296 <export>
297 <cpp cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lros"/>
298 <cpp os="osx" cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lrosthread -framework CoreServices"/>
299 </export>
300 </stack>"""
301
302
303 def get_test_path():
304 return os.path.abspath(os.path.dirname(__file__))
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
32 import os
33 import sys
34 import unittest
35
36 import roslib.names
37
38 class NamesTest(unittest.TestCase):
39
40 def test_get_ros_namespace(self):
41 if 'ROS_NAMESPACE' in os.environ:
42 rosns = os.environ['ROS_NAMESPACE']
43 del os.environ['ROS_NAMESPACE']
44 else:
45 rosns = None
46 sysargv = sys.argv
47
48 try:
49 sys.argv = []
50 self.assertEquals('/', roslib.names.get_ros_namespace())
51 self.assertEquals('/', roslib.names.get_ros_namespace(argv=[]))
52 self.assertEquals('/', roslib.names.get_ros_namespace(env={}))
53 self.assertEquals('/', roslib.names.get_ros_namespace(env={}, argv=[]))
54
55 os.environ['ROS_NAMESPACE'] = 'unresolved'
56 self.assertEquals('/unresolved/', roslib.names.get_ros_namespace())
57 self.assertEquals('/unresolved/', roslib.names.get_ros_namespace(env={'ROS_NAMESPACE': 'unresolved'}))
58 sys.argv = ['foo', '__ns:=unresolved_override']
59 self.assertEquals('/unresolved_override/', roslib.names.get_ros_namespace(env={'ROS_NAMESPACE': 'unresolved'}))
60 self.assertEquals('/override2/', roslib.names.get_ros_namespace(env={'ROS_NAMESPACE': 'unresolved'}, argv=['foo', '__ns:=override2']))
61
62 sys.argv = []
63 os.environ['ROS_NAMESPACE'] = '/resolved/'
64 self.assertEquals('/resolved/', roslib.names.get_ros_namespace())
65 self.assertEquals('/resolved/', roslib.names.get_ros_namespace(env={'ROS_NAMESPACE': '/resolved'}))
66
67 del os.environ['ROS_NAMESPACE']
68
69 sys.argv = ['foo', '__ns:=unresolved_ns']
70 self.assertEquals('/unresolved_ns/', roslib.names.get_ros_namespace())
71 self.assertEquals('/unresolved_ns2/', roslib.names.get_ros_namespace(argv=['foo', '__ns:=unresolved_ns2']))
72 sys.argv = ['foo', '__ns:=/resolved_ns/']
73 self.assertEquals('/resolved_ns/', roslib.names.get_ros_namespace())
74 self.assertEquals('/resolved_ns2/', roslib.names.get_ros_namespace(argv=['foo', '__ns:=resolved_ns2']))
75 finally:
76 sys.argv = sysargv
77
78 # restore
79 if rosns:
80 os.environ['ROS_NAMESPACE'] = rosns
81
82 def test_make_global_ns(self):
83 from roslib.names import make_global_ns
84
85 for n in ['~foo']:
86 try:
87 make_global_ns(n)
88 self.fail("make_global_ns should fail on %s"%n)
89 except ValueError: pass
90
91 self.assertEquals('/foo/', make_global_ns('foo'))
92 self.assertEquals('/', make_global_ns(''))
93 self.assertEquals('/foo/', make_global_ns('/foo'))
94 self.assertEquals('/foo/', make_global_ns('/foo/'))
95 self.assertEquals('/foo/bar/', make_global_ns('/foo/bar'))
96 self.assertEquals('/foo/bar/', make_global_ns('/foo/bar/'))
97
98 def test_is_global(self):
99 try:
100 roslib.names.is_global(None)
101 self.fail("is_global should raise exception on invalid param")
102 except: pass
103 tests = ['/', '/global', '/global2']
104 for t in tests:
105 self.assert_(roslib.names.is_global(t))
106 fails = ['', 'not_global', 'not/global']
107 for t in fails:
108 self.failIf(roslib.names.is_global(t))
109
110 def test_is_private(self):
111 try:
112 roslib.names.is_private(None)
113 self.fail("is_private should raise exception on invalid param")
114 except: pass
115 tests = ['~name', '~name/sub']
116 for t in tests:
117 self.assert_(roslib.names.is_private(t))
118 fails = ['', 'not_private', 'not/private', 'not/~private', '/not/~private']
119 for t in fails:
120 self.failIf(roslib.names.is_private(t))
121
122 def test_namespace(self):
123 from roslib.names import namespace
124 try:
125 namespace(1)
126 self.fail("1")
127 except TypeError: pass
128 try:
129 namespace(None)
130 self.fail("None")
131 except ValueError: pass
132 self.assertEquals('/', namespace(''))
133 self.assertEquals('/', namespace('/'))
134 self.assertEquals('/', namespace('/foo'))
135 self.assertEquals('/', namespace('/foo/'))
136 self.assertEquals('/foo/', namespace('/foo/bar'))
137 self.assertEquals('/foo/', namespace('/foo/bar/'))
138 self.assertEquals('/foo/bar/', namespace('/foo/bar/baz'))
139 self.assertEquals('/foo/bar/', namespace('/foo/bar/baz/'))
140
141 # unicode tests
142 self.assertEquals(u'/', namespace(u''))
143 self.assertEquals(u'/', namespace(u'/'))
144 self.assertEquals(u'/foo/bar/', namespace(u'/foo/bar/baz/'))
145
146 def test_nsjoin(self):
147 from roslib.names import ns_join
148
149 # private and global names cannot be joined
150 self.assertEquals('~name', ns_join('/foo', '~name'))
151 self.assertEquals('/name', ns_join('/foo', '/name'))
152 self.assertEquals('~name', ns_join('~', '~name'))
153 self.assertEquals('/name', ns_join('/', '/name'))
154
155 # ns can be '~' or '/'
156 self.assertEquals('~name', ns_join('~', 'name'))
157 self.assertEquals('/name', ns_join('/', 'name'))
158
159 self.assertEquals('/ns/name', ns_join('/ns', 'name'))
160 self.assertEquals('/ns/name', ns_join('/ns/', 'name'))
161 self.assertEquals('/ns/ns2/name', ns_join('/ns', 'ns2/name'))
162 self.assertEquals('/ns/ns2/name', ns_join('/ns/', 'ns2/name'))
163
164 # allow ns to be empty
165 self.assertEquals('name', ns_join('', 'name'))
166
167
168 def test_load_mappings(self):
169 from roslib.names import load_mappings
170 self.assertEquals({}, load_mappings([]))
171 self.assertEquals({}, load_mappings(['foo']))
172 self.assertEquals({}, load_mappings([':=']))
173 self.assertEquals({}, load_mappings([':=:=']))
174 self.assertEquals({}, load_mappings(['f:=']))
175 self.assertEquals({}, load_mappings([':=b']))
176 self.assertEquals({}, load_mappings(['foo:=bar:=baz']))
177 # should ignore node param assignments
178 self.assertEquals({}, load_mappings(['_foo:=bar']))
179
180 self.assertEquals({'foo': 'bar'}, load_mappings(['foo:=bar']))
181 # should allow double-underscore names
182 self.assertEquals({'__foo': 'bar'}, load_mappings(['__foo:=bar']))
183 self.assertEquals({'foo': 'bar'}, load_mappings(['./f', '-x', '--blah', 'foo:=bar']))
184 self.assertEquals({'a': '1', 'b': '2', 'c': '3'}, load_mappings(['c:=3', 'c:=', ':=3', 'a:=1', 'b:=2']))
185
186 def test_resource_name(self):
187 from roslib.names import resource_name
188 self.assertEquals('foo/bar', resource_name('foo', 'bar'))
189 self.assertEquals('bar', resource_name('foo', 'bar', my_pkg='foo'))
190 self.assertEquals('foo/bar', resource_name('foo', 'bar', my_pkg='bar'))
191 self.assertEquals('foo/bar', resource_name('foo', 'bar', my_pkg=''))
192 self.assertEquals('foo/bar', resource_name('foo', 'bar', my_pkg=None))
193
194 def test_resource_name_base(self):
195 from roslib.names import resource_name_base
196 self.assertEquals('', resource_name_base(''))
197 self.assertEquals('bar', resource_name_base('bar'))
198 self.assertEquals('bar', resource_name_base('foo/bar'))
199 self.assertEquals('bar', resource_name_base('/bar'))
200 self.assertEquals('', resource_name_base('foo/'))
201
202 def test_resource_name_package(self):
203 from roslib.names import resource_name_package
204 self.assertEquals(None, resource_name_package(''))
205 self.assertEquals(None, resource_name_package('foo'))
206 self.assertEquals('foo', resource_name_package('foo/'))
207 self.assertEquals('foo', resource_name_package('foo/bar'))
208
209 def test_package_resource_name(self):
210 from roslib.names import package_resource_name
211 self.assertEquals(('', ''), package_resource_name(''))
212 self.assertEquals(('', 'foo'), package_resource_name('foo'))
213 self.assertEquals(('foo', 'bar'), package_resource_name('foo/bar'))
214 self.assertEquals(('foo', ''), package_resource_name('foo/'))
215 try:
216 # only allowed single separator
217 package_resource_name("foo/bar/baz")
218 self.fail("should have raised ValueError")
219 except ValueError:
220 pass
221
222
223 def test_is_legal_resource_name(self):
224 from roslib.names import is_legal_resource_name
225 failures = [None, '', 'hello\n', '\t', 'foo++', 'foo-bar', '#foo',
226 ' name', 'name ',
227 '~name', '/name',
228 '1name', 'foo\\']
229 for f in failures:
230 self.failIf(is_legal_resource_name(f), f)
231 tests = ['f', 'f1', 'f_', 'foo', 'foo_bar', 'foo/bar', 'roslib/Log']
232 for t in tests:
233 self.assert_(is_legal_resource_name(t), t)
234
235 def test_is_legal_name(self):
236 from roslib.names import is_legal_name
237 failures = [None,
238 'foo++', 'foo-bar', '#foo',
239 'hello\n', '\t', ' name', 'name ',
240 'f//b',
241 '1name', 'foo\\']
242 for f in failures:
243 self.failIf(is_legal_name(f), f)
244 tests = ['',
245 'f', 'f1', 'f_', 'f/', 'foo', 'foo_bar', 'foo/bar', 'foo/bar/baz',
246 '~f', '~a/b/c',
247 '~/f',
248 '/a/b/c/d', '/']
249 for t in tests:
250 self.assert_(is_legal_name(t), "[%s]"%t)
251
252 def test_is_legal_base_name(self):
253 from roslib.names import is_legal_base_name
254 failures = [None, '', 'hello\n', '\t', 'foo++', 'foo-bar', '#foo',
255 'f/', 'foo/bar', '/', '/a',
256 'f//b',
257 '~f', '~a/b/c',
258 ' name', 'name ',
259 '1name', 'foo\\']
260 for f in failures:
261 self.failIf(is_legal_base_name(f), f)
262 tests = ['f', 'f1', 'f_', 'foo', 'foo_bar']
263 for t in tests:
264 self.assert_(is_legal_base_name(t), "[%s]"%t)
265
266 def test_is_legal_resource_base_name(self):
267 from roslib.names import is_legal_resource_base_name
268 failures = [None, '', 'hello\n', '\t', 'foo++', 'foo-bar', '#foo',
269 'f/', 'foo/bar', '/', '/a',
270 'f//b',
271 '~f', '~a/b/c',
272 '~/f',
273 ' name', 'name ',
274 '1name', 'foo\\']
275 for f in failures:
276 self.failIf(is_legal_resource_base_name(f), f)
277 tests = ['f', 'f1', 'f_', 'foo', 'foo_bar']
278 for t in tests:
279 self.assert_(is_legal_resource_base_name(t), "[%s]"%t)
280
281 def test_resolve_name(self):
282 from roslib.names import resolve_name
283 # TODO: test with remappings
284 tests = [
285 ('', '/', '/'),
286 ('', '/node', '/'),
287 ('', '/ns1/node', '/ns1/'),
288
289 ('foo', '', '/foo'),
290 ('foo/', '', '/foo'),
291 ('/foo', '', '/foo'),
292 ('/foo/', '', '/foo'),
293 ('/foo', '/', '/foo'),
294 ('/foo/', '/', '/foo'),
295 ('/foo', '/bar', '/foo'),
296 ('/foo/', '/bar', '/foo'),
297
298 ('foo', '/ns1/ns2', '/ns1/foo'),
299 ('foo', '/ns1/ns2/', '/ns1/foo'),
300 ('foo', '/ns1/ns2/ns3/', '/ns1/ns2/foo'),
301 ('foo/', '/ns1/ns2', '/ns1/foo'),
302 ('/foo', '/ns1/ns2', '/foo'),
303 ('foo/bar', '/ns1/ns2', '/ns1/foo/bar'),
304 ('foo//bar', '/ns1/ns2', '/ns1/foo/bar'),
305 ('foo/bar', '/ns1/ns2/ns3', '/ns1/ns2/foo/bar'),
306 ('foo//bar//', '/ns1/ns2/ns3', '/ns1/ns2/foo/bar'),
307
308 ('~foo', '/', '/foo'),
309 ('~foo', '/node', '/node/foo'),
310 ('~foo', '/ns1/ns2', '/ns1/ns2/foo'),
311 ('~foo/', '/ns1/ns2', '/ns1/ns2/foo'),
312 ('~foo/bar', '/ns1/ns2', '/ns1/ns2/foo/bar'),
313
314 # #3044
315 ('~/foo', '/', '/foo'),
316 ('~/foo', '/node', '/node/foo'),
317 ('~/foo', '/ns1/ns2', '/ns1/ns2/foo'),
318 ('~/foo/', '/ns1/ns2', '/ns1/ns2/foo'),
319 ('~/foo/bar', '/ns1/ns2', '/ns1/ns2/foo/bar'),
320
321 ]
322 for name, node_name, v in tests:
323 self.assertEquals(v, resolve_name(name, node_name))
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
32 import os
33 import struct
34 import sys
35 import unittest
36
37 import roslib.packages
38
39 class RoslibPackagesTest(unittest.TestCase):
40
41 def test_find_node(self):
42 import roslib.packages
43 d = roslib.packages.get_pkg_dir('roslib')
44 p = os.path.join(d, 'test', 'fake_node.py')
45 self.assertEquals([p], roslib.packages.find_node('roslib', 'fake_node.py'))
46
47 self.assertEquals([], roslib.packages.find_node('roslib', 'not_a_node'))
48
49 def test_get_pkg_dir(self):
50 import roslib.packages
51 import roslib.rospack
52 path = roslib.rospack.rospackexec(['find', 'roslib'])
53 self.assertEquals(path, roslib.packages.get_pkg_dir('roslib'))
54 try:
55 self.assertEquals(path, roslib.packages.get_pkg_dir('fake_roslib'))
56 self.fail("should have raised")
57 except roslib.packages.InvalidROSPkgException: pass
58
59 def test_get_dir_pkg(self):
60 import roslib.packages
61 path = get_roslib_path()
62
63 res = roslib.packages.get_dir_pkg(path)
64 res = (os.path.realpath(res[0]), res[1])
65 self.assertEquals((path, 'roslib'), res)
66 res = roslib.packages.get_dir_pkg(os.path.join(path, 'test'))
67 res = (os.path.realpath(res[0]), res[1])
68 self.assertEquals((path, 'roslib'), res)
69
70 # must fail on parent of roslib
71 self.assertEquals((None, None), roslib.packages.get_dir_pkg(os.path.dirname(path)))
72
73 def get_roslib_path():
74 return os.path.realpath(os.path.abspath(os.path.join(get_test_path(), '..')))
75
76 def get_test_path():
77 return os.path.abspath(os.path.dirname(__file__))
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
32 import os
33 import sys
34 import unittest
35
36 import roslib.rosenv
37
38 class EnvTest(unittest.TestCase):
39
40 def test_get_ros_root(self):
41 from roslib.rosenv import get_ros_root
42 self.assertEquals(None, get_ros_root(required=False, env={}))
43 self.assertEquals(None, get_ros_root(False, {}))
44 try:
45 get_ros_root(required=True, env={})
46 self.fail("get_ros_root should have failed")
47 except: pass
48
49 env = {'ROS_ROOT': '/fake/path'}
50 self.assertEquals('/fake/path', get_ros_root(required=False, env=env))
51 try:
52 get_ros_root(required=True, env=env)
53 self.fail("get_ros_root should have failed")
54 except: pass
55
56 def test_get_ros_package_path(self):
57 from roslib.rosenv import get_ros_package_path
58 self.assertEquals(None, get_ros_package_path(required=False, env={}))
59 self.assertEquals(None, get_ros_package_path(False, {}))
60 try:
61 get_ros_package_path(required=True, env={})
62 self.fail("get_ros_package_path should have raised")
63 except: pass
64 env = {'ROS_PACKAGE_PATH': ':'}
65 self.assertEquals(':', get_ros_package_path(True, env=env))
66 self.assertEquals(':', get_ros_package_path(False, env=env))
67
68 # trip-wire tests. Cannot guarantee that ROS_PACKAGE_PATH is set
69 # to valid value on test machine, just make sure logic doesn't crash
70 self.assertEquals(os.environ.get('ROS_PACKAGE_PATH', None), get_ros_package_path(required=False))
71
72 def test_get_ros_master_uri(self):
73 from roslib.rosenv import get_master_uri
74 self.assertEquals(None, get_master_uri(required=False, env={}))
75 self.assertEquals(None, get_master_uri(False, {}))
76 try:
77 get_master_uri(required=True, env={})
78 self.fail("get_ros_package_path should have raised")
79 except: pass
80 env = {'ROS_MASTER_URI': 'http://localhost:1234'}
81 self.assertEquals('http://localhost:1234', get_master_uri(True, env=env))
82 self.assertEquals('http://localhost:1234', get_master_uri(False, env=env))
83
84 argv = ['__master:=http://localhost:5678']
85 self.assertEquals('http://localhost:5678', get_master_uri(False, env=env, argv=argv))
86
87 try:
88 argv = ['__master:=http://localhost:5678:=http://localhost:1234']
89 get_master_uri(required=False, env=env, argv=argv)
90 self.fail("should have thrown")
91 except roslib.rosenv.ROSEnvException: pass
92
93 try:
94 argv = ['__master:=']
95 get_master_uri(False, env=env, argv=argv)
96 self.fail("should have thrown")
97 except roslib.rosenv.ROSEnvException: pass
98
99 # make sure test works with os.environ
100 self.assertEquals(os.environ.get('ROS_MASTER_URI', None), get_master_uri(required=False))
101
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
32 import os
33 import sys
34 import unittest
35
36 import roslib
37
38 def get_test_path():
39 return os.path.abspath(os.path.dirname(__file__))
40
41 class RoslibStackManifestTest(unittest.TestCase):
42
43 def _subtest_parse_stack_example1(self, m):
44 from roslib.manifestlib import _Manifest
45 self.assert_(isinstance(m, _Manifest))
46 self.assertEquals('stack', m._type)
47 self.assertEquals("a brief description", m.brief)
48 self.assertEquals("Line 1\nLine 2", m.description.strip())
49 self.assertEquals("The authors\ngo here", m.author.strip())
50 self.assertEquals("Public Domain\nwith other stuff", m.license.strip())
51 self.assertEquals("http://ros.org/stack/", m.url)
52 self.assertEquals("http://www.willowgarage.com/files/willowgarage/robot10.jpg", m.logo)
53 dpkgs = [d.stack for d in m.depends]
54 self.assertEquals(set(['stackname', 'common']), set(dpkgs))
55 self.assertEquals([], m.rosdeps)
56 self.assertEquals([], m.exports)
57
58 def _subtest_parse_stack_version(self, m):
59 self.assertEquals("1.2.3", m.version)
60
61 def test_parse_example1_file(self):
62 from roslib.stack_manifest import parse_file, StackManifest
63
64 p = os.path.join(get_test_path(), 'manifest_tests', 'stack_example1.xml')
65 self._subtest_parse_stack_example1(parse_file(p))
66
67 p = os.path.join(get_test_path(), 'manifest_tests', 'stack_version.xml')
68 self._subtest_parse_stack_version(parse_file(p))
69
70 def test_parse_example1_string(self):
71 from roslib.manifestlib import parse, _Manifest
72 self._subtest_parse_stack_example1(parse(_Manifest('stack'), STACK_EXAMPLE1))
73
74 def test_StackManifest(self):
75 from roslib.stack_manifest import StackManifest
76 m = StackManifest()
77 self.assertEquals('stack', m._type)
78
79 def test_StackManifest_str(self):
80 # just make sure it doesn't crash
81 from roslib.stack_manifest import parse
82 str(parse(STACK_EXAMPLE1))
83
84 def test_StackManifest_xml(self):
85 from roslib.stack_manifest import parse, StackManifest
86 m = parse(STACK_EXAMPLE1)
87 self._subtest_parse_stack_example1(m)
88 # verify roundtrip
89 m2 = parse(m.xml())
90 self._subtest_parse_stack_example1(m2)
91
92 # bad file examples should be more like the roslaunch tests where there is just 1 thing wrong
93 STACK_EXAMPLE1 = """<stack>
94 <description brief="a brief description">Line 1
95 Line 2
96 </description>
97 <author>The authors
98 go here</author>
99 <license>Public Domain
100 with other stuff</license>
101 <url>http://ros.org/stack/</url>
102 <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
103 <depend stack="stackname" />
104 <depend stack="common"/>
105 </stack>"""
106
107 STACK_INVALID1 = """<stack>
108 <description brief="a brief description">Line 1</description>
109 <author>The authors</author>
110 <license>Public Domain</license>
111 <rosdep name="python" />
112 </stack>"""
113
114 STACK_INVALID2 = """<stack>
115 <description brief="a brief description">Line 1</description>
116 <author>The authors</author>
117 <license>Public Domain</license>
118 <export>
119 <cpp cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lros"/>
120 <cpp os="osx" cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lrosthread -framework CoreServices"/>
121 </export>
122 </stack>"""
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
32 import os
33 import sys
34 import unittest
35
36 import roslib
37 import rospkg
38
39 class RoslibStacksTest(unittest.TestCase):
40
41 def test_list_stacks(self):
42 from roslib.stacks import list_stacks
43 l = list_stacks()
44 self.assert_('ros' in l)
45
46 # test with env
47 test_dir = os.path.join(roslib.packages.get_pkg_dir('roslib'), 'test', 'stack_tests', 's1')
48 env = os.environ.copy()
49 env['ROS_PACKAGE_PATH'] = test_dir
50 val = set(list_stacks(env=env))
51 # ros stack not guaranteed to list anymore as ROS_ROOT may not be set
52 if 'ros' in val:
53 val.remove('ros')
54 self.assertEquals(set(['foo', 'bar']), val)
55
56
57 def test_list_stacks_by_path(self):
58 from roslib.stacks import list_stacks_by_path
59
60 # test with synthetic stacks
61 test_dir = os.path.join(roslib.packages.get_pkg_dir('roslib'), 'test', 'stack_tests')
62 self.assertEquals(set(['bar', 'foo']), set(list_stacks_by_path(test_dir)))
63
64 test_dir = os.path.join(roslib.packages.get_pkg_dir('roslib'), 'test', 'stack_tests', 's1')
65 self.assertEquals(set(['bar', 'foo']), set(list_stacks_by_path(test_dir)))
66
67 test_dir = os.path.join(roslib.packages.get_pkg_dir('roslib'), 'test', 'stack_tests', 's1', 'bar')
68 self.assertEquals(['bar'], list_stacks_by_path(test_dir))
69
70 # test symlink following
71
72 test_dir = os.path.join(roslib.packages.get_pkg_dir('roslib'), 'test', 'stack_tests2')
73 self.assertEquals(set(['foo', 'bar']), set(list_stacks_by_path(test_dir)))
74
75 def test_list_stacks_by_path_unary(self):
76 from roslib.stacks import list_stacks_by_path
77 # test with synthetic stacks
78 test_dir = os.path.join(roslib.packages.get_pkg_dir('roslib'), 'test', 'stack_tests_unary')
79 self.assertEquals(set(['bar', 'foo', 'baz']), set(list_stacks_by_path(test_dir)))
80
81 def test_get_stack_dir_unary(self):
82 # now manipulate the environment to test precedence
83 # - save original RPP as we popen rosstack in other tests
84 d = roslib.packages.get_pkg_dir('roslib')
85 d = os.path.join(d, 'test', 'stack_tests_unary')
86 s1_d = os.path.join(d, 's1')
87 rpp = rospkg.get_ros_package_path()
88 try:
89 paths = [d]
90 os.environ[rospkg.environment.ROS_PACKAGE_PATH] = os.pathsep.join(paths)
91 self.assertEquals(os.path.join(s1_d, 'foo'), roslib.stacks.get_stack_dir('foo'))
92 self.assertEquals(os.path.join(s1_d, 'bar'), roslib.stacks.get_stack_dir('bar'))
93 self.assertEquals(os.path.join(s1_d, 'baz'), roslib.stacks.get_stack_dir('baz'))
94 finally:
95 #restore rpp
96 if rpp is not None:
97 os.environ[rospkg.environment.ROS_PACKAGE_PATH] = rpp
98 else:
99 del os.environ[rospkg.environment.ROS_PACKAGE_PATH]
100
101 def test_get_stack_dir(self):
102 import roslib.packages
103 from roslib.stacks import get_stack_dir, InvalidROSStackException, list_stacks
104 try:
105 get_stack_dir('non_existent')
106 self.fail("should have raised")
107 except roslib.stacks.InvalidROSStackException:
108 pass
109
110 # now manipulate the environment to test precedence
111 # - save original RPP as we popen rosstack in other tests
112 rpp = os.environ.get(rospkg.environment.ROS_PACKAGE_PATH, None)
113 try:
114 d = roslib.packages.get_pkg_dir('roslib')
115 d = os.path.join(d, 'test', 'stack_tests')
116
117 # - s1/s2/s3
118 print "s1/s2/s3"
119 paths = [os.path.join(d, p) for p in ['s1', 's2', 's3']]
120 os.environ[rospkg.environment.ROS_PACKAGE_PATH] = os.pathsep.join(paths)
121 # - run multiple times to test caching
122 for i in xrange(2):
123 stacks = roslib.stacks.list_stacks()
124 self.assert_('foo' in stacks)
125 self.assert_('bar' in stacks)
126
127 foo_p = os.path.join(d, 's1', 'foo')
128 bar_p = os.path.join(d, 's1', 'bar')
129 self.assertEquals(foo_p, roslib.stacks.get_stack_dir('foo'))
130 self.assertEquals(bar_p, roslib.stacks.get_stack_dir('bar'))
131
132 # - s2/s3/s1
133 print "s2/s3/s1"
134
135 paths = [os.path.join(d, p) for p in ['s2', 's3', 's1']]
136 os.environ[rospkg.environment.ROS_PACKAGE_PATH] = os.pathsep.join(paths)
137 stacks = roslib.stacks.list_stacks()
138 self.assert_('foo' in stacks)
139 self.assert_('bar' in stacks)
140
141 foo_p = os.path.join(d, 's2', 'foo')
142 bar_p = os.path.join(d, 's1', 'bar')
143 self.assertEquals(foo_p, roslib.stacks.get_stack_dir('foo'))
144 self.assertEquals(bar_p, roslib.stacks.get_stack_dir('bar'))
145 finally:
146 #restore rpp
147 if rpp is not None:
148 os.environ[rospkg.environment.ROS_PACKAGE_PATH] = rpp
149 else:
150 del os.environ[rospkg.environment.ROS_PACKAGE_PATH]
151
152 def test_expand_to_packages_unary(self):
153 # test unary
154 test_dir = os.path.join(roslib.packages.get_pkg_dir('roslib'), 'test', 'stack_tests_unary')
155
156 env = os.environ.copy()
157 env[rospkg.environment.ROS_PACKAGE_PATH] = test_dir
158
159 from roslib.stacks import expand_to_packages
160 self.assertEquals((['foo'], []), expand_to_packages(['foo'], env=env))
161 self.assertEquals((['foo', 'bar'], []), expand_to_packages(['foo', 'bar'], env=env))
162
163 def test_expand_to_packages(self):
164 from roslib.stacks import expand_to_packages
165 try:
166 # it's possible to accidentally pass in a sequence type
167 # like a string and get weird results, so check that we
168 # don't
169 self.assertEquals(([], []), expand_to_packages('ros'))
170 self.fail("expand_to_packages should only take in a list of strings")
171 except ValueError: pass
172
173 self.assertEquals(([], []), expand_to_packages([]))
174 self.assertEquals((['rosmake', 'roslib', 'roslib'], []), expand_to_packages(['rosmake', 'roslib', 'roslib']))
175 self.assertEquals(([], ['bogus_one', 'bogus_two']), expand_to_packages(['bogus_one', 'bogus_two']))
176
177 self.assertEquals(([], ['bogus_one', 'bogus_two']), expand_to_packages(['bogus_one', 'bogus_two']))
178
179 # TODO: setup directory tree so that this can be more precisely calculated
180 valid, invalid = expand_to_packages(['ros', 'bogus_one'])
181 self.assertEquals(['bogus_one'], invalid)
182 check = ['rosbuild', 'rosunit', 'roslib']
183 print valid
184 for c in check:
185 self.assert_(c in valid, "expected [%s] to be in ros expansion"%c)
186
187 def test_get_stack_version(self):
188 from roslib.stacks import get_stack_version
189
190 test_dir = os.path.join(get_test_path(), 'stack_tests', 's1')
191 env = os.environ.copy()
192 env[rospkg.environment.ROS_PACKAGE_PATH] = test_dir
193
194 # REP 109: stack.xml has precedence over CMakeLists.txt, version is whitespace stripped
195 self.assertEquals('1.6.0-manifest', roslib.stacks.get_stack_version('foo', env=env))
196 # REP 109: test fallback to CMakeLists.txt version
197 self.assertEquals('1.5.0-cmake', roslib.stacks.get_stack_version('bar', env=env))
198
199 if 0:
200 test_dir = os.path.join(roslib.packages.get_pkg_dir('roslib'), 'test', 'stack_tests_unary')
201 env = os.environ.copy()
202 env[rospkg.environment.ROS_PACKAGE_PATH] = test_dir
203
204 def get_test_path():
205 return os.path.abspath(os.path.dirname(__file__))
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 <string>
32 #include <vector>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <boost/thread.hpp>
36 #include <gtest/gtest.h>
37 #include "ros/package.h"
38
39 void string_split(const std::string &s, std::vector<std::string> &t, const std::string &d)
40 { t.clear();
41 size_t start = 0, end;
42 while ((end = s.find_first_of(d, start)) != std::string::npos)
43 {
44 if((end-start) > 0)
45 t.push_back(s.substr(start, end-start));
46 start = end + 1;
47 }
48 if(start < s.size())
49 t.push_back(s.substr(start));
50 }
51
52 char g_rr_buf[1024];
53 void set_env_vars(void)
54 {
55 // Point ROS_PACKAGE_PATH at the roslib directory, and point
56 // ROS_ROOT into an empty directory.
57 getcwd(g_rr_buf, sizeof(g_rr_buf));
58 setenv("ROS_PACKAGE_PATH", g_rr_buf, 1);
59 strncpy(g_rr_buf+strlen(g_rr_buf), "/tmp.XXXXXX", sizeof(g_rr_buf)-strlen(g_rr_buf)-1);
60 g_rr_buf[sizeof(g_rr_buf)-1] = '\0';
61 mkdtemp(g_rr_buf);
62 setenv("ROS_ROOT", g_rr_buf, 1);
63 }
64 void cleanup_env_vars(void)
65 {
66 // Remove the empty directory that we created in set_env_vars().
67 rmdir(g_rr_buf);
68 memset(g_rr_buf, 0, sizeof(g_rr_buf));
69 }
70
71 TEST(roslib, commandListNames)
72 {
73 set_env_vars();
74
75 std::string output = ros::package::command("list-names");
76 std::vector<std::string> output_list;
77 string_split(output, output_list, "\n");
78 ASSERT_EQ((int)output_list.size(), 1);
79 ASSERT_STREQ(output_list[0].c_str(), "roslib");
80
81 cleanup_env_vars();
82 }
83
84 TEST(roslib, commandList)
85 {
86 set_env_vars();
87
88 std::string output = ros::package::command("list");
89 std::vector<std::string> output_list;
90 std::vector<std::string> name_path;
91 string_split(output, output_list, "\n");
92 ASSERT_EQ((int)output_list.size(), 1);
93 string_split(output_list[0], name_path, " ");
94 ASSERT_EQ((int)name_path.size(), 2);
95 ASSERT_STREQ(name_path[0].c_str(), "roslib");
96
97 cleanup_env_vars();
98 }
99
100 TEST(roslib, getAll)
101 {
102 set_env_vars();
103
104 std::vector<std::string> output_list;
105 ASSERT_TRUE(ros::package::getAll(output_list));
106 ASSERT_EQ((int)output_list.size(), 1);
107 ASSERT_STREQ(output_list[0].c_str(), "roslib");
108
109 cleanup_env_vars();
110 }
111
112 void
113 roslib_caller()
114 {
115 struct timespec ts = {0, 100000};
116 std::string output;
117 for(int i=0;i<100;i++)
118 {
119 output = ros::package::command("plugins --attrib=foo roslib");
120 nanosleep(&ts, NULL);
121 }
122 }
123
124 TEST(roslib, concurrent_access)
125 {
126 set_env_vars();
127 const int size = 10;
128 boost::thread t[size];
129 for(int i=0;i<size; i++)
130 t[i] = boost::thread(boost::bind(roslib_caller));
131 for(int i=0;i<size; i++)
132 t[i].join();
133 cleanup_env_vars();
134 }
135
136 int main(int argc, char **argv)
137 {
138 testing::InitGoogleTest(&argc, argv);
139 return RUN_ALL_TESTS();
140 }
141
0 REM generated from ros/env-hooks/10.ros.bat.in
1
2 REM scrub old ROS bin dirs, to avoid accidentally finding the wrong executables
3 set PATH=`python -c "import os; print(os.pathsep.join([x for x in \"$PATH\".split(os.pathsep) if not any([d for d in ['cturtle', 'diamondback', 'electric', 'fuerte'] if d in x])]))"`
4
5 set ROS_DISTRO=groovy
6 if [%ROS_MASTER_URI]==[] (
7 set ROS_MASTER_URI=http://localhost:11311
8 )
9
10 REM python function to generate ROS package path based on all parent workspaces (prepends the separator if necessary)
11 REM do not use EnableDelayedExpansion here, it messes with the != symbols
12 echo from __future__ import print_function > _parent_package_path.py
13 echo import os >> _parent_package_path.py
14 echo env_name = 'CATKIN_WORKSPACES' >> _parent_package_path.py
15 echo items = os.environ[env_name].split(';') if env_name in os.environ and os.environ[env_name] != '' else [] >> _parent_package_path.py
16 echo path = '' >> _parent_package_path.py
17 echo for item in items: >> _parent_package_path.py
18 echo path += ':' + (os.path.join(item, 'share') if item.find(':') == -1 else item.split(':')[1]) >> _parent_package_path.py
19 echo print(path) >> _parent_package_path.py
20
21 setlocal EnableDelayedExpansion
22
23 set ROS_PACKAGE_PATH_PARENTS=
24 for /f %%a in ('python _parent_package_path.py') do set ROS_PACKAGE_PATH_PARENTS=!ROS_PACKAGE_PATH_PARENTS!%%a
25
26 if [@BUILDSPACE@]==[true] (
27 set ROS_PACKAGE_PATH=@CMAKE_SOURCE_DIR@$ROS_PACKAGE_PATH_PARENTS
28 set ROS_ROOT=@CMAKE_CURRENT_SOURCE_DIR@
29 set ROS_ETC_DIR=@CATKIN_BUILD_PREFIX@/@CATKIN_PACKAGE_ETC_DESTINATION@
30 )
31 if [@INSTALLSPACE@]==[true] (
32 set ROS_PACKAGE_PATH=@CMAKE_INSTALL_PREFIX@/share:@CMAKE_INSTALL_PREFIX@/stacks$ROS_PACKAGE_PATH_PARENTS
33 set ROS_ROOT=@CMAKE_INSTALL_PREFIX@/@CATKIN_PACKAGE_SHARE_DESTINATION@
34 set ROS_ETC_DIR=@CMAKE_INSTALL_PREFIX@/@CATKIN_PACKAGE_ETC_DESTINATION@
35 )
36
37 del _parent_package_path.py
38
39 endlocal
0 # generated from ros/env-hooks/10.ros.sh.in
1
2 # scrub old ROS bin dirs, to avoid accidentally finding the wrong executables
3 export PATH=`python -c "import os; print(os.pathsep.join([x for x in \"$PATH\".split(os.pathsep) if not any([d for d in ['cturtle', 'diamondback', 'electric', 'fuerte'] if d in x])]))"`
4
5 export ROS_DISTRO=groovy
6 if [ ! "$ROS_MASTER_URI" ] ; then
7 export ROS_MASTER_URI=http://localhost:11311
8 fi
9
10 # python function to generate ROS package path based on all workspaces
11 PYTHON_CODE_BUILD_ROS_PACKAGE_PATH=$(cat <<EOF
12 from __future__ import print_function
13 import os
14 env_name = 'CMAKE_PREFIX_PATH'
15 paths = [path for path in os.environ[env_name].split(os.pathsep)] if env_name in os.environ and os.environ[env_name] != '' else []
16 workspaces = [path for path in paths if os.path.exists(os.path.join(path, '.CATKIN_WORKSPACE'))]
17 paths = []
18 for workspace in workspaces:
19 filename = os.path.join(workspace, '.CATKIN_WORKSPACE')
20 data = ''
21 with open(filename) as f:
22 data = f.read()
23 if data == '':
24 paths.append(os.path.join(workspace, 'share'))
25 paths.append(os.path.join(workspace, 'stacks'))
26 else:
27 for source_path in data.split(';'):
28 paths.append(source_path)
29 print(os.pathsep.join(paths))
30 EOF
31 )
32 export ROS_PACKAGE_PATH=`python -c "$PYTHON_CODE_BUILD_ROS_PACKAGE_PATH"`
33
34 if @BUILDSPACE@; then
35 export ROS_ROOT=@CMAKE_CURRENT_SOURCE_DIR@
36 export ROS_ETC_DIR=@CATKIN_BUILD_PREFIX@/@CATKIN_PACKAGE_ETC_DESTINATION@
37 fi
38 if @INSTALLSPACE@; then
39 export ROS_ROOT=@CMAKE_INSTALL_PREFIX@/@CATKIN_PACKAGE_SHARE_DESTINATION@
40 export ROS_ETC_DIR=@CMAKE_INSTALL_PREFIX@/@CATKIN_PACKAGE_ETC_DESTINATION@
41 fi
0 cmake_minimum_required(VERSION 2.8)
1 project(ros)
2 find_package(catkin REQUIRED)
3 catkin_package()
0 <package>
1 <name>ros</name>
2 <version>1.9.11</version>
3 <description>ROS packaging system</description>
4 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
5 <license>BSD</license>
6
7 <url type="website">http://www.ros.org/wiki/ROS</url>
8 <url type="bugtracker">https://code.ros.org/trac/ros/query?status=assigned&amp;status=new&amp;status=reopened&amp;component=ros&amp;order=priority</url>
9 <url type="repository">https://code.ros.org/svn/ros/stacks/ros/trunk/</url>
10
11 <author>Eric Berger</author>
12 <author>Ken Conley</author>
13 <author>Josh Faust</author>
14 <author>Tully Foote</author>
15 <author>Brian Gerkey</author>
16 <author>Jeremy Leibs</author>
17 <author>Morgan Quigley</author>
18 <author>Rob Wheeler</author>
19
20 <!-- This is a metapackage, so it just run-depends on all its child
21 packages. -->
22 <run_depend>mk</run_depend>
23 <run_depend>rosbuild</run_depend>
24 <run_depend>roslang</run_depend>
25 <run_depend>roslib</run_depend>
26 <run_depend>rosbash</run_depend>
27 <run_depend>rosboost_cfg</run_depend>
28 <run_depend>rosclean</run_depend>
29 <run_depend>roscreate</run_depend>
30 <run_depend>rosmake</run_depend>
31 <run_depend>rosunit</run_depend>
32
33 <export>
34 <metapackage/>
35 </export>
36 </package>
0 #!/usr/bin/env python
1
2 from __future__ import print_function
3 from setuptools import setup
4 import sys
5 from xml.etree.ElementTree import ElementTree
6
7 try:
8 root = ElementTree(None, 'stack.xml')
9 version = root.findtext('version')
10 except Exception as e:
11 print('Could not extract version from your stack.xml:\n%s' % e, file=sys.stderr)
12 sys.exit(-1)
13
14 sys.path.insert(0, 'src')
15
16 setup(name = 'roslib',
17 version = version,
18 packages = ['ros', 'roslib', 'rosunit', 'rosmake', 'rosclean', 'roscreate', 'rosboost_cfg'],
19 package_dir = {'ros':'core/roslib/src/ros',
20 'roslib':'core/roslib/src/roslib',
21 'rosunit':'tools/rosunit/src/rosunit',
22 'rosmake':'tools/rosmake/src/rosmake',
23 'rosclean':'tools/rosclean/src/rosclean',
24 'roscreate':'tools/roscreate/src/roscreate',
25 'rosboost_cfg':'tools/rosboost_cfg/src/rosboost_cfg',
26 },
27 package_data = {
28 'roscreate': ['*.tmpl'],
29 },
30 install_requires=['rospkg'],
31 scripts = ['tools/rosclean/scripts/rosclean',
32 'tools/rosmake/scripts/rosmake',
33 'tools/rosunit/scripts/rosunit',
34 'tools/rosboost_cfg/scripts/rosboost-cfg',
35 'tools/roscreate/scripts/roscreate-pkg',
36 ],
37 author = "Maintained by Ken Conley",
38 author_email = "kwc@willowgarage.com",
39 url = "http://www.ros.org/wiki/roslib",
40 download_url = "http://pr.willowgarage.com/downloads/roslib/",
41 keywords = ["ROS"],
42 classifiers = [
43 "Programming Language :: Python",
44 "License :: OSI Approved :: BSD License" ],
45 description = "roslib",
46 long_description = """\
47 Internal Python libraries for low-level 'ros' stack. This also installs the rosmake and rosclean tools.
48 This does *not* include the ROS communication libraries.
49 """,
50 license = "BSD"
51 )
0 <stack>
1 <name>ros</name>
2 <version>1.9.9</version>
3 <description brief="ROS core">ROS packaging system</description>
4 <author>Eric Berger</author>
5 <author>Ken Conley</author>
6 <author>Josh Faust</author>
7 <author>Tully Foote</author>
8 <author>Brian Gerkey</author>
9 <author>Jeremy Leibs</author>
10 <author>Morgan Quigley</author>
11 <author>Rob Wheeler</author>
12 <maintainer email="tfoote@willowgarage.com">Tully Foote</maintainer>
13 <license>BSD</license>
14 <copyright>Willow Garage</copyright>
15 <url>http://www.ros.org/wiki/ROS</url>
16 <review status="doc reviewed" notes=""/>
17
18 <build_depends>boost</build_depends>
19 <build_depends>bzip2</build_depends>
20 <build_depends>catkin</build_depends>
21 <build_depends>gtest</build_depends>
22 <build_depends>pkg-config</build_depends>
23 <build_depends>python</build_depends>
24 <build_depends>python-nose</build_depends>
25 <build_depends>python-rospkg</build_depends>
26 <build_depends>rospack</build_depends>
27
28 <depends>boost</depends>
29 <depends>bzip2</depends>
30 <depends>catkin</depends>
31 <depends>gtest</depends>
32 <depends>python</depends>
33 <depends>python-nose</depends>
34 <depends>python-rospkg</depends>
35 <depends>rospack</depends>
36 </stack>
0 if @BUILDSPACE@; then
1 . @CMAKE_CURRENT_SOURCE_DIR@/rosbash
2 fi
3 if @INSTALLSPACE@; then
4 . @CMAKE_INSTALL_PREFIX@/share/rosbash/rosbash
5 fi
0 if @BUILDSPACE@; then
1 . @CMAKE_CURRENT_SOURCE_DIR@/rostcsh
2 fi
3 if @INSTALLSPACE@; then
4 . @CMAKE_INSTALL_PREFIX@/share/rosbash/rostcsh
5 fi
0 if @BUILDSPACE@; then
1 . @CMAKE_CURRENT_SOURCE_DIR@/roszsh
2 fi
3 if @INSTALLSPACE@; then
4 . @CMAKE_INSTALL_PREFIX@/share/rosbash/roszsh
5 fi
0 cmake_minimum_required(VERSION 2.8)
1 project(rosbash)
2 find_package(catkin)
3 catkin_package()
4 catkin_package_export()
5
6 install(FILES rosbash rostcsh roszsh
7 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
8 install(PROGRAMS scripts/rosrun
9 DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION})
10
11 message(STATUS " Making toplevel forward script for bash script rosrun")
12 set(BASH_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/rosrun)
13 configure_file(${catkin_EXTRAS_DIR}/templates/script.bash.in
14 ${CATKIN_BUILD_PREFIX}/bin/rosrun
15 @ONLY)
16
17 catkin_add_env_hooks(15.rosbash SHELLS bash tcsh zsh)
0 <package>
1 <description brief="ros bash tools">
2
3 Assorted shell commands for using ros with bash.
4
5 </description>
6 <author>Jeremy Leibs, Thibault Kruse</author>
7 <license>BSD</license>
8 <url>http://pr.willowgarage.com/wiki/rosbash</url>
9 <review status="doc reviewed"/>
10 <platform os="ubuntu" version="9.04"/>
11 <platform os="ubuntu" version="9.10"/>
12 <platform os="ubuntu" version="10.04"/>
13 <platform os="macports" version="macports"/>
14 </package>
0 <package>
1 <name>rosbash</name>
2 <version>1.9.11</version>
3 <description>
4
5 Assorted shell commands for using ros with bash.
6
7 </description>
8 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
9 <license>BSD</license>
10
11 <url>http://www.ros.org/wiki/rosbash</url>
12 <author>Jeremy Leibs</author>
13 <author>Thibault Kruse</author>
14
15 <build_depend>catkin</build_depend>
16
17 <export>
18 <platform os="ubuntu" version="9.04"/>
19 <platform os="ubuntu" version="9.10"/>
20 <platform os="ubuntu" version="10.04"/>
21 <platform os="macports" version="macports"/>
22 </export>
23 </package>
0 # rosbash - functions to support ROS users
1 # Useful things to know:
2 # 'local' variables get unset after function, all others stay forever
3 # COMPREPLY is the var used by bash complete builtin
4
5 function _rossed {
6 if [[ `uname` == Darwin || `uname` == FreeBSD ]]; then
7 sed -E "$@"
8 else
9 sed -r "$@"
10 fi
11 }
12
13 function _rosfind {
14 if [[ `uname` == Darwin || `uname` == FreeBSD ]]; then
15 # BSD find needs -E for extended regexp
16 find -E "$@"
17 else
18 find "$@"
19 fi
20 }
21
22 # _ros_location_find
23 # based on $ROS_LOCATIONS, which the user can set to any colon
24 # separated of key=folder pairs also resolves keys 'log' and
25 # 'test_results' to ROS defaults finally resolves to package, then
26 # stack echoes its result
27 function _ros_location_find {
28 local homedir ROS_LOCATION_KEYS_ARR ROS_LOCATIONS_ARR loc
29 homedir=`echo $HOME | sed -e "s/\//\t\//g" -e "s/\t/\\\\\/g"`
30 ROS_LOCATION_KEYS_ARR=(`echo $ROS_LOCATIONS | _rossed -e 's/([^:=]*)=([^:=]*)(:*[^=])*(:|$)/\1 /g'`)
31 ROS_LOCATIONS_ARR=(`echo $ROS_LOCATIONS | _rossed -e 's/([^:=]*)=([^:=]*)(:*[^=])*(:|$)/\2 /g' -e "s/~/${homedir}/g"`)
32
33 for (( i = 0 ; i < ${#ROS_LOCATION_KEYS_ARR[@]} ; i++ )); do
34 if [[ $1 == ${ROS_LOCATION_KEYS_ARR[$i]} ]]; then
35 echo ${ROS_LOCATIONS_ARR[i]}
36 return 0
37 fi
38 done
39
40 if [[ $1 == log ]]; then
41 echo `roslaunch-logs`
42 return 0
43 elif [[ $1 == test_results ]]; then
44 echo `rosrun rosunit test_results_dir.py`
45 return 0
46 fi
47
48 loc=`export ROS_CACHE_TIMEOUT=-1.0 && rospack find $1 2> /dev/null`
49 if [[ $? != 0 ]]; then
50 loc=`export ROS_CACHE_TIMEOUT=-1.0 && rosstack find $1 2> /dev/null`
51 if [[ $? != 0 ]]; then
52 return 1
53 fi
54 echo $loc
55 return 0
56 fi
57 echo $loc
58 return 0
59 }
60
61 function _ros_list_locations {
62 local ROS_LOCATION_KEYS packages stacks
63 ROS_LOCATION_KEYS=`echo $ROS_LOCATIONS | _rossed -e 's/([^:=]*)=([^:=]*)(:*[^=])*(:|$)/\1 /g'`
64 packages=`export ROS_CACHE_TIMEOUT=-1.0 && rospack list-names`
65 stacks=`export ROS_CACHE_TIMEOUT=-1.0 && rosstack list-names`
66 echo $packages $stacks log test_results $ROS_LOCATION_KEYS | tr ' ' '\n'
67 return 0
68 }
69
70 function _ros_package_find {
71 local loc
72 loc=`export ROS_CACHE_TIMEOUT=-1.0 && rospack find $1 2> /dev/null`
73 if [[ $? != 0 ]]; then
74 return 1
75 fi
76 echo $loc
77 return 0
78 }
79
80 function _ros_list_packages {
81 local packages
82 packages=`export ROS_CACHE_TIMEOUT=-1.0 && rospack list-names`
83 echo $packages | tr ' ' '\n'
84 return 0
85 }
86
87 function _ros_list_stacks {
88 local stacks
89 stacks=`export ROS_CACHE_TIMEOUT=-1.0 && rosstack list-names`
90 echo $stacks | tr ' ' '\n'
91 return 0
92 }
93
94 # takes as argument either just a package-path or just a pkgname
95 # returns 0 for no argument or if package (+ path) exist, 1 else
96 # on success with arguments returns [pkgname, abspath, relpath basename]
97 function _ros_decode_path {
98 local rosname rosdir reldir last
99 rosvals=()
100 if [[ -z $1 ]]; then
101 return 0
102 fi
103
104 if [[ $1 =~ .+/.* ]]; then
105 rosname=${1%%/*}
106 reldir=/${1#*/}
107 last=${reldir##*/}
108 reldir=${reldir%/*}/
109 else
110 rosname=$1
111 if [[ -z $2 || $2 != "forceeval" ]]; then
112 rosvals=(${rosname})
113 return 1
114 fi
115 fi
116
117 rosdir=`_ros_location_find $rosname`
118 if [[ $? != 0 ]]; then
119 rosvals=(${rosname})
120 return 1
121 fi
122
123 rosvals=(${rosname} ${rosdir} ${reldir} ${last})
124 }
125
126 function rospython {
127 local pkgname
128 if [[ $1 = "--help" ]]; then
129 echo -e "usage: rospython [package] \n\nRun python loading package manifest first."
130 return 0
131 fi
132 if [[ -z $1 ]]; then
133 if [[ -f ./manifest.xml ]]; then
134 pkgname=`basename \`pwd\``
135 python -i -c "import roslib; roslib.load_manifest('$pkgname')"
136 else
137 python
138 fi
139 else
140 python -i -c "import roslib; roslib.load_manifest('$1')"
141 fi
142 }
143
144 function roscd {
145 local rosvals
146 if [[ $1 = "--help" ]] | [[ $# -gt 1 ]]; then
147 echo -e "usage: roscd package\n\nJump to target package."
148 return 0
149 fi
150 if [ -z $1 ]; then
151 if [ -z $ROS_WORKSPACE ]; then
152 echo -e "No ROS_WORKSPACE set. Please set ROS_WORKSPACE to use roscd with no arguments."
153 return 1
154 fi
155 cd ${ROS_WORKSPACE}
156 return 0
157 fi
158
159 _ros_decode_path $1 forceeval
160 if [ $? != 0 ]; then
161 echo "roscd: No such package/stack '$1'"
162 return 1
163 elif [ -z $rosvals ]; then
164 if [ -z $ROS_WORKSPACE ]; then
165 echo -e "No ROS_WORKSPACE set. Please set ROS_WORKSPACE to use roscd with no arguments."
166 return 1
167 fi
168 cd ${ROS_WORKSPACE}
169 return 0
170 else
171 cd ${rosvals[1]}${rosvals[2]}${rosvals[3]}
172 return 0
173 fi
174 }
175
176 function _is_integer {
177 [ "$1" -eq "$1" ] > /dev/null 2>&1
178 return $?
179 }
180
181 function rosd {
182 if [[ $1 = "--help" ]]; then
183 echo -e "usage: rosd\n\nDisplays the list of currently remembered directories with indexes."
184 return 0
185 fi
186 let count=0;
187 for items in `dirs`;
188 do
189 echo $count $items;
190 let count=$((count+1));
191 done
192 }
193
194 function rospd {
195 if [[ $1 = "--help" ]]; then
196 echo -e "usage: rospd\n\nLike pushd, also accepts indexes from rosd."
197 return 0
198 fi
199 if _is_integer $1; then
200 pushd +$1 > /dev/null ;
201 else
202 local rosvals
203 _ros_decode_path $1 forceeval
204 pushd ${rosvals[1]}${rosvals[2]}${rosvals[3]} > /dev/null ;
205 fi
206 rosd
207 }
208
209 function rosls {
210 local rosvals
211 if [[ $1 = "--help" ]]; then
212 echo -e "usage: rosls [package]\n\nLists contents of a package directory."
213 return 0
214 fi
215 _ros_decode_path $1 forceeval
216 ls ${rosvals[1]}${rosvals[2]}${rosvals[3]} $2
217 }
218
219 # sets arg as return value
220 function _roscmd {
221 local pkgdir exepath opt catkin_project_libexec_dir opts
222 if [[ -n $CMAKE_PREFIX_PATH ]]; then
223 catkin_project_libexec_dir=`catkin_find --first-only --libexec $1 2> /dev/null`
224 fi
225 pkgdir=`_ros_package_find $1`
226 if [[ -z $catkin_project_libexec_dir && -z $pkgdir ]]; then
227 echo "Couldn't find package [$1]"
228 return 1
229 fi
230 exepath=(`find -L $catkin_project_libexec_dir $pkgdir -name $2 -type f ! -regex .*/[.].* ! -regex .*$pkgdir\/build\/.* | uniq`)
231 if [[ ${#exepath[@]} == 0 ]] ; then
232 echo "That file does not exist in that package."
233 return 1
234 elif [[ ${#exepath[@]} -gt 1 ]] ; then
235 echo "You have chosen a non-unique filename, please pick one of the following:"
236 select opt in ${exepath[@]}; do
237 echo $opt
238 break
239 done
240 else
241 opt=${exepath[0]}
242 fi
243 arg=${opt}
244 }
245
246 function rosed {
247 local arg
248 if [[ $1 = "--help" ]]; then
249 echo -e "usage: rossed [package] [file]\n\nEdit a file within a package."
250 return 0
251 fi
252 _roscmd ${1} ${2}
253 if [[ -n ${arg} ]]; then
254 if [[ -z $EDITOR ]]; then
255 vim ${arg}
256 else
257 $EDITOR ${arg}
258 fi
259 fi
260 }
261
262 function roscp {
263 local arg
264 if [[ $1 = "--help" ]] | [[ $# -ne 3 ]]; then
265 echo -e "usage: roscp package filename target\n\nCopy a file from a package to target location."
266 return 0
267 fi
268 _roscmd ${1} ${2}
269 cp ${arg} ${3}
270 }
271
272 function rosawesome {
273 alias megamaid=rosrecord
274 alias suck2blow=rosplay
275 alias botherder=roscore
276 }
277
278 function _roscomplete {
279 local arg opts
280 COMPREPLY=()
281 arg="${COMP_WORDS[COMP_CWORD]}"
282 opts="`_ros_list_packages` `_ros_list_stacks`"
283 IFS=$'\n'
284 COMPREPLY=($(compgen -W "${opts}" -- ${arg}))
285 unset IFS
286 }
287
288 function _roscomplete_rosmake {
289 local arg
290 COMPREPLY=()
291 arg="${COMP_WORDS[COMP_CWORD]}"
292 _roscomplete
293 if [[ ${arg} =~ \-\-.* ]]; then
294 COMPREPLY=(${COMPREPLY[@]} $(compgen -W "--test-only --all --mark-installed --unmark-installed --robust --build-everything --specified-only --buildtest --buildtest1 --output --pre-clean --bootstrap --disable-logging --target --pjobs= --threads --profile --skip-blacklist --status-rate" -- ${arg}))
295 fi
296 }
297
298 function _roscomplete_sub_dir {
299 local arg opts rosvals
300 COMPREPLY=()
301 arg="${COMP_WORDS[COMP_CWORD]}"
302 _ros_decode_path ${arg}
303 if [[ -z ${rosvals[2]} ]]; then
304 opts=`_ros_list_locations`
305 IFS=$'\n'
306 COMPREPLY=($(compgen -W "${opts}" -S / -- ${rosvals[0]}))
307 unset IFS
308 else
309 if [ -e ${rosvals[1]}${rosvals[2]} ]; then
310 opts=`find -L ${rosvals[1]}${rosvals[2]} -maxdepth 1 -mindepth 1 -type d ! -regex ".*/[.].*" -print0 | tr '\000' '\n' | sed -e "s/.*\/\(.*\)/\1\//g"`
311 else
312 opts=''
313 fi
314 IFS=$'\n'
315 COMPREPLY=($(compgen -P ${rosvals[0]}${rosvals[2]} -W "${opts}" -- ${rosvals[3]}))
316 unset IFS
317 fi
318 }
319
320 function _msg_opts {
321 local arg pkgs pkgname msgname searchmsg path
322
323 if [[ $1 =~ .+/.* ]]; then
324 pkgname=${1%%/*}
325 msgname=${1#*/}
326 searchmsg=1
327 else
328 pkgname=${1}
329 fi
330
331 if [[ -z ${searchmsg} ]]; then
332 pkgs=(`rospack list`)
333
334 for (( i = 0 ; i < ${#pkgs[@]} ; i=i+2 )); do
335 if [[ -d ${pkgs[i+1]}/msg ]]; then
336 echo ${pkgs[i]}/
337 fi
338 done
339 else
340 path=`rospack find ${pkgname}`
341 if [ -d ${path}/msg ]; then
342 echo `find -L ${path}/msg -maxdepth 1 -mindepth 1 -name *.msg ! -regex ".*/[.].*" -print0 | tr '\000' '\n' | sed -e "s/.*\/\(.*\)\.msg/${pkgname}\/\1/g"`
343 fi
344 fi
345 }
346
347 function _srv_opts {
348 local arg pkgs pkgname srvname searchsrv path count opts
349
350 if [[ $1 =~ .+/.* ]]; then
351 pkgname=${1%%/*}
352 srvname=${1#*/}
353 searchsrv=1
354 else
355 pkgname=${1}
356 fi
357
358 if [[ -z ${searchsrv} ]]; then
359 pkgs=(`rospack list | grep "^${pkgname}"`)
360 count=0
361
362 opts=""
363
364 for (( i = 0 ; i < ${#pkgs[@]} ; i=i+2 )); do
365 if [[ -d ${pkgs[i+1]}/srv ]]; then
366 opts="$opts ${pkgs[i]}/"
367 pkgname=${pkgs[i]}
368 count=$((count+1))
369 fi
370 done
371
372 if [[ $count -gt 1 ]]; then
373 echo $opts
374 return 0
375 fi
376 fi
377
378 path=`rospack find ${pkgname} 2> /dev/null`
379
380 if [ $? -eq 0 ] && [ -d ${path}/srv ]; then
381 echo `find -L ${path}/srv -maxdepth 1 -mindepth 1 -name *.srv ! -regex ".*/[.].*" -print0 | tr '\000' '\n' | sed -e "s/.*\/\(.*\)\.srv/${pkgname}\/\1/g"`
382 fi
383 }
384
385 function _roscomplete_rossrv {
386 COMPREPLY=()
387 arg="${COMP_WORDS[COMP_CWORD]}"
388
389 if [[ $COMP_CWORD == 1 ]]; then
390 opts="show list md5 package packages"
391 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
392 elif [[ $COMP_CWORD == 2 ]]; then
393 case ${COMP_WORDS[1]} in
394 show|users|md5)
395 opts=`_srv_opts ${COMP_WORDS[$COMP_CWORD]}`
396 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
397 ;;
398 package)
399 opts=`rospack list-names`
400 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
401 ;;
402 packages|list)
403 # This shouldn't really have a completion rule
404 ;;
405 esac
406 fi
407 }
408
409 function _roscomplete_search_dir {
410 local arg opts pkgdir catkin_project_libexec_dir
411 COMPREPLY=()
412 arg="${COMP_WORDS[COMP_CWORD]}"
413 if [[ $COMP_CWORD == 1 ]]; then
414 opts=`_ros_list_packages`
415 IFS=$'\n'
416 COMPREPLY=($(compgen -W "${opts}" -- ${arg}))
417 unset IFS
418 elif [[ $COMP_CWORD == 2 ]]; then
419 if [[ -n $CMAKE_PREFIX_PATH ]]; then
420 catkin_project_libexec_dir=`catkin_find --first-only --libexec ${COMP_WORDS[1]} 2> /dev/null`
421 fi
422 pkgdir=`_ros_package_find ${COMP_WORDS[1]}`
423 if [[ -n "$catkin_project_libexec_dir_dir" || -n "$pkgdir" ]]; then
424 opts=`_rosfind -L $catkin_project_libexec_dir $pkgdir ${1} ! -regex ".*/[.].*" ! -regex ".*$pkgdir\/build\/.*" -print0 | tr '\000' '\n' | sed -e "s/.*\/\(.*\)/\1/g"`
425 else
426 opts=""
427 fi
428 IFS=$'\n'
429 COMPREPLY=($(compgen -W "${opts}" -- ${arg}))
430 unset IFS
431 else
432 homedir=`echo $HOME | sed -e "s/\//\t\//g" -e "s/\t/\\\\\/g"`
433 arg=`echo ${arg} | sed -e "s/~/${homedir}/g"`
434 if [[ $arg =~ ^/*.+/.* ]]; then
435 path=${arg%/*}
436 else
437 path=.
438 fi
439 if [[ -e ${path} ]]; then
440 opts=`find -L $path -maxdepth 1 -type d ! -regex ".*/[.].*" ! -regex "^[.]/" -print0 | tr '\000' '\n' | sed -e "s/$/\//g" -e "s/^[.]\///g" -e "s/'/\\\\\'/g" -e "s/ /\\\\\ /g"`$'\n'`find -L $path -maxdepth 1 -type f ! -regex ".*/[.][^.]*" ! -regex "^[.]/" -print0 | tr '\000' '\n' | sed -e "s/^[.]\///g" -e "s/'/\\\\\'/g" -e "s/ /\\\\\ /g"`
441 else
442 opts=""
443 fi
444 IFS=$'\n'
445 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
446 unset IFS
447 if [[ ${#COMPREPLY[*]} = 1 ]]; then
448 newpath=${COMPREPLY[0]%/*}
449 if [[ -d ${newpath} ]]; then
450 opts=`find -L $newpath -maxdepth 1 -type d ! -regex ".*/[.].*" ! -regex "^[.]/" -print0 | tr '\000' '\n' | sed -e "s/$/\//g" -e "s/^[.]\///g" -e "s/'/\\\\\'/g" -e "s/ /\\\\\ /g"`$'\n'`find -L $newpath -maxdepth 1 -type f ! -regex ".*/[.][^.]*" ! -regex "^[.]/" -print0 | tr '\000' '\n' | sed -e "s/^[.]\///g" -e "s/'/\\\\\'/g" -e "s/ /\\\\\ /g"`
451 IFS=$'\n'
452 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
453 unset IFS
454 fi
455 fi
456 fi
457
458 }
459
460 function _roscomplete_exe {
461 _roscomplete_search_dir "-type f -perm +111"
462 }
463
464 function _roscomplete_file {
465 _roscomplete_search_dir "-type f ! -regex .*[.][oa]$"
466 }
467
468 function _roscomplete_launch {
469 arg="${COMP_WORDS[COMP_CWORD]}"
470 COMPREPLY=()
471 if [[ ${arg} =~ \-\-.* ]]; then
472 COMPREPLY=(${COMPREPLY[@]} $(compgen -W "--files --args --nodes --find-node --child --local --screen --server_uri --run_id --wait --port --core --pid --dump-params" -- ${arg}))
473
474 else
475 _roscomplete_search_dir "( -type f -regex .*\.launch$ -o -type f -regex .*\.test$ )"
476 if [[ $COMP_CWORD == 1 ]]; then
477 COMPREPLY=($(compgen -o plusdirs -f -X "!*.launch" -- ${arg}) ${COMPREPLY[@]} $(compgen -o plusdirs -f -X "!*.test" -- ${arg}) ${COMPREPLY[@]})
478 fi
479 fi
480 }
481
482 function _roscomplete_test {
483 arg="${COMP_WORDS[COMP_CWORD]}"
484 if [[ ${arg} =~ \-\-.* ]]; then
485 COMPREPLY=(${COMPREPLY[@]} $(compgen -W "--bare --bare-limit --bare-name --pkgdir --package" -- ${arg}))
486 else
487 _roscomplete_search_dir "( -type f -regex .*\.launch$ -o -type f -regex .*\.test$ )"
488 if [[ $COMP_CWORD == 1 ]]; then
489 COMPREPLY=($(compgen -o plusdirs -f -X "!*.launch" -- ${arg}) ${COMPREPLY[@]} $(compgen -o plusdirs -f -X "!*.test" -- ${arg}) ${COMPREPLY[@]})
490 fi
491 fi
492 }
493
494 function _roscomplete_rosbag {
495 local opts
496 COMPREPLY=()
497 arg="${COMP_WORDS[COMP_CWORD]}"
498
499 if [[ $COMP_CWORD == 1 ]]; then
500 opts="check compress decompress filter fix help info play record reindex"
501 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
502 else
503 if [[ ${arg} =~ \-\-.* ]]; then
504 case ${COMP_WORDS[1]} in
505 check)
506 opts="--genrules --append --noplugins --help"
507 ;;
508 compress|decompress)
509 opts="--output-dir --force --quiet --help"
510 ;;
511 filter)
512 opts="--print --help"
513 ;;
514 fix)
515 opts="--force --noplugins --help"
516 ;;
517 info)
518 opts="--yaml --key --freq --help"
519 ;;
520 play)
521 opts="--help --quiet --immediate --pause --queue --clock --hz --delay --rate --start --skip-empty --loop --keep-alive --try-future-version --topics --bags"
522 ;;
523 record)
524 opts="--help --all --regex --exclude --quiet --output-prefix --output-name --split --size --duration --buffsize --limit --node --bz2"
525 ;;
526 reindex)
527 opts="--help --force --quiet --output-dir"
528 ;;
529 esac
530 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
531 fi
532 fi
533
534 }
535
536 function _roscomplete_rospack {
537 local arg opts
538 COMPREPLY=()
539 arg="${COMP_WORDS[COMP_CWORD]}"
540
541 if [[ $COMP_CWORD == 1 ]]; then
542 opts="help find list list-names list-duplicates langs depends depends-manifests depends1 depends-indent depends-msgsrv depends-why rosdep rosdep0 vcs vcs0 depends-on depends-on1 export plugins cflags-only-I cflags-only-other libs-only-L libs-only-l libs-only-other profile"
543 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
544 else
545 opts=`rospack list-names`
546 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
547 fi
548
549 }
550
551 function _roscomplete_rosnode {
552 local arg opts
553 COMPREPLY=()
554 arg="${COMP_WORDS[COMP_CWORD]}"
555
556 if [[ $COMP_CWORD == 1 ]]; then
557 opts="ping list info machine kill"
558 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
559 elif [[ $COMP_CWORD == 2 ]]; then
560 case ${COMP_WORDS[1]} in
561 info)
562 opts=`rosnode list 2> /dev/null`
563 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
564 ;;
565 ping|list|kill)
566 if [[ ${arg} =~ \-\-.* ]]; then
567 opts="--all --help"
568 else
569 opts=`rosnode list 2> /dev/null`
570 fi
571 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
572 ;;
573 machine)
574 # This takes more logic to determine which machines are present.
575 ;;
576 esac
577 else
578 case ${COMP_WORDS[1]} in
579 kill)
580 # complete on node name
581 opts=`rosnode list 2> /dev/null`
582 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
583 ;;
584 esac
585 fi
586
587 }
588 function _roscomplete_rosparam {
589 local arg opts
590 COMPREPLY=()
591 arg="${COMP_WORDS[COMP_CWORD]}"
592
593 if [[ $COMP_CWORD == 1 ]]; then
594 opts="set get load dump delete list"
595 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
596 elif [[ $COMP_CWORD == 2 ]]; then
597 case ${COMP_WORDS[1]} in
598 set|get|delete|list)
599 opts=`rosparam list 2> /dev/null`
600 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
601 ;;
602 load|dump)
603 # complete on files
604 COMPREPLY=($(compgen -f -- ${arg}))
605 ;;
606 esac
607 elif [[ $COMP_CWORD == 3 ]]; then
608 case ${COMP_WORDS[1]} in
609 load|dump)
610 # complete on namespace
611 opts=`rosparam list 2> /dev/null`
612 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
613 ;;
614 esac
615 fi
616
617 }
618 function _roscomplete_rostopic {
619 local arg opts
620 COMPREPLY=()
621 arg="${COMP_WORDS[COMP_CWORD]}"
622
623 if [[ $COMP_CWORD == 1 ]]; then
624 opts="bw echo hz list pub type find info"
625 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
626 elif [[ $COMP_CWORD -ge 2 ]]; then
627 if [[ ${arg} =~ \-\-.* ]]; then
628 case ${COMP_WORDS[1]} in
629 pub)
630 COMPREPLY=($(compgen -W "--rate --once --file --latch" -- ${arg}))
631 ;;
632 bw)
633 COMPREPLY=($(compgen -W "--window" -- ${arg}))
634 ;;
635 echo)
636 COMPREPLY=($(compgen -W "--bag --filter --nostr --noarr --clear --all offset" -- ${arg}))
637 ;;
638 hz)
639 COMPREPLY=($(compgen -W "--window --filter" -- ${arg}))
640 ;;
641 list)
642 COMPREPLY=($(compgen -W "--bag --verbose --host" -- ${arg}))
643 ;;
644 esac
645 else
646 case ${COMP_WORDS[1]} in
647 bw|echo|hz|list|type|info)
648 if [[ ${COMP_WORDS[$(( $COMP_CWORD - 1 ))]} == "-b" ]]; then
649 COMPREPLY=($(compgen -f -- ${arg}))
650 else
651 opts=`rostopic list 2> /dev/null`
652 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
653 fi
654 ;;
655 find)
656 opts=`_msg_opts ${COMP_WORDS[$COMP_CWORD]}`
657 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
658 ;;
659 pub)
660 if [[ $COMP_CWORD == 2 ]]; then
661 opts=`rostopic list 2> /dev/null`
662 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
663 elif [[ $COMP_CWORD == 3 ]]; then
664 opts=`_msg_opts ${COMP_WORDS[$COMP_CWORD]}`
665 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
666 elif [[ $COMP_CWORD == 4 ]]; then
667 opts=`rosmsg-proto msg 2> /dev/null -s ${COMP_WORDS[3]}`
668 if [ 0 -eq $? ]; then
669 COMPREPLY="$opts"
670 fi
671 fi
672 ;;
673 esac
674 fi
675 fi
676 }
677
678 function _roscomplete_rosservice {
679 local arg opts
680 COMPREPLY=()
681 arg="${COMP_WORDS[COMP_CWORD]}"
682
683 if [[ $COMP_CWORD == 1 ]]; then
684 opts="list call type find uri"
685 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
686 elif [[ $COMP_CWORD == 2 ]]; then
687 case ${COMP_WORDS[1]} in
688 uri|list|type|call)
689 opts=`rosservice list 2> /dev/null`
690 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
691 ;;
692 find)
693 # Need a clever way to do message searching
694 opts=`_srv_opts ${COMP_WORDS[$COMP_CWORD]}`
695 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
696 ;;
697 esac
698 elif [[ $COMP_CWORD == 3 ]]; then
699 case ${COMP_WORDS[1]} in
700 call)
701 type=`rosservice type ${COMP_WORDS[2]}`
702 opts=`rosmsg-proto srv 2> /dev/null -s ${type}`
703 if [ 0 -eq $? ]; then
704 COMPREPLY="$opts"
705 fi
706 ;;
707 esac
708 fi
709
710 }
711
712 function _msg_opts {
713 local arg pkgs pkgname msgname searchmsg path count opts
714
715 if [[ $1 =~ .+/.* ]]; then
716 pkgname=${1%%/*}
717 msgname=${1#*/}
718 searchmsg=1
719 else
720 pkgname=${1}
721 fi
722
723 if [[ -z ${searchmsg} ]]; then
724 pkgs=(`rospack list | grep "^${pkgname}"`)
725 count=0
726
727 opts=""
728
729 for (( i = 0 ; i < ${#pkgs[@]} ; i=i+2 )); do
730 if [[ -d ${pkgs[i+1]}/msg ]]; then
731 opts="$opts ${pkgs[i]}/"
732 pkgname=${pkgs[i]}
733 count=$((count+1))
734 fi
735 done
736
737 if [[ $count -gt 1 ]]; then
738 echo $opts
739 return 0
740 fi
741 fi
742
743 path=`rospack find ${pkgname} 2> /dev/null`
744
745 if [ $? -eq 0 ] && [ -d ${path}/msg ]; then
746 echo `find -L ${path}/msg -maxdepth 1 -mindepth 1 -name *.msg ! -regex ".*/[.].*" -print0 | tr '\000' '\n' | sed -e "s/.*\/\(.*\)\.msg/${pkgname}\/\1/g"`
747 fi
748 }
749
750 function _roscomplete_rosmsg {
751 local arg opts
752 COMPREPLY=()
753 arg="${COMP_WORDS[COMP_CWORD]}"
754
755 if [[ $COMP_CWORD == 1 ]]; then
756 opts="show list md5 package packages"
757 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
758 elif [[ $COMP_CWORD == 2 ]]; then
759 case ${COMP_WORDS[1]} in
760 show|users|md5)
761 opts=`_msg_opts ${COMP_WORDS[$COMP_CWORD]}`
762 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
763 ;;
764 package)
765 opts=`rospack list-names`
766 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
767 ;;
768 packages|list)
769 # This shouldn't really have a completion rule
770 ;;
771 esac
772 fi
773
774 }
775
776 function _roscomplete_roscreate_pkg {
777 local arg opts
778 COMPREPLY=()
779 arg="${COMP_WORDS[COMP_CWORD]}"
780
781 if [[ $COMP_CWORD != 1 ]]; then
782 opts=`rospack list-names`
783 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
784 fi
785 }
786
787 function _roscomplete_roswtf {
788 local arg
789 COMPREPLY=()
790 arg="${COMP_WORDS[COMP_CWORD]}"
791 if [[ ${arg} =~ \-\-.* ]]; then
792 COMPREPLY=($(compgen -W "--all --no-plugins --offline" -- ${arg}))
793 else
794 if [[ $COMP_CWORD == 1 ]]; then
795 COMPREPLY=($(compgen -o plusdirs -f -X "!*.launch" -- ${arg}))
796 fi
797 fi
798 }
799
800 function _roscomplete_rosclean {
801 local arg opts
802 COMPREPLY=()
803 arg="${COMP_WORDS[COMP_CWORD]}"
804 if [[ $COMP_CWORD == 1 ]]; then
805 opts="check purge"
806 COMPREPLY=($(compgen -W "$opts" -- ${arg}))
807 fi
808 }
809
810 complete -F "_roscomplete_sub_dir" -o "nospace" "roscd"
811 complete -F "_roscomplete_sub_dir" -o "nospace" "rospd"
812 complete -F "_roscomplete_sub_dir" -o "nospace" "rosls"
813 complete -F "_roscomplete_rosmake" "rosmake"
814 complete -F "_roscomplete_rosclean" "rosclean"
815 complete -F "_roscomplete_exe" "rosrun"
816 complete -F "_roscomplete_file" "rosed"
817 complete -F "_roscomplete_file" "roscp"
818 complete -F "_roscomplete_launch" -o filenames "roslaunch"
819 complete -F "_roscomplete_test" -o filenames "rostest"
820 complete -F "_roscomplete_rospack" "rospack"
821 complete -F "_roscomplete_rosbag" -o default "rosbag"
822 complete -F "_roscomplete_rosnode" "rosnode"
823 complete -F "_roscomplete_rosparam" "rosparam"
824 complete -F "_roscomplete_rostopic" "rostopic"
825 complete -F "_roscomplete_rosservice" "rosservice"
826 complete -F "_roscomplete_rosmsg" "rosmsg"
827 complete -F "_roscomplete_rossrv" "rossrv"
828 complete -F "_roscomplete_roscreate_pkg" "roscreate-pkg"
829 complete -F "_roscomplete_roswtf" -o filenames "roswtf"
0 alias roscd 'cd `rospack find \!*; if ($status) rosstack find \!*;`'
1 alias rcd roscd
2 alias rosls 'ls `rospack find \!*`'
3 #alias rosd 'echo "unimplemented"'
4 #alias rospd 'echo "unimplemented"'
5 #alias rosed 'echo "unimplemented"'
6 #alias roscmd 'echo "unimplemented"'
7 #alias roscp 'echo "unimplemented"'
8
9 complete roscd 'p/1/`rospack list-names && rosstack list-names`/'
10 complete rosmake 'p/*/`rospack list-names && rosstack list-names`/'
11 complete rosupdate 'p/1/`rospack list-names && rosstack list-names`/'
12 complete rosdeb 'p/*/`rospack list-names && rosstack list-names`/'
13
14 complete rosdep \
15 'p/1/(help generate_bash install depdb what_needs check satisfy)/' \
16 'n/{generate_bash,install,depdb,check,satisfy}/`rospack list-names`/'
17
18 complete rospack \
19 'p/1/(help find list list-names langs deps deps-manifests deps1 deps-indent rosdeps rosdeps0 vcs vcs0 depends-on)/' \
20 'n/{find,deps,deps-manifests,deps1,deps-indent,rosdeps,rosdeps0,vcs,vcs0,depends-on,depends-on1,export,cflags-only-I,cflags-only-other,libs-only-L,libs-only-l,libs-only-other}/`rospack list-names`/'
21
22 complete rosstack \
23 'p/1/(help find list list-names langs deps deps-manifests deps1 deps-indent rosdeps rosdeps0 vcs vcs0 depends-on)/' \
24 'n/{find,deps,deps-manifests,deps1,deps-indent,rosdeps,rosdeps0,vcs,vcs0,depends-on,depends-on1,export,cflags-only-I,cflags-only-other,libs-only-L,libs-only-l,libs-only-other}/`rosstack list-names`/'
25
26 complete rostopic \
27 'p/1/(hz echo type list)/' \
28 'n/{echo,hz,type}/`rostopic list | sort -u`/'
29
30 complete rosmsg \
31 'p/1/(show users package packages)/' \
32 'n@{show,users}@`rostopic list -v | grep " \* " | cut -f 4 -d " " | grep -v unknown | sed -e "s/[\d93\d91]//g"`@' \
33 'n/{package,packages}/`rospack list-names`/'
34
35 complete rossrv \
36 'p/1/(show users package packages)/' \
37 'n@{show,users}@`rostopic list -v | grep " \* " | cut -f 4 -d " +" | grep -v unknown | sed -e "s/[\d93\d91]//g"`@' \
38 'n/{package,packages}/`rospack list-names`/'
39
40 complete rosrun \
41 'p/1/`rospack list-names`/' \
42 'p@2@`rospack find $:1 | xargs -I 1 find 1 -perm /a+x -type f -printf "%f\n"`@'
43
44 complete roslaunch \
45 'p/1/`rospack list-names && find . -maxdepth 3 -regex ".*[xl][am][ul]n*c*h*" -printf "%P\n" `/' \
46 'p@2@`rospack find $:1 | xargs -I 1 find 1 -maxdepth 3 -regex ".*[xl][am][ul]n*c*h*" -type f -printf "%f\n"`@'
47
0 # roszsh - functions to support ROS users
1 # Useful things to know:
2 # 'local' variables get unset after function, all others stay forever
3 # 'reply' is the var used by zsh compctl builtin
4
5 function _rossed {
6 if [[ `uname` == Darwin || `uname` == FreeBSD ]]; then
7 sed -E "$@"
8 else
9 sed -r "$@"
10 fi
11 }
12
13 function _rosfind {
14 if [[ `uname` == Darwin || `uname` == FreeBSD ]]; then
15 # BSD find needs -E for extended regexp
16 find -E "$@"
17 else
18 find "$@"
19 fi
20 }
21
22 # _ros_location_find
23 # based on $ROS_LOCATIONS, which the user can set to any colon
24 # separated of key=folder pairs also resolves keys 'log' and
25 # 'test_results' to ROS defaults finally resolves to package, then
26 # stack echoes its result
27 function _ros_location_find {
28 local homedir ROS_LOCATION_KEYS_ARR ROS_LOCATIONS_ARR loc
29 homedir=`echo $HOME | sed -e "s/\//\t\//g" -e "s/\t/\\\\\/g"`
30 ROS_LOCATION_KEYS_ARR=(`echo $ROS_LOCATIONS | _rossed -e 's/([^:=]*)=([^:=]*)(:*[^=])*(:|$)/\1 /g'`)
31 ROS_LOCATIONS_ARR=(`echo $ROS_LOCATIONS | _rossed -e 's/([^:=]*)=([^:=]*)(:*[^=])*(:|$)/\2 /g' -e "s/~/${homedir}/g"`)
32
33 for (( i = 1 ; i <= ${#ROS_LOCATION_KEYS_ARR[@]} ; i++ )); do
34 if [[ $1 == ${ROS_LOCATION_KEYS_ARR[$i]} ]]; then
35 echo ${ROS_LOCATIONS_ARR[i]}
36 return 0
37 fi
38 done
39
40 if [[ $1 == log ]]; then
41 echo `roslaunch-logs`
42 return 0
43 elif [[ $1 == test_results ]]; then
44 echo `rosrun rosunit test_results_dir.py`
45 return 0
46 fi
47
48 loc=`export ROS_CACHE_TIMEOUT=-1.0 && rospack find $1 2> /dev/null`
49 if [[ $? != 0 ]]; then
50 loc=`export ROS_CACHE_TIMEOUT=-1.0 && rosstack find $1 2> /dev/null`
51 if [[ $? != 0 ]]; then
52 return 1
53 fi
54 echo $loc
55 return 0
56 fi
57 echo $loc
58 return 0
59 }
60
61 function _ros_list_locations {
62 local ROS_LOCATION_KEYS packages stacks
63 ROS_LOCATION_KEYS=`echo $ROS_LOCATIONS | _rossed -e 's/([^:=]*)=([^:=]*)(:*[^=])*(:|$)/\1 /g'`
64 packages=`export ROS_CACHE_TIMEOUT=-1.0 && rospack list-names`
65 stacks=`export ROS_CACHE_TIMEOUT=-1.0 && rosstack list-names`
66 echo $packages $stacks log test_results $ROS_LOCATION_KEYS | tr ' ' '\n'
67 return 0
68 }
69
70 function _ros_decode_path {
71 local rosname rosdir reldir last rospackdir rosstack_result rosstackdir
72 rosvals=()
73 if [[ -z $1 ]]; then
74 return 0
75 fi
76
77 echo $1 | grep -G '.\+/.*' > /dev/null
78 if [[ $? == 0 ]]; then
79 rosname=${1%%/*}
80 reldir=/${1#*/}
81 last=${reldir##*/}
82 reldir=${reldir%/*}/
83 else
84 rosname=$1
85 if [[ -z $2 || $2 != "forceeval" ]]; then
86 rosvals=(${rosname})
87 return 1
88 fi
89 fi
90
91 if [[ $rosname == ros ]]; then
92 rosdir=`rosstack find ros`
93 elif [[ $rosname == pkg ]]; then
94 rosdir=${ROS_PACKAGE_PATH%%:*}
95 elif [[ $rosname == log ]]; then
96 rosdir=`roslaunch-logs`
97 elif [[ $rosname == test_results ]]; then
98 rosdir=`rosrun rosunit test_results_dir.py`
99 else
100 rospackdir=`export ROS_CACHE_TIMEOUT=-1.0 && rospack find $rosname 2> /dev/null`
101 rospack_result=$?
102 rosstackdir=`export ROS_CACHE_TIMEOUT=-1.0 && rosstack find $rosname 2> /dev/null`
103 rosstack_result=$?
104 if [[ $rospack_result == 0 ]]; then
105 rosdir=$rospackdir
106 elif [[ $rosstack_result == 0 ]]; then
107 rosdir=$rosstackdir
108 else
109 rosvals=(${rosname})
110 return 1
111 fi
112 fi
113
114 rosvals=(${rosname} ${rosdir} ${reldir} ${last})
115 }
116
117 function rospython {
118 if [[ $1 = "--help" ]]; then
119 echo -e "usage: rospython [package] \n\nRun python loading package manifest first."
120 return 0
121 fi
122 if [[ -z $1 ]]; then
123 if [[ -f ./manifest.xml ]]; then
124 pkgname=`basename \`pwd\``
125 python -i -c "import roslib; roslib.load_manifest('$pkgname')"
126 else
127 python
128 fi
129 else
130 python -i -c "import roslib; roslib.load_manifest('$1')"
131 fi
132 }
133
134 function roscd {
135 local rosvals
136 if [[ $1 = "--help" ]] | [[ $# -gt 1 ]]; then
137 echo -e "usage: roscd package\n\nJump to target package."
138 return 0
139 fi
140 if [ -z $1 ]; then
141 if [ -z $ROS_WORKSPACE ]; then
142 echo -e "No ROS_WORKSPACE set. Please set ROS_WORKSPACE to use roscd with no arguments."
143 return 1
144 fi
145 cd ${ROS_WORKSPACE}
146 return 0
147 fi
148
149 _ros_decode_path $1 forceeval
150 if [ $? != 0 ]; then
151 echo "roscd: No such package '$1'"
152 return 1
153 elif [ -z ${rosvals[1]} ]; then
154 if [ -z $ROS_WORKSPACE ]; then
155 echo -e "No ROS_WORKSPACE set. Please set ROS_WORKSPACE to use roscd with no arguments."
156 return 1
157 fi
158 cd ${ROS_WORKSPACE}
159 return 0
160 else
161 cd ${rosvals[2]}${rosvals[3]}${rosvals[4]}
162 return 0
163 fi
164 }
165
166 function _is_integer {
167 [ "$1" -eq "$1" ] > /dev/null 2>&1
168 return $?
169 }
170
171 function rosd {
172 if [[ $1 = "--help" ]]; then
173 echo -e "usage: rosd\n\nDisplays the list of currently remembered directories with indexes."
174 return 0
175 fi
176 let count=0;
177 for items in `dirs`;
178 do
179 echo $count $items;
180 let count=$((count+1));
181 done
182 }
183
184 function rospd {
185 if [[ $1 = "--help" ]]; then
186 echo -e "usage: rospd\n\nLike pushd, also accepts indexes from rosd."
187 return 0
188 fi
189 if _is_integer $1; then
190 pushd +$1 > /dev/null ;
191 else
192 local rosvals
193 _ros_decode_path $1 forceeval
194 pushd ${rosvals[2]}${rosvals[3]}${rosvals[4]} > /dev/null ;
195 fi
196 rosd
197 }
198
199 function rosls {
200 local rosvals
201 if [[ $1 = "--help" ]]; then
202 echo -e "usage: rosls [package]\n\nLists contents of a package directory."
203 return 0
204 fi
205 _ros_decode_path $1 forceeval
206 ls ${rosvals[2]}${rosvals[3]}${rosvals[4]} $2
207 }
208
209 # sets arg as return value
210 function _roscmd {
211 local pkgdir exepath opt
212 pkgdir=`export ROS_CACHE_TIMEOUT=-1.0 && rospack find $1 2> /dev/null`
213 if [[ $? != 0 ]] ; then
214 echo "Couldn't find package [$1]"
215 return 1
216 fi
217 exepath=(`find $pkgdir -name $2 -type f`)
218 if [[ ${#exepath[@]} == 0 ]] ; then
219 echo "That file does not exist in that package."
220 return 1
221 elif [[ ${#exepath[@]} -gt 1 ]] ; then
222 echo "You have chosen a non-unique filename, please pick one of the following:"
223 select opt in ${exepath[@]}; do
224 echo $opt
225 break
226 done
227 else
228 opt=${exepath[1]}
229 fi
230 arg=${opt}
231 }
232
233 function rosed {
234 local arg
235 if [[ $1 = "--help" ]]; then
236 echo -e "usage: rosed [package] [file]\n\nEdit a file within a package."
237 return 0
238 fi
239 _roscmd ${1} ${2}
240 if [[ -z $EDITOR ]]; then
241 vim ${arg}
242 else
243 $EDITOR ${arg}
244 fi
245 }
246
247 function roscp {
248 local arg
249 if [[ $1 = "--help" ]] | [[ $# -ne 3 ]]; then
250 echo -e "usage: roscp package filename target\n\nCopy a file from a package to target location."
251 return 0
252 fi
253 _roscmd ${1} ${2}
254 cp ${arg} ${3}
255 }
256
257 function _roscomplete {
258 local arg opts stack_opts
259 reply=()
260 opts=`export ROS_CACHE_TIMEOUT=-1.0 && rospack list-names`
261 stack_opts=`export ROS_CACHE_TIMEOUT=-1.0 && rosstack list-names`
262 IFS=$'\n'
263 reply=(${=opts} ${=stack_opts})
264 unset IFS
265 }
266
267 function _roscomplete_rosmake {
268 local param
269 _roscomplete
270 if [[ $PREFIX == "--"* ]]; then
271 param="--test-only --all --mark-installed --unmark-installed --robust --build-everything --specified-only --buildtest --buildtest1 --output --pre-clean --bootstrap --disable-logging --target --pjobs --threads --profile --skip-blacklist --status-rate"
272 reply=(${=reply} ${=param})
273 fi
274 }
275
276 function _roscomplete_sub_dir {
277 local arg opts rosvals sedcmd stack_opts
278 reply=()
279 arg="$1"
280 _ros_decode_path ${arg}
281 if [[ -z ${rosvals[3]} ]]; then
282 opts=`export ROS_CACHE_TIMEOUT=-1.0 && rospack list-names`
283 stack_opts=`export ROS_CACHE_TIMEOUT=-1.0 && rosstack list-names`
284 IFS=$'\n'
285 reply=(${=opts} ${=stack_opts})
286 unset IFS
287 else
288 if [ -e ${rosvals[2]}${rosvals[3]} ]; then
289 sedcmd="s:^${rosvals[2]}:${rosvals[1]}:"g
290 #echo
291 #echo $sedcmd
292 #echo
293 opts=`find ${rosvals[2]}${rosvals[3]} -maxdepth 1 -mindepth 1 -type d ! -regex ".*/[.].*" -print0 | tr '\000' '\n' | sed -e "$sedcmd"`
294 #echo $opts
295 else
296 opts=''
297 fi
298 IFS=$'\n'
299 reply=(${=opts})
300 unset IFS
301 fi
302 }
303
304 function _roscomplete_search_dir {
305 local words arg opts pkgdir pkgdir_result stack_result
306 reply=()
307 words=(${=BUFFER})
308 pkgdir=`export ROS_CACHE_TIMEOUT=-1.0 && rospack find ${words[2]} 2> /dev/null`
309 pkgdir_result=$?
310 stackdir=`export ROS_CACHE_TIMEOUT=-1.0 && rosstack find ${words[2]} 2> /dev/null`
311 stack_result=$?
312 if [[ $pkgdir_result == 0 ]]; then
313 opts=`find $pkgdir ${=1} -print0 | tr '\000' '\n' | sed -e "s/.*\/\(.*\)/\1/g"`
314 elif [[ $stack_result == 0 ]] ; then
315 opts=`find $stackdir ${=1} -print0 | tr '\000' '\n' | sed -e "s/.*\/\(.*\)/\1/g"`
316 else
317 opts=""
318 fi
319 IFS=$'\n'
320 reply=(${=opts})
321 unset IFS
322 }
323
324 function _roscomplete_exe {
325 _roscomplete_search_dir "-type f -perm +111 -regex .*/.*$"
326 }
327
328 function _roscomplete_file {
329 _roscomplete_search_dir "-type f ! -regex .*/[.].* ! -regex .*[.][oa]$"
330 }
331
332 function _roscomplete_launchfile {
333 _roscomplete_search_dir "( -type f -regex .*\.launch$ -o -type f -regex .*\.test$ )"
334 }
335
336 function _roscomplete_rosbag {
337 local opts
338 reply=()
339
340 if [[ ${CURRENT} == 2 ]]; then
341 opts="check compress decompress filter fix help info play record reindex"
342 reply=(${=opts})
343 else
344 if [[ ${=${(s: :)words}[$(( ${CURRENT} ))]} =~ \-\- ]]; then
345 opts="--all --help"
346 case ${=${(s: :)words}[2]} in
347 check)
348 opts="--genrules --append --noplugins --help"
349 ;;
350 compress|decompress)
351 opts="--output-dir --force --quiet --help"
352 ;;
353 filter)
354 opts="--print --help"
355 ;;
356 fix)
357 opts="--force --noplugins --help"
358 ;;
359 info)
360 opts="--yaml --key --freq --help"
361 ;;
362 play)
363 opts="--help --quiet --immediate --pause --queue --clock --hz --delay --rate --start --skip-empty --loop --keep-alive --try-future-version --topics --bags"
364 ;;
365 record)
366 opts="--help --all --regex --exclude --quiet --output-prefix --output-name --split --size --duration --buffsize --limit --node --bz2"
367 ;;
368 reindex)
369 opts="--help --force --quiet --output-dir"
370 ;;
371 esac
372 reply=(${=opts})
373 fi
374 fi
375
376 }
377
378 function _roscomplete_rospack {
379 local opts
380 reply=()
381 if [[ ${CURRENT} == 2 ]]; then
382 opts="help find list list-names list-duplicates langs depends depends-manifests depends1 depends-indent depends-msgsrv depends-why rosdep rosdep0 vcs vcs0 depends-on depends-on1 export plugins cflags-only-I cflags-only-other libs-only-L libs-only-l libs-only-other profile"
383 reply=(${=opts})
384 else
385 opts=`rospack list-names`
386 reply=(${=opts})
387 fi
388 }
389
390 function _roscomplete_rosnode {
391 local opts
392 reply=()
393
394 if [[ ${CURRENT} == 2 ]]; then
395 opts="ping list info machine kill"
396 reply=(${=opts})
397 elif [[ ${CURRENT} == 3 ]]; then
398 case ${=${(s: :)words}[2]} in
399 info)
400 opts=`rosnode list 2> /dev/null`
401 reply=(${=opts})
402 ;;
403 ping|list|kill)
404 if [[ ${=${(s: :)words}[$(( ${CURRENT} ))]} =~ \-\- ]]; then
405 opts="--all --help"
406 else
407 opts=`rosnode list 2> /dev/null`
408 fi
409 reply=(${=opts})
410 ;;
411 machine)
412 # This takes more logic to determine which machines are present.
413 ;;
414 esac
415 else
416 case ${=${(s: :)words}[2]} in
417 kill)
418 # complete on node name
419 opts=`rosnode list 2> /dev/null`
420 reply=(${=opts})
421 ;;
422 esac
423 fi
424 }
425
426 function _roscomplete_rosparam {
427 local opts
428 reply=()
429
430 if [[ ${CURRENT} == 2 ]]; then
431 opts="set get load dump delete list"
432 reply=(${=opts})
433 elif [[ ${CURRENT} == 3 ]]; then
434 case ${=${(s: :)words}[2]} in
435 set|get|delete|list)
436 opts=`rosparam list 2> /dev/null`
437 reply=(${=opts})
438 ;;
439 load|dump)
440 # complete on files
441 reply=(${=opts})
442 ;;
443 esac
444 elif [[ ${CURRENT} == 4 ]]; then
445 case ${=${(s: :)words}[2]} in
446 load|dump)
447 # complete on namespace
448 opts=`rosparam list 2> /dev/null`
449 reply=(${=opts})
450 ;;
451 esac
452 fi
453
454 }
455 function _roscomplete_rostopic {
456 local opts
457 reply=()
458
459 if [[ ${CURRENT} == 2 ]]; then
460 opts="bw echo hz list pub type find info"
461 reply=(${=opts})
462 elif [[ ${CURRENT} > 2 ]]; then
463 if [[ ${=${(s: :)words}[$(( ${CURRENT} ))]} =~ \-\- ]]; then
464 case ${=${(s: :)words}[2]} in
465 pub)
466 opts="--help --rate --once --file --latch"
467 ;;
468 bw)
469 opts="--help --window"
470 ;;
471 echo)
472 opts="--help --bag --filter --nostr --noarr --clear --all --offset"
473 ;;
474 hz)
475 opts="--help --window --filter"
476 ;;
477 list)
478 opts="--help --bag --verbose --host"
479 ;;
480 type|info)
481 opts="--help"
482 ;;
483 esac
484 reply=(${=opts})
485 else
486 case ${=${(s: :)words}[2]} in
487 bw|echo|hz|list|type|info)
488 if [[ ${=${(s: :)words}[$(( ${CURRENT} -1 ))]} == "-b" ]]; then
489 opts=`find . -maxdepth 1 -type f -not -name ".*" -not -name "*[~#]" | sed 's!.*/!!'`
490 reply=(${=opts})
491 else
492 opts=`rostopic list 2> /dev/null`
493 reply=(${=opts})
494 fi
495 ;;
496 find)
497 opts=`_msg_opts ${=${(s: :)words[-1]}}`
498 reply=(${=opts})
499 ;;
500 pub)
501 if [[ ${CURRENT} == 3 ]]; then
502 opts=`rostopic list 2> /dev/null`
503 reply=(${=opts})
504 elif [[ ${CURRENT} == 4 ]]; then
505 if [[ ${=${(s: :)words}[$(( ${CURRENT} ))]} =~ / ]]; then
506 opts=`_msg_opts ${=${(s: :)words[-1]}} | sed -s 's/.*\///'`
507 else
508 opts=`_msg_opts ${=${(s: :)words[-1]}}`
509 fi
510 reply=(${=opts})
511 elif [[ ${CURRENT} == 5 ]]; then
512 opts=`rosmsg-proto msg 2> /dev/null -s ${=${(s: :)words[-1]}}`
513 reply=(${=opts})
514 fi
515 ;;
516 esac
517 fi
518 fi
519 }
520
521 function _roscomplete_rosservice {
522 local opts
523 reply=()
524
525
526 if [[ ${CURRENT} == 2 ]]; then
527 opts="list call type find uri"
528 reply=(${=opts})
529 elif [[ ${CURRENT} == 3 ]]; then
530 case ${=${(s: :)words}[2]} in
531 uri|list|type|call)
532 opts=`rosservice list 2> /dev/null`
533 IFS=$'\n'
534 reply=(${=opts})
535 unset IFS
536 ;;
537 find)
538 opts=`_srv_opts ${=${(s: :)words[-1]}}`
539 reply=(${=opts})
540 ;;
541 esac
542 elif [[ ${CURRENT} == 4 ]]; then
543 case ${=${(s: :)words}[2]} in
544 call)
545 type=`rosservice type ${=${(s: :)words[-1]}}`
546 opts=`rosmsg-proto srv 2> /dev/null -s ${type}`
547 reply=(${=opts})
548 ;;
549 esac
550 fi
551
552 }
553
554 function _msg_opts {
555 local pkgname msgname searchmsg pkgs count pkgname2 opts result
556 unset searchmsg
557
558 if [[ $1 =~ .+/.* ]]; then
559 pkgname=${1%%/*}
560 msgname=${1#*/}
561 searchmsg=1
562 else
563 pkgname=${1}
564 fi
565
566 if [[ -z ${searchmsg} ]]; then
567 pkgs=`command rospack list | grep "^${pkgname}"`
568 count=0
569
570 opts=""
571
572 for pkg in ${(f)pkgs}; do
573 pkgdir=${=${(s: :)pkg}[2]}
574 if [[ -d $pkgdir/msg ]]; then
575 pkgname2=${=${(s: :)pkg}[1]}
576 opts="$opts $pkgname2/"
577 count=$((count+1))
578 fi
579 done
580
581 if [[ $count > 0 ]]; then
582 echo $opts
583 return 0
584 fi
585 fi
586
587 pkgpath=`rospack find ${pkgname} 2> /dev/null`
588 if [[ $? == 0 ]] && [[ -d ${pkgpath}/msg ]]; then
589 result=`find -L ${pkgpath}/msg -maxdepth 1 -mindepth 1 -name \*.msg ! -regex ".\*/[.].\*" -print0 | tr '\000' '\n' | sed -e "s/.*\/\(.*\)\.msg/${pkgname}\/\1/g"`
590 echo $result
591 fi
592 }
593
594 function _roscomplete_rosmsg {
595 local opts
596 reply=()
597
598
599 if [[ ${CURRENT} == 2 ]]; then
600 opts="show list md5 package packages"
601 reply=(${=opts})
602 elif [[ ${CURRENT} == 3 ]]; then
603 case ${=${(s: :)words}[2]} in
604 show|users|md5)
605 opts=`_msg_opts ${=${(s: :)words[-1]}}`
606 reply=(${=opts})
607 ;;
608 package)
609 opts=`rospack list-names`
610 reply=(${=opts})
611 ;;
612 packages|list)
613 # This shouldn't really have a completion rule
614 ;;
615 esac
616 fi
617 }
618
619 function _srv_opts {
620 local pkgname srvname searchsrv pkgs count opts pkgpath result
621 unset searchsrv
622
623 if [[ $1 =~ .+/.* ]]; then
624 pkgname=${1%%/*}
625 srvname=${1#*/}
626 searchsrv=1
627 else
628 pkgname=${1}
629 fi
630
631 if [[ -z ${searchsrv} ]]; then
632 pkgs=`command rospack list | grep "^${pkgname}"`
633 count=0
634
635 opts=""
636
637 for pkg in ${(f)pkgs}; do
638 pkgdir=${=${(s: :)pkg}[2]}
639 if [[ -d $pkgdir/srv ]]; then
640 pkgname2=${=${(s: :)pkg}[1]}
641 opts="$opts $pkgname2/"
642 count=$((count+1))
643 fi
644 done
645
646 if [[ $count > 0 ]]; then
647 echo $opts
648 return 0
649 fi
650 fi
651
652 pkgpath=`rospack find ${pkgname} 2> /dev/null`
653 if [[ $? == 0 ]] && [[ -d ${pkgpath}/srv ]]; then
654 result=`find -L ${pkgpath}/srv -maxdepth 1 -mindepth 1 -name \*.srv ! -regex ".\*/[.].\*" -print0 | tr '\000' '\n' | sed -e "s/.*\/\(.*\)\.srv/${pkgname}\/\1/g"`
655 echo $result
656 fi
657 }
658
659 function _roscomplete_rossrv {
660 local opts
661 reply=()
662
663 if [[ ${CURRENT} == 2 ]]; then
664 opts="show list md5 package packages"
665 reply=(${=opts})
666 elif [[ ${CURRENT} == 3 ]]; then
667 case ${=${(s: :)words}[2]} in
668 show|users|md5)
669 opts=`_srv_opts ${=${(s: :)words[-1]}}`
670 reply=(${=opts})
671 ;;
672 package)
673 opts=`rospack list-names`
674 reply=(${=opts})
675 ;;
676 packages|list)
677 # This shouldn't really have a completion rule
678 ;;
679 esac
680 fi
681 }
682
683 function _roscomplete_roscreate_pkg {
684 local opts
685 reply=()
686
687 if [[ ${CURRENT} > 2 ]]; then
688 opts=`rospack list-names`
689 reply=(${=opts})
690 fi
691 }
692
693 compctl -K "_roscomplete_sub_dir" -S / "roscd" "rospd" "rosls"
694 compctl -K "_roscomplete_rosmake" "rosmake"
695
696 compctl -x 'p[1]' -k "(check purge)" -- "rosclean"
697 compctl -f -x 'p[1]' -K "_roscomplete" - 'p[2]' -K _roscomplete_file -- "rosed" "roscp"
698 compctl -f -x 'p[1]' -K "_roscomplete" - 'p[2]' -K _roscomplete_exe -- "rosrun"
699 compctl -/g '*.(launch|test)' -x 'p[1]' -K "_roscomplete" -tx - 'p[2]' -K _roscomplete_launchfile -- + -x 'S[--]' -k "(--files --args --nodes --find-node --child --local --screen --server_uri --run_id --wait --port --core --pid --dump-params)" -- "roslaunch"
700 compctl -/g '*.(launch|test)' -x 'p[1]' -K "_roscomplete" -tx - 'p[2]' -K _roscomplete_launchfile -- + -x 'S[--]' -k "(--bare --bare-limit --bare-name --pkgdir --package)" -- "rostest"
701 compctl -K "_roscomplete_rospack" "rospack"
702 compctl -K "_roscomplete_rosbag" "rosbag"
703 compctl -K "_roscomplete_rosnode" "rosnode"
704 compctl -K "_roscomplete_rosparam" "rosparam"
705 compctl -x 'p[0,2]' -K "_roscomplete_rostopic" - 'n[1,/] p[3]' -K "_roscomplete_rostopic" - 'p[3]' -S '' -K "_roscomplete_rostopic" - 'p[4]' -K "_roscomplete_rostopic" -- "rostopic"
706 compctl -K "_roscomplete_rosservice" "rosservice"
707 compctl -x 'p[1]' -k "(md5 package packages show users)" - 'p[2]' -S '' -K "_roscomplete_rosmsg" -- "rosmsg"
708 compctl -x 'p[1]' -k "(md5 package packages show users)" - 'p[2]' -S '' -K "_roscomplete_rossrv" -- "rossrv"
709 compctl -K "_roscomplete_roscreate_pkg" "roscreate-pkg"
710 compctl -/g '*.(launch|test)' -x 'S[--]' -k "(--all --no-plugins --offline)" -- "roswtf"
0 #!/usr/bin/env bash
1 if [[ $1 = "--help" ]]; then
2 echo "Usage: rosrun PACKAGE EXECUTABLE [ARGS]"
3 echo " rosrun will locate PACKAGE and try to find"
4 echo " an executable named EXECUTABLE in the PACKAGE tree."
5 echo " If it finds it, it will run it with ARGS."
6 exit 0
7 fi
8 if [ $# -lt 2 ]; then
9 echo "Usage: rosrun PACKAGE EXECUTABLE [ARGS]"
10 echo " rosrun will locate PACKAGE and try to find"
11 echo " an executable named EXECUTABLE in the PACKAGE tree."
12 echo " If it finds it, it will run it with ARGS."
13 exit 1
14 fi
15 # early check that filename does not end with '/'
16 case $2 in
17 */) echo "Invalid filename: " $2
18 exit 1
19 ;;
20 esac
21 # basename also makes .//foo into foo
22 basename=`basename $2`
23 if [[ -n $CMAKE_PREFIX_PATH ]]; then
24 catkin_project_libexec_dir=`catkin_find --first-only --libexec $1 2> /dev/null`
25 fi
26 pkgdir=`rospack find $1`
27 if [[ -z $catkin_project_libexec_dir && -z $pkgdir ]]; then
28 exit 2
29 fi
30 if [[ ! $2 == */* ]]; then
31 # The -perm /mode usage is not available in find on the Mac
32 #exepathlist=(`find $pkgdir -name $2 -type f -perm /u+x,g+x,o+x`)
33 # -L: #3475
34 exepathlist=(`find -L $catkin_project_libexec_dir $pkgdir -name $2 -type f -perm +111 ! -regex ".*$pkgdir\/build\/.*" | uniq`)
35 if [[ ${#exepathlist[@]} == 0 ]] ; then
36 echo "[rosrun] Couldn't find executable named $2 below $pkgdir"
37 nonexepathlist=(`find -H $pkgdir -name $2`)
38 if [[ ${#nonexepathlist[@]} != 0 ]] ; then
39 echo "[rosrun] Found the following, but they're either not files, "
40 echo "[rosrun] or not executable:"
41 for p in ${nonexepathlist[@]}; do
42 echo "[rosrun] ${p}"
43 done
44 fi
45 exit 3
46
47 elif [[ ${#exepathlist[@]} -gt 1 ]] ; then
48 echo "[rosrun] You have chosen a non-unique executable, please pick one of the following:"
49 select opt in ${exepathlist[@]}; do
50 exepath=$opt
51 break
52 done
53 else
54 exepath=${exepathlist[0]}
55 fi
56 else
57 absname=$pkgdir/$2
58 if [ ! -f $absname -o ! -x $absname ] ; then
59 echo "[rosrun] Couldn't find executable named $absname"
60 exit 3
61 fi
62 exepath=$absname
63 fi
64 shift
65 shift
66 exec $exepath "$@"
0 <launch>
1 <!-- file exists just for completion purposes -->
2 </launch>
0 #! /usr/bin/env bash
1
2 # This script is supposed to be a unit test for rosbash, also used for manual checks against other operating systems.
3
4 # TODO extend this script
5
6
7 . ../rosbash
8
9 echo Testing BASH
10
11 . test_zshbash.sh
12
13 # roslaunch completion
14
15 export COMP_CWORD=1
16 _roscomplete_launch roslaunch
17 if [[ ! ${COMPREPLY[@]} =~ rosbash ]]; then
18 echo "rosbash package missing from" ${COMPREPLY[@]} ; exit 1
19 fi
20 echo success roslaunch pkg
21
22 export COMP_WORDS=("roslaunch" "--")
23 export COMP_CWORD=1
24 _roscomplete_launch roslaunch "--" roslaunch
25 if [[ ! ${COMPREPLY[@]} == "--files --args --nodes --find-node --child --local --screen --server_uri --run_id --wait --port --core --pid --dump-params" ]]; then
26 echo "roslaunch --options missing" from ${COMPREPLY[@]} ; exit 1
27 fi
28 echo success roslaunch --option
29
30 export COMP_WORDS=("roslaunch" "roslaunch")
31 export COMP_CWORD=2
32 _roscomplete_launch roslaunch roslaunch
33 if [[ ! ${COMPREPLY[@]} =~ "example.launch" ]]; then
34 echo "example.launch missing from " ${COMPREPLY[@]} ; exit 1
35 fi
36 echo success roslaunch launchfiles
37
38
39 # if [[ ! ${COMPREPLY[@]} =~ "example.launch" ]]; then
40 # echo "example.launch missing from " ${COMPREPLY[@]} ; exit 1
41 # fi
42 # echo success 4
0 #! /usr/bin/env zsh
1
2 # This script is supposed to be a unit test for rosbash, also used for manual checks against other operating systems.
3
4 # TODO extend this script
5
6
7 . ../roszsh
8
9 echo "Testing ZSH"
10
11 . ./test_zshbash.sh
12
13 # roslaunch completion
14
15 BUFFER=("foo roslaunch")
16 _roscomplete_search_dir
17 if [[ ! ${reply[@]} =~ "example.launch" ]]; then
18 echo "rosbash package missing from" ${reply[@]} ; exit 1
19 fi
20 echo success roslaunch launchfiles
0 #!/usr/bin/env python
1
2 import os
3 import subprocess
4 import unittest
5 import tempfile
6 import shutil
7
8 PKG_PATH = os.getcwd()
9 TEST_PATH = os.path.join(PKG_PATH, 'test')
10
11 def make_bash_pre_command(strings, currentword):
12 return "bash -c '. %s; export COMP_WORDS=(%s); export COMP_CWORD=%s;"%(os.path.join(PKG_PATH, 'rosbash'), ' '.join(['"%s"'%w for w in strings]), currentword)
13
14 class TestRosBash(unittest.TestCase):
15
16 def setUp(self):
17 self.cmdbash = os.path.join(TEST_PATH, 'test_rosbash.bash')
18 self.assertTrue(os.path.exists(self.cmdbash))
19
20 self.cmdzsh = os.path.join(TEST_PATH, 'test_roszsh.zsh')
21 self.assertTrue(os.path.exists(self.cmdzsh))
22
23 def test_rosbash_completion(self):
24 subprocess.check_call([self.cmdbash], cwd = TEST_PATH)
25
26 def test_roszsh_completion(self):
27 subprocess.check_call([self.cmdzsh], cwd = TEST_PATH)
28
29
30 class TestWithFiles(unittest.TestCase):
31
32 @classmethod
33 def setUpClass(self):
34 self.test_root_path = tempfile.mkdtemp()
35
36 @classmethod
37 def tearDownClass(self):
38 shutil.rmtree(self.test_root_path)
39
40 def test_make_precommand(self):
41 self.assertEqual( "bash -c '. %s; export COMP_WORDS=(\"foo\" \"bar\"); export COMP_CWORD=1;"%os.path.join(PKG_PATH, 'rosbash'), make_bash_pre_command(['foo', 'bar'], 1))
42 self.assertEqual( "bash -c '. %s; export COMP_WORDS=(\"foo\"); export COMP_CWORD=2;"%os.path.join(PKG_PATH, 'rosbash'), make_bash_pre_command(['foo'], 2))
43
44 def test_roslaunch_completion(self):
45 # regression test that roslaunch completion works even in the presence of launchfiles
46 subprocess.check_call("touch foo.launch", shell=True, cwd=self.test_root_path)
47 subprocess.check_call("touch bar.launch", shell=True, cwd=self.test_root_path)
48
49 cmd = make_bash_pre_command(['rosbash', 'rosbash'], 2)
50 cmd += "_roscomplete_launch rosbash rosbash; echo $COMPREPLY'"
51 p = subprocess.Popen(cmd,
52 shell=True,
53 stdout=subprocess.PIPE,
54 cwd=self.test_root_path)
55 output = p.communicate()
56 self.assertEqual(0, p.returncode, (p.returncode, output, cmd))
57
58 self.assertTrue('example.launch' in output[0], (p.returncode, output[0], cmd))
0 # tests that can work against both bash and zsh
1
2
3
4 # rossed (extended regular expressions)
5 if [[ ! `echo abcde | _rossed 's,abc?,foo,'` = "foode" ]]; then
6 echo "rosbash package missing from" ${reply[@]} ; exit 1
7 fi
8
9 echo success rossed
10
11
12 ROS_LOCATIONS=foohome=/work:share=/share
13 if [[ ! `_ros_location_find foohome` = "/work" ]]; then
14 echo "ros_location_find missed foohome"; exit 1
15 fi
16 if [[ ! `_ros_location_find share` = "/share" ]]; then
17 echo "ros_location_find missed share"; exit 1
18 fi
19
20 echo success ros_location_find
0 cmake_minimum_required(VERSION 2.8)
1 project(rosboost_cfg)
2 find_package(catkin REQUIRED)
3 catkin_package()
4 catkin_package_export()
5
6 catkin_python_setup()
0 <package>
1 <description brief="Contains scripts for determining the boost configuration on a system">
2
3 Contains scripts used by the rosboost-cfg tool for determining cflags/lflags/etc. of boost on your system
4
5 </description>
6 <author>Josh Faust</author>
7 <license>BSD</license>
8 <review status="unreviewed" notes=""/>
9 <url>http://ros.org/wiki/rosboost_cfg</url>
10 </package>
11
12
0 <package>
1 <name>rosboost_cfg</name>
2 <version>1.9.11</version>
3 <description>
4
5 Contains scripts used by the rosboost-cfg tool for determining cflags/lflags/etc. of boost on your system
6
7 </description>
8 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
9 <license>BSD</license>
10
11 <url>http://ros.org/wiki/rosboost_cfg</url>
12 <author>Josh Faust</author>
13
14 <build_depend>catkin</build_depend>
15 </package>
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2010, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 import rosboost_cfg
34 rosboost_cfg.main()
0 #!/usr/bin/env python
1
2 from distutils.core import setup
3 from catkin_pkg.package import parse_package_for_distutils
4
5 d = parse_package_for_distutils()
6 d['packages'] = ['rosboost_cfg']
7 d['package_dir'] = {'': 'src'}
8 d['scripts'] = ['scripts/rosboost-cfg']
9 d['install_requires'] = ['rospkg']
10
11 setup(**d)
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2010, 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 from .rosboost_cfg import *
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2010, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 from __future__ import print_function
34
35 import sys
36 import os
37 import string
38 from glob import glob
39 import subprocess
40 import platform
41 from optparse import OptionParser
42
43 lib_suffix = "so"
44 if (sys.platform == "darwin"):
45 lib_suffix = "dylib"
46
47 link_static = 'ROS_BOOST_LINK' in os.environ and os.environ['ROS_BOOST_LINK'] == "static"
48 if (link_static):
49 lib_suffix = "a"
50
51 no_L_or_I = 'ROS_BOOST_NO_L_OR_I' in os.environ
52
53 boost_version = None
54 if ('ROS_BOOST_VERSION' in os.environ and len(os.environ['ROS_BOOST_VERSION']) > 0):
55 ver = os.environ['ROS_BOOST_VERSION']
56 ver = ver.split('.')
57
58 boost_version = [int(v) for v in ver]
59 if (len(boost_version) == 2):
60 boost_version.append(0)
61
62 def print_usage_and_exit():
63 print("Usage: rosboost-cfg --lflags [thread,regex,graph,...]")
64 print(" rosboost-cfg --cflags")
65 print(" rosboost-cfg --libs [thread,regex,graph,...]")
66 print(" rosboost-cfg --include_dirs")
67 print(" rosboost-cfg --lib_dirs")
68 print(" rosboost-cfg --root")
69 sys.exit(1)
70
71 class BoostError(Exception):
72 def __init__(self, value):
73 self.value = value
74 def __str__(self):
75 return repr(self.value)
76
77 class Version(object):
78 def __init__(self, major, minor, patch, root, include_dir, lib_dir, is_default_search_location):
79 self.major = major
80 self.minor = minor
81 self.patch = patch
82 self.root = root
83 self.include_dir = include_dir
84 self.lib_dir = lib_dir
85 self.is_default_search_location = is_default_search_location
86 self.is_system_install = os.path.split(self.include_dir)[0] == self.root
87
88 def __cmp__(self, other):
89 if (self.major != other.major):
90 if self.major < other.major:
91 return -1
92 else:
93 return 1
94 if (self.minor != other.minor):
95 if self.minor < other.minor:
96 return -1
97 else:
98 return 1
99 if (self.patch != other.patch):
100 if self.patch < other.patch:
101 return -1
102 else:
103 return 1
104
105 return 0
106 def __repr__(self):
107 return repr((self.major, self.minor, self.patch, self.root, self.include_dir, self.is_default_search_location, self.is_system_install))
108
109 def find_lib_dir(root_dir):
110 # prefer lib64 unless explicitly specified in the environment
111 if ('ROS_BOOST_LIB_DIR_NAME' in os.environ):
112 possible_dirs = [os.path.join(root_dir, os.environ['ROS_BOOST_LIB_DIR_NAME'])]
113 else:
114 possible_dirs = [os.path.join(root_dir, "lib64"), os.path.join(root_dir, "lib")]
115
116 for p in possible_dirs:
117 glob_files = glob("%s*"%(os.path.join(p, "libboost*")))
118 if (len(glob_files) > 0):
119 return p
120
121 return None
122
123 def extract_versions(dir, is_default_search_location):
124 version_paths = [os.path.join(dir, "version.hpp"),
125 os.path.join(dir, "boost", "version.hpp")]
126 glob_dirs = glob("%s*"%(os.path.join(dir, "boost-")))
127 [version_paths.append(os.path.join(gdir, "boost", "version.hpp")) for gdir in glob_dirs]
128
129 versions = []
130
131 for p in version_paths:
132 ver_string = ""
133 if (os.path.isfile(p)):
134 fh = open(p,"r")
135 lines = fh.readlines()
136 fh.close()
137 for line in lines:
138 if line.find("#define BOOST_VERSION ") > -1:
139 def_string = line.split()
140 ver_string = def_string[2]
141 ver_int = int(ver_string)
142 patch = ver_int % 100
143 minor = ver_int / 100 % 1000
144 major = ver_int / 100000
145 include_dir = os.path.split(os.path.split(p)[0])[0]
146 root_dir = os.path.split(dir)[0]
147 lib_dir = find_lib_dir(root_dir)
148 versions.append(Version(major, minor, patch, root_dir, include_dir, lib_dir, is_default_search_location))
149
150 return versions
151
152 def find_versions(search_paths):
153 vers = []
154
155 for path, system in search_paths:
156 path = os.path.join(path, "include")
157 pvers = extract_versions(path, system)
158 [vers.append(ver) for ver in pvers]
159
160 if (len(vers) == 0):
161 return None
162
163 if (boost_version is not None):
164 for v in vers:
165 if (v.major == boost_version[0] and v.minor == boost_version[1] and v.patch == boost_version[2]):
166 return [v]
167
168 raise BoostError('Could not find boost version %s required by ROS_BOOST_VERSION environment variable'%(boost_version))
169
170 vers.sort()
171 return vers
172
173 def find_boost(search_paths):
174 result = find_versions(search_paths)
175 if result is None:
176 return None
177 if len(result) > 1:
178 sys.stderr.write("WARN, found multiple boost versions '%s', using latest"%result)
179 return result[-1]
180
181 def search_paths(sysroot):
182 _search_paths = [(sysroot+'/usr', True),
183 (sysroot+'/usr/local', True),
184 (None if 'INCLUDE_DIRS' not in os.environ else os.environ['INCLUDE_DIRS'], True),
185 (None if 'CPATH' not in os.environ else os.environ['CPATH'], True),
186 (None if 'C_INCLUDE_PATH' not in os.environ else os.environ['C_INCLUDE_PATH'], True),
187 (None if 'CPLUS_INCLUDE_PATH' not in os.environ else os.environ['CPLUS_INCLUDE_PATH'], True),
188 (None if 'ROS_BOOST_ROOT' not in os.environ else os.environ['ROS_BOOST_ROOT'], False)]
189
190 search_paths = []
191 for (str, system) in _search_paths:
192 if (str is not None):
193 dirs = str.split(':')
194 for dir in dirs:
195 if (len(dir) > 0):
196 if (dir.endswith('/include')):
197 dir = dir[:-len('/include')]
198 search_paths.append((dir, system))
199 return search_paths
200
201 def lib_dir(ver):
202 return ver.lib_dir
203
204 def find_lib(ver, name, full_lib = link_static):
205 global lib_suffix
206 global link_static
207
208 dynamic_search_paths = []
209 static_search_paths = []
210
211 if (ver.is_system_install):
212 dynamic_search_paths = ["libboost_%s-mt.%s"%(name, lib_suffix),
213 "libboost_%s.%s"%(name, lib_suffix)]
214 static_search_paths = ["libboost_%s-mt.a"%(name),
215 "libboost_%s.a"%(name)]
216 else:
217 dynamic_search_paths = ["libboost_%s*%s_%s*.%s"%(name, ver.major, ver.minor, lib_suffix),
218 "libboost_%s-mt*.%s"%(name, lib_suffix),
219 "libboost_%s*.%s"%(name, lib_suffix)]
220 static_search_paths = ["libboost_%s*%s_%s*.a"%(name, ver.major, ver.minor),
221 "libboost_%s-mt*.a"%(name),
222 "libboost_%s*.a"%(name)]
223
224 # Boost.Python needs some special handling on some systems (Karmic), since it may have per-python-version libs
225 if (name == "python"):
226 python_ver = platform.python_version().split('.')
227 dynamic_search_paths = ["libboost_%s-mt-py%s%s.%s"%(name, python_ver[0], python_ver[1], lib_suffix),
228 "libboost_%s-py%s%s.%s"%(name, python_ver[0], python_ver[1], lib_suffix)] + dynamic_search_paths
229 static_search_paths = ["libboost_%s-mt-py%s%s.a"%(name, python_ver[0], python_ver[1]),
230 "libboost_%s-py%s%s.a"%(name, python_ver[0], python_ver[1])] + static_search_paths
231
232 search_paths = static_search_paths if link_static else dynamic_search_paths
233
234 dir = lib_dir(ver)
235
236 if dir is None:
237 raise BoostError('Could not locate library [%s], version %s'%(name, ver))
238
239 for p in search_paths:
240 globstr = os.path.join(dir, p)
241 libs = glob(globstr)
242 if (len(libs) > 0):
243 if (full_lib):
244 return libs[0]
245 else:
246 return os.path.basename(libs[0])
247
248 raise BoostError('Could not locate library [%s], version %s in lib directory [%s]'%(name, ver, dir))
249
250 def include_dirs(ver, prefix = ''):
251 if ver.is_system_install or no_L_or_I:
252 return ""
253
254 return " %s%s"%(prefix, ver.include_dir)
255
256 def cflags(ver):
257 return include_dirs(ver, '-I')
258
259 def lib_dir_flags(ver):
260 if not ver.is_default_search_location:
261 dir = lib_dir(ver)
262 return ' -L%s -Wl,-rpath,%s'%(dir, dir)
263
264 return ''
265
266 def lib_flags(ver, name):
267 lib = find_lib(ver, name)
268 if (link_static):
269 return ' %s'%(lib)
270 else:
271 # Cut off "lib" and extension (.so/.a/.dylib/etc.)
272 return ' -l%s'%(os.path.splitext(lib)[0][len('lib'):])
273
274 def lflags(ver, libs):
275 s= lib_dir_flags(ver) + " "
276 for lib in libs:
277 s += lib_flags(ver, lib) + " "
278 return s
279
280 def libs(ver, libs):
281 s = ""
282 for lib in libs:
283 s += find_lib(ver, lib, True) + " "
284 return s
285
286 def lib_dirs(ver):
287 if (ver.is_default_search_location or no_L_or_I):
288 return ""
289
290 return lib_dir(ver)
291
292 OPTIONS = ['libs', 'include_dirs', 'lib_dirs', 'cflags', 'lflags', 'root', 'print_versions', 'version']
293
294 def check_one_option(options, key):
295 for k in dir(options):
296 if (k in OPTIONS):
297 v = getattr(options, k)
298 if (k != key and v):
299 raise BoostError("Only one option (excepting sysroot) is allowed at a time")
300
301 def main():
302 if (len(sys.argv) < 2):
303 print_usage_and_exit()
304
305 parser = OptionParser()
306 parser.add_option("-l", "--libs", dest="libs", type="string", help="")
307 parser.add_option("-i", "--include_dirs", dest="include_dirs", action="store_true", default=False, help="")
308 parser.add_option("-d", "--lib_dirs", dest="lib_dirs", action="store_true", help="")
309 parser.add_option("-c", "--cflags", dest="cflags", action="store_true", default=False, help="")
310 parser.add_option("-f", "--lflags", dest="lflags", type="string", help="")
311 parser.add_option("-r", "--root", dest="root", action="store_true", default=False, help="")
312 parser.add_option("-p", "--print_versions", dest="print_versions", action="store_true", default=False, help="")
313 parser.add_option("-v", "--version", dest="version", action="store_true", default=False, help="")
314 parser.add_option("-s", "--sysroot", dest="sysroot", type="string", default='', help="Location of the system root (usually toolchain root).")
315
316 (options, args) = parser.parse_args()
317
318 if (options.print_versions):
319 check_one_option(options, 'print_versions')
320 for ver in find_versions(search_paths(options.sysroot)):
321 print('%s.%s.%s root=%s include_dir=%s'%(ver.major, ver.minor, ver.patch, ver.root, ver.include_dir))
322 return
323
324 ver = find_boost(search_paths(options.sysroot))
325
326 if ver is None:
327 raise BoostError("Cannot find boost in any of %s"%search_paths(options.sysroot))
328 sys.exit(0)
329
330 if options.version:
331 check_one_option(options, 'version')
332 print('%s.%s.%s root=%s include_dir=%s'%(ver.major, ver.minor, ver.patch, ver.root, ver.include_dir))
333 return
334
335 if ver.major < 1 or (ver.major == 1 and ver.minor < 37):
336 raise BoostError('Boost version %s.%s.%s does not meet the minimum requirements of boost 1.37.0'%(ver.major, ver.minor, ver.patch))
337
338
339
340 output = ""
341 if (options.root):
342 check_one_option(options, 'root')
343 output = ver.root
344 elif (options.libs):
345 check_one_option(options, 'libs')
346 output = libs(ver, options.libs.split(','))
347 elif (options.include_dirs):
348 check_one_option(options, 'include_dirs')
349 output = include_dirs(ver)
350 elif (options.lib_dirs):
351 check_one_option(options, 'lib_dirs')
352 output = lib_dirs(ver)
353 elif (options.cflags):
354 check_one_option(options, 'cflags')
355 output = cflags(ver)
356 elif (options.lflags):
357 check_one_option(options, 'lflags')
358 output = lflags(ver, options.lflags.split(','))
359 else:
360 print_usage_and_exit()
361
362 print(output.strip())
363
364 if __name__ == "__main__":
365 main()
366
0 cmake_minimum_required(VERSION 2.8)
1 project(rosclean)
2 find_package(catkin REQUIRED)
3 catkin_package()
4 catkin_package_export()
5
6 catkin_python_setup()
7
8 catkin_add_nosetests(test)
0 <package>
1 <description brief="rosclean">
2
3 rosclean: cleanup filesystem resources (e.g. log files).
4
5 </description>
6 <author>Ken Conley</author>
7 <license>BSD</license>
8 <review status="unreviewed" notes=""/>
9 <url>http://ros.org/wiki/rosclean</url>
10
11 </package>
12
13
0 <package>
1 <name>rosclean</name>
2 <version>1.9.11</version>
3 <description brief="rosclean">
4
5 rosclean: cleanup filesystem resources (e.g. log files).
6
7 </description>
8 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
9 <license>BSD</license>
10
11 <url>http://ros.org/wiki/rosclean</url>
12 <author>Ken Conley</author>
13
14 <build_depend>catkin</build_depend>
15 </package>
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2010, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 import rosclean
34 rosclean.rosclean_main()
0 #!/usr/bin/env python
1
2 from distutils.core import setup
3 from catkin_pkg.package import parse_package_for_distutils
4
5 d = parse_package_for_distutils()
6 d['packages'] = ['rosclean']
7 d['package_dir'] = {'': 'src'}
8 d['scripts'] = ['scripts/rosclean']
9 d['install_requires'] = ['rospkg']
10
11 setup(**d)
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2010, 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 from __future__ import print_function
33
34 __version__ = '1.7.0'
35
36 import os
37 import sys
38 import platform
39 import subprocess
40
41 import rospkg
42
43 class CleanupException(Exception): pass
44
45 def _ask_and_call(cmds, cwd=None):
46 """
47 Pretty print cmds, ask if they should be run, and if so, runs
48 them using subprocess.check_call.
49
50 :param cwd: (optional) set cwd of command that is executed, ``str``
51 :returns: ``True`` if cmds were run.
52 """
53 # Pretty-print a string version of the commands
54 def quote(s):
55 return '"%s"'%s if ' ' in s else s
56 sys.stdout.write("Okay to execute:\n\n%s\n(y/n)?\n"%('\n'.join([' '.join([quote(s) for s in c]) for c in cmds])))
57 while 1:
58 input = sys.stdin.readline().strip().lower()
59 if input in ['y', 'n']:
60 break
61 accepted = input == 'y'
62 if accepted:
63 for c in cmds:
64 if cwd:
65 subprocess.check_call(c, cwd=cwd)
66 else:
67 subprocess.check_call(c)
68 return accepted
69
70 def _usage():
71 print("""Usage: rosclean <command>
72
73 Commands:
74 \trosclean check\tCheck usage of log files
75 \trosclean purge\tRemove log files
76 """)
77 sys.exit(getattr(os, 'EX_USAGE', 1))
78
79 def _get_check_dirs():
80 home_dir = rospkg.get_ros_home()
81 log_dir = rospkg.get_log_dir()
82 dirs = [ (log_dir, 'ROS node logs'),
83 (os.path.join(home_dir, 'rosmake'), 'rosmake logs')]
84 return [x for x in dirs if os.path.isdir(x[0])]
85
86 def _rosclean_cmd_check(argv):
87 dirs = _get_check_dirs()
88 for d, label in dirs:
89 desc = get_human_readable_disk_usage(d)
90 print("%s %s"%(desc, label))
91
92 def get_human_readable_disk_usage(d):
93 """
94 Get human-readable disk usage for directory
95
96 :param d: directory path, ``str`
97 :returns: human-readable disk usage (du -h), ``str``
98 """
99 # only implemented on Linux and FreeBSD for now. Should work on OS X but need to verify first (du is not identical)
100 if platform.system() in ['Linux', 'FreeBSD']:
101 try:
102 return subprocess.Popen(['du', '-sh', d], stdout=subprocess.PIPE).communicate()[0].split()[0]
103 except:
104 raise CleanupException("rosclean is not supported on this platform")
105 else:
106 raise CleanupException("rosclean is not supported on this platform")
107
108 def get_disk_usage(d):
109 """
110 Get disk usage in bytes for directory
111 :param d: directory path, ``str``
112 :returns: disk usage in bytes (du -b) or (du -A) * 1024, ``int``
113 :raises: :exc:`CleanupException` If get_disk_usage() cannot be used on this platform
114 """
115 # only implemented on Linux and FreeBSD for now. Should work on OS X but need to verify first (du is not identical)
116 if platform.system() == 'Linux':
117 try:
118 return int(subprocess.Popen(['du', '-sb', d], stdout=subprocess.PIPE).communicate()[0].split()[0])
119 except:
120 raise CleanupException("rosclean is not supported on this platform")
121 elif platform.system() == 'FreeBSD':
122 try:
123 return int(subprocess.Popen(['du', '-sA', d], stdout=subprocess.PIPE).communicate()[0].split()[0]) * 1024
124 except:
125 raise CleanupException("rosclean is not supported on this platform")
126 else:
127 raise CleanupException("rosclean is not supported on this platform")
128
129 def _rosclean_cmd_purge(argv):
130 dirs = _get_check_dirs()
131
132 for d, label in dirs:
133 print("Purging %s.\nPLEASE BE CAREFUL TO VERIFY THE COMMAND BELOW!"%label)
134 cmds = [['rm', '-rf', d]]
135 try:
136 _ask_and_call(cmds)
137 except:
138 print("FAILED to execute command", file=sys.stderr)
139
140 def rosclean_main(argv=None):
141 if argv == None:
142 argv = sys.argv
143 if len(argv) < 2:
144 _usage()
145 command = argv[1]
146 if command == 'check':
147 _rosclean_cmd_check(argv)
148 elif command == 'purge':
149 _rosclean_cmd_purge(argv)
150 else:
151 _usage()
152
153 if __name__ == '__main__':
154 rosclean_main()
0 [nosetests]
1 with-coverage=1
2 cover-package=rosclean
3 with-xunit=1
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2012, 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 import os
33 import sys
34
35 def test__get_check_dirs():
36 # just a tripwire, no way to assert the actual values w/o identical reimplementation
37 from rosclean import _get_check_dirs
38 vals = _get_check_dirs()
39 for path, desc in vals:
40 assert os.path.isdir(path)
41
42 def test_get_human_readable_disk_usage():
43 from rosclean import get_human_readable_disk_usage
44 val = get_human_readable_disk_usage(get_test_path())
45 assert val
46
47 def get_test_path():
48 return os.path.abspath(os.path.join(os.path.dirname(__file__)))
49
50 def test_get_disk_usage():
51 from rosclean import get_disk_usage
52 val = get_disk_usage(get_test_path())
53 assert val > 0
54
55 def test_cmd():
56 from rosclean import rosclean_main
57 try:
58 rosclean_main(['rosclean', 'fake'])
59 assert False, "should have raised sys exit"
60 except SystemExit:
61 pass
62
63 # should run cleanly
64 try:
65 rosclean_main(['rosclean', 'check'])
66 except SystemExit:
67 assert False, "failed with sys exit"
0 cmake_minimum_required(VERSION 2.8)
1 project(roscreate)
2 find_package(catkin REQUIRED)
3 catkin_package()
4 catkin_package_export()
5
6 catkin_python_setup()
7
8 catkin_add_nosetests(test)
0 <package>
1 <description brief="roscreate">
2
3 roscreate contains tools that assist in the creation of ROS filesystem resources. There are currently
4 two tools that it provides: <tt>roscreate-pkg</tt> and <tt>roscreate-stack</tt>. <tt>roscreate-pkg</tt> creates a new package directory,
5 including the appropriate build and manifest files. <tt>roscreate-stack</tt> computes a <tt>stack.xml</tt> file from a list of packages
6 in a directory.
7
8 </description>
9 <author>Ken Conley/kwc@willowgarage.com</author>
10 <license>BSD</license>
11 <review status="Doc reviewed" notes="2010/01/07"/>
12 <url>http://ros.org/wiki/roscreate</url>
13 <platform os="ubuntu" version="9.04"/>
14 <platform os="ubuntu" version="9.10"/>
15 <platform os="ubuntu" version="10.04"/>
16 <platform os="macports" version="macports"/>
17 </package>
18
19
20
0 <package>
1 <name>roscreate</name>
2 <version>1.9.11</version>
3 <description>
4 roscreate contains a tool that assists in the creation of ROS filesystem resources.
5 It provides: <tt>roscreate-pkg</tt>, which creates a new package directory,
6 including the appropriate build and manifest files.
7 </description>
8 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
9 <license>BSD</license>
10
11 <url type="website">http://ros.org/wiki/roscreate</url>
12
13 <author email="kwc@willowgarage.com">Ken Conley</author>
14
15 <build_depend>catkin</build_depend>
16 </package>
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 import roscreate
34 roscreate.roscreatepkg.roscreatepkg_main()
0 #!/usr/bin/env python
1
2 from distutils.core import setup
3 from catkin_pkg.package import parse_package_for_distutils
4
5 d = parse_package_for_distutils()
6 d['packages'] = ['roscreate']
7 d['package_dir'] = {'': 'src'}
8 d['scripts'] = ['scripts/roscreate-pkg']
9 d['install_requires'] = ['rospkg']
10
11 setup(**d)
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34
35 import roscreate.core
36 import roscreate.roscreatepkg
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2009, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 from __future__ import print_function
34
35 import os
36 import sys
37
38 import rospkg
39 import pkg_resources
40
41 def print_warning(msg):
42 """print warning to screen (bold red)"""
43 print('\033[31m%s\033[0m'%msg, file=sys.stderr)
44
45 def author_name():
46 """
47 Utility to compute logged in user name
48
49 :returns: name of current user, ``str``
50 """
51 import getpass
52 name = getpass.getuser()
53 try:
54 import pwd
55 login = name
56 name = pwd.getpwnam(login)[4]
57 name = ''.join(name.split(',')) # strip commas
58 # in case pwnam is not set
59 if not name:
60 name = login
61 except:
62 #pwd failed
63 pass
64 if type(name) == str:
65 name = name.decode('utf-8')
66 return name
67
68 def read_template(tmplf):
69 """
70 Read resource template from egg installation, or fallback on rospkg otherwise.
71
72 :returns: text of template file
73 """
74 if pkg_resources.resource_exists('roscreate', tmplf):
75 f = pkg_resources.resource_stream('roscreate', tmplf)
76 t = f.read()
77 else:
78 # fallback on rospkg
79 r = rospkg.RosPack()
80 with open(os.path.join(r.get_path('roscreate'), 'templates', tmplf)) as f:
81 t = f.read()
82 if type(t) == str:
83 t = t.decode('utf-8')
84 return t
85
86
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 # Revision $Id$
33
34 from __future__ import print_function
35
36 NAME='roscreate-pkg'
37
38 import os
39 import sys
40
41 import roslib.names
42
43 from roscreate.core import read_template, author_name
44 from rospkg import on_ros_path, RosPack, ResourceNotFound
45
46 def get_templates():
47 templates = {}
48 templates['CMakeLists.txt'] = read_template('CMakeLists.tmpl')
49 templates['manifest.xml'] = read_template('manifest.tmpl')
50 templates['mainpage.dox'] = read_template('mainpage.tmpl')
51 templates['Makefile'] = read_template('Makefile.tmpl')
52 return templates
53
54 def instantiate_template(template, package, brief, description, author, depends):
55 return template%locals()
56
57 def create_package(package, author, depends, uses_roscpp=False, uses_rospy=False):
58 p = os.path.abspath(package)
59 if os.path.exists(p):
60 print("%s already exists, aborting"%p, file=sys.stderr)
61 sys.exit(1)
62
63 os.makedirs(p)
64 print("Created package directory", p)
65
66 if uses_roscpp:
67 # create package/include/package and package/src for roscpp code
68 cpp_path = os.path.join(p, 'include', package)
69 try:
70 os.makedirs(cpp_path)
71 print("Created include directory", cpp_path)
72 cpp_path = os.path.join(p, 'src')
73 os.makedirs(cpp_path)
74 print("Created cpp source directory", cpp_path)
75 except:
76 # file exists
77 pass
78 if uses_rospy:
79 # create package/src/ for python files
80 py_path = os.path.join(p, 'src')
81 try:
82 os.makedirs(py_path)
83 print("Created python source directory", py_path)
84 except:
85 # file exists
86 pass
87
88 templates = get_templates()
89 for filename, template in templates.iteritems():
90 contents = instantiate_template(template, package, package, package, author, depends)
91 p = os.path.abspath(os.path.join(package, filename))
92 with open(p, 'w') as f:
93 f.write(contents.encode('utf-8'))
94 print("Created package file", p)
95 print("\nPlease edit %s/manifest.xml and mainpage.dox to finish creating your package"%package)
96
97 def roscreatepkg_main():
98 from optparse import OptionParser
99 parser = OptionParser(usage="usage: %prog <package-name> [dependencies...]", prog=NAME)
100 options, args = parser.parse_args()
101 if not args:
102 parser.error("you must specify a package name and optionally also list package dependencies")
103 package = args[0]
104
105 if not roslib.names.is_legal_resource_base_name(package):
106 parser.error("illegal package name: %s\nNames must start with a letter and contain only alphanumeric characters\nand underscores."%package)
107
108 # validate dependencies and turn into XML
109 depends = args[1:]
110 uses_roscpp = 'roscpp' in depends
111 uses_rospy = 'rospy' in depends
112
113 rospack = RosPack()
114 for d in depends:
115 try:
116 rospack.get_path(d)
117 except ResourceNotFound:
118 print("ERROR: dependency [%s] cannot be found"%d, file=sys.stderr)
119 sys.exit(1)
120
121 depends = u''.join([u' <depend package="%s"/>\n'%d for d in depends])
122
123 if not on_ros_path(os.getcwd()):
124 print('!'*80+"\nWARNING: current working directory is not on ROS_PACKAGE_PATH!\nPlease update your ROS_PACKAGE_PATH environment variable.\n"+'!'*80, file=sys.stderr)
125 if type(package) == str:
126 package = package.decode('utf-8')
127 create_package(package, author_name(), depends, uses_roscpp=uses_roscpp, uses_rospy=uses_rospy)
0 cmake_minimum_required(VERSION 2.4.6)
1 include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
2
3 # Append to CPACK_SOURCE_IGNORE_FILES a semicolon-separated list of
4 # directories (or patterns, but directories should suffice) that should
5 # be excluded from the distro. This is not the place to put things that
6 # should be ignored everywhere, like "build" directories; that happens in
7 # rosbuild/rosbuild.cmake. Here should be listed packages that aren't
8 # ready for inclusion in a distro.
9 #
10 # This list is combined with the list in rosbuild/rosbuild.cmake. Note
11 # that CMake 2.6 may be required to ensure that the two lists are combined
12 # properly. CMake 2.4 seems to have unpredictable scoping rules for such
13 # variables.
14 #list(APPEND CPACK_SOURCE_IGNORE_FILES /core/experimental)
15
16 rosbuild_make_distribution(0.1.0)
0 cmake_minimum_required(VERSION 2.4.6)
1 include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
2
3 # Set the build type. Options are:
4 # Coverage : w/ debug symbols, w/o optimization, w/ code-coverage
5 # Debug : w/ debug symbols, w/o optimization
6 # Release : w/o debug symbols, w/ optimization
7 # RelWithDebInfo : w/ debug symbols, w/ optimization
8 # MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
9 #set(ROS_BUILD_TYPE RelWithDebInfo)
10
11 rosbuild_init()
12
13 #set the default path for built executables to the "bin" directory
14 set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
15 #set the default path for built libraries to the "lib" directory
16 set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
17
18 #uncomment if you have defined messages
19 #rosbuild_genmsg()
20 #uncomment if you have defined services
21 #rosbuild_gensrv()
22
23 #common commands for building c++ executables and libraries
24 #rosbuild_add_library(${PROJECT_NAME} src/example.cpp)
25 #target_link_libraries(${PROJECT_NAME} another_library)
26 #rosbuild_add_boost_directories()
27 #rosbuild_link_boost(${PROJECT_NAME} thread)
28 #rosbuild_add_executable(example examples/example.cpp)
29 #target_link_libraries(example ${PROJECT_NAME})
0 include $(shell rospack find mk)/cmake_stack.mk
0 include $(shell rospack find mk)/cmake.mk
0 /**
1 \mainpage
2 \htmlinclude manifest.html
3
4 \b %(package)s
5
6 <!--
7 Provide an overview of your package.
8 -->
9
10 -->
11
12
13 */
0 <package>
1 <description brief="%(brief)s">
2
3 %(description)s
4
5 </description>
6 <author>%(author)s</author>
7 <license>BSD</license>
8 <review status="unreviewed" notes=""/>
9 <url>http://ros.org/wiki/%(package)s</url>
10 %(depends)s
11 </package>
12
13
0 <stack>
1 <description brief="%(brief)s">%(description)s</description>
2 <author>%(author)s</author>
3 <license>%(licenses)s</license>
4 %(review)s
5 <url>http://ros.org/wiki/%(stack)s</url>
6 %(depends)s
7 </stack>
0 <package>
1 <description brief="depends_roslib">
2
3 depends_roslib
4
5 </description>
6 <author>Kenneth Conley</author>
7 <license>BSD</license>
8 <depend package="roslib"/>
9
10 </package>
11
12
0 <stack>
1 <description brief="stack1">stack1</description>
2 <author>Maintained by Kenneth Conley</author>
3 <license>BSD</license>
4 <depend stack="ros" /> <!-- roslib -->
5 </stack>
0 cmake_minimum_required(VERSION 2.4.6)
1 include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
2
3 # Set the build type. Options are:
4 # Coverage : w/ debug symbols, w/o optimization, w/ code-coverage
5 # Debug : w/ debug symbols, w/o optimization
6 # Release : w/o debug symbols, w/ optimization
7 # RelWithDebInfo : w/ debug symbols, w/ optimization
8 # MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
9 #set(ROS_BUILD_TYPE RelWithDebInfo)
10
11 rosbuild_init()
12
13 #set the default path for built executables to the "bin" directory
14 set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
15 #set the default path for built libraries to the "lib" directory
16 set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
17
18 #uncomment if you have defined messages
19 #rosbuild_genmsg()
20 #uncomment if you have defined services
21 #rosbuild_gensrv()
22
23 #common commands for building c++ executables and libraries
24 #rosbuild_add_library(${PROJECT_NAME} src/example.cpp)
25 #target_link_libraries(${PROJECT_NAME} another_library)
26 #rosbuild_add_boost_directories()
27 #rosbuild_link_boost(${PROJECT_NAME} thread)
28 #rosbuild_add_executable(example examples/example.cpp)
29 #target_link_libraries(example ${PROJECT_NAME})
0 include $(shell rospack find mk)/cmake.mk
0 /**
1 \mainpage
2 \htmlinclude manifest.html
3
4 \b depends_stack1 is ...
5
6 <!--
7 In addition to providing an overview of your package,
8 this is the section where the specification and design/architecture
9 should be detailed. While the original specification may be done on the
10 wiki, it should be transferred here once your package starts to take shape.
11 You can then link to this documentation page from the Wiki.
12 -->
13
14
15 \section codeapi Code API
16
17 <!--
18 Provide links to specific auto-generated API documentation within your
19 package that is of particular interest to a reader. Doxygen will
20 document pretty much every part of your code, so do your best here to
21 point the reader to the actual API.
22
23 If your codebase is fairly large or has different sets of APIs, you
24 should use the doxygen 'group' tag to keep these APIs together. For
25 example, the roscpp documentation has 'libros' group.
26 -->
27
28
29 */
0 <package>
1 <description brief="depends_stack1">
2 depends_stack1
3 </description>
4 <author>Kenneth Conley</author>
5 <license>BSD</license>
6 <depend package="depends_roslib" />
7 </package>
8
9
0 <stack>
1 <description brief="stack2">stack2</description>
2 <author>Maintained by Kenneth Conley</author>
3 <license>BSD</license>
4 <depend stack="stack1" /> <!-- depends_roslib -->
5 </stack>
0 [nosetests]
1 with-coverage=1
2 cover-package=roscreate
3 with-xunit=1
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2012, 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 def test_author_name():
33 from roscreate.core import author_name
34 val = author_name()
35 assert val, val
36
37 def test_read_template():
38 from roscreate.core import read_template
39 s = set()
40 # this unit test will break if any of the templates get removed/renamed
41 tests = ['Makefile.tmpl', 'stack.tmpl', 'mainpage.tmpl', 'CMakeLists.stack.tmpl']
42 for f in tests:
43 text = read_template(f)
44 s.add(text)
45 # simple assert to make sure we didn't read the same thing from each template
46 assert len(s) == len(tests)
47
48 # hardcode test against a known template
49 text = read_template('Makefile.tmpl')
50 assert text == 'include $(shell rospack find mk)/cmake.mk'
0 cmake_minimum_required(VERSION 2.8)
1 project(rosmake)
2 find_package(catkin REQUIRED)
3
4 catkin_package()
5 catkin_package_export()
6
7 catkin_python_setup()
8
9 catkin_add_nosetests(test)
0 <package>
1 <description brief="rosmake">
2
3 rosmake is a ros dependency aware build tool which can be used to
4 build all dependencies in the correct order.
5
6 </description>
7 <author>Tully Foote/tfoote@willowgarage.com</author>
8 <license>BSD</license>
9 <review status="Doc Reviewed" notes="2010-01-12"/>
10 <url>http://ros.org/wiki/rosmake</url>
11
12 <export>
13 <rosdoc config="rosdoc.yaml"/>
14 </export>
15
16 </package>
17
18
0 <package>
1 <name>rosmake</name>
2 <version>1.9.11</version>
3 <description>
4 rosmake is a ros dependency aware build tool which can be used to
5 build all dependencies in the correct order.
6 </description>
7 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
8 <license>BSD</license>
9
10 <url>http://ros.org/wiki/rosmake</url>
11
12 <author email="tfoote@willowgarage.com">Tully Foote</author>
13
14 <build_depend>catkin</build_depend>
15
16 <export>
17 <rosdoc config="rosdoc.yaml"/>
18 </export>
19 </package>
0 - builder: epydoc
0 #! /usr/bin/env python
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 are met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 # * Neither the name of Willow Garage, Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28
29
30 # Author Tully Foote/tfoote@willowgarage.com
31 from __future__ import print_function
32
33 import os
34 import sys
35 import subprocess
36 import threading
37
38 import rospkg
39 import rosmake
40
41 ## make sure that rospack is built, it is a requirement for rosmake
42 def assert_rospack_built():
43 p = subprocess.Popen(["rospack","help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
44 output = p.communicate()
45 if p.returncode != 0:
46 print("rospack not available.\nPlease install rospack before using rosmake and make sure it is available on your path. %s %s"%(output[0], output[1]), file=sys.stderr)
47 sys.exit(1)
48
49 result = 1
50 with rosmake.Printer():
51 rma = rosmake.RosMakeAll()
52
53 try:
54 if rma.main():
55 result = 0
56 except rospkg.ResourceNotFound as e:
57 print("cannot find required resource: %s"%(str(e)))
58
59 #make sure the thread is done
60 rosmake.Printer().join()
61 for t in threading.enumerate():
62 if t != threading.currentThread():
63 # Join all threads before exiting
64 print("Cleaning up thread", t)
65 t.join()
66 sys.exit(result)
0 #!/usr/bin/env python
1
2 from distutils.core import setup
3 from catkin_pkg.package import parse_package_for_distutils
4
5 d = parse_package_for_distutils()
6 d['packages'] = ['rosmake']
7 d['package_dir'] = {'': 'src'}
8 d['scripts'] = ['scripts/rosmake']
9 d['install_requires'] = ['rospkg']
10
11 setup(**d)
0 # Copyright (c) 2009, Willow Garage, Inc.
1 # All rights reserved.
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 #
6 # * Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer.
8 # * Redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution.
11 # * Neither the name of the Willow Garage, Inc. nor the names of its
12 # contributors may be used to endorse or promote products derived from
13 # this software without specific prior written permission.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 # POSSIBILITY OF SUCH DAMAGE.
26
27 """
28 Implements the 'rosmake' command-line tool via the L{RosMakeAll}
29 class. This class is for internal-use only within ROS tools. The API
30 is very likely to change in future releases and is not stable.
31 """
32
33 from .engine import RosMakeAll, Printer
34
35 __version__ = '1.7.0'
0 #! /usr/bin/env python
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 are met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 # * Neither the name of Willow Garage, Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28
29 # Author Tully Foote/tfoote@willowgarage.com
30
31 from __future__ import print_function
32
33 import os
34 import re
35 import signal
36 import sys
37 import subprocess
38 import time
39 import threading
40 import traceback
41
42 import rospkg
43
44 try:
45 from exceptions import SystemExit #Python 2.x
46 except ImportError:
47 pass #Python 3.x (in Python 3, 'exceptions' is always imported)
48
49 from operator import itemgetter
50
51 from . import parallel_build
52 from . import package_stats
53
54 from optparse import OptionParser
55
56 # #3883
57 _popen_lock = threading.Lock()
58
59 def make_command():
60 """
61 @return: name of 'make' command
62 @rtype: str
63 """
64 return os.environ.get("MAKE", "make")
65
66 # this is a copy of the roslogging utility. it's been moved here as it is a common
67 # routine for programs using accessing ROS directories
68 def makedirs_with_parent_perms(p):
69 """
70 Create the directory using the permissions of the nearest
71 (existing) parent directory. This is useful for logging, where a
72 root process sometimes has to log in the user's space.
73 @param p: directory to create
74 @type p: str
75 """
76 p = os.path.abspath(p)
77 parent = os.path.dirname(p)
78 # recurse upwards, checking to make sure we haven't reached the
79 # top
80 if not os.path.exists(p) and p and parent != p:
81 makedirs_with_parent_perms(parent)
82 s = os.stat(parent)
83 os.mkdir(p)
84
85 # if perms of new dir don't match, set anew
86 s2 = os.stat(p)
87 if s.st_uid != s2.st_uid or s.st_gid != s2.st_gid:
88 os.chown(p, s.st_uid, s.st_gid)
89 if s.st_mode != s2.st_mode:
90 os.chmod(p, s.st_mode)
91
92 class Printer:
93 # storage for the instance reference
94 __instance = None
95
96 def __init__(self):
97 """ Create singleton instance """
98 # Check whether we already have an instance
99 if Printer.__instance is None:
100 # Create and remember instance
101 Printer.__instance = Printer.__impl()
102
103 # Store instance reference as the only member in the handle
104 self.__dict__['_Printer__instance'] = Printer.__instance
105
106 def __getattr__(self, attr):
107 """ Delegate access to implementation """
108 return getattr(self.__instance, attr)
109
110 def __setattr__(self, attr, value):
111 """ Delegate access to implementation """
112 return setattr(self.__instance, attr, value)
113
114 def __enter__(self):
115 """Pass through for the __enter__ function for the __instance"""
116 return self.__instance.__enter__()
117
118 def __exit__(self, mtype, value, tb):
119 """Pass through for the __exit__ function for the __instance"""
120 return self.__instance.__exit__(mtype, value, tb)
121
122 class __impl(threading.Thread):
123 def __init__(self):
124 threading.Thread.__init__(self)
125 self.build_queue = None
126 self.condition = threading.Condition()
127 self.running = True
128 self.done = False
129 self.status = ""
130 self.verbose = False
131 self.full_verbose = False
132 self.duration = 1./10.
133 self._last_status = None
134
135 # Rosmake specific data
136 self.cache_argument = None
137 self.cache_right = ''
138 self.pkg_start_times = {}
139
140 def shutdown(self):
141 self.running = False
142 cycles = 10
143 for i in range(0,cycles):# sleep for at least 2 cycles of the status testing 'cycles' times
144 if self.done:
145 #print "SUCCESSFULLY SHUTDOWN"
146 return True
147 #print "Sleeping for %f FOR SHUTDOWN. %d threads running"%(max(self.duration/cycles*2, 0.01), threading.activeCount())
148 time.sleep(max(self.duration, 0.1)/cycles*2)
149 raise Exception("Failed to shutdown status thread in %.2f seconds"%(self.duration * 2))
150
151 def __enter__(self):
152 self.start()
153 def __exit__(self, mtype, value, tb):
154 self.shutdown()
155 if value:
156 if not mtype == type(SystemExit()):
157 traceback.print_exception(mtype, value, tb)
158 else:
159 sys.exit(value)
160
161 def run(self):
162 while self.running:
163 #shutdown if duration set to zero
164 if self.duration <= 0:
165 self.running = False
166 break
167 self.set_status_from_cache()
168 if len(self.pkg_start_times.keys()) > 0:
169 n = self.terminal_width() - len(self.status)
170 status = self.status
171 if n > 0:
172 status = " "*n + self.status
173 if status != self._last_status:
174 self._print_status("%s"%status)
175 self._last_status = status
176 time.sleep(self.duration)
177 self.done = True
178 #print "STATUS THREAD FINISHED"
179
180 def rosmake_cache_info(self, argument, start_times, right):
181 self.cache_argument = argument
182 self.pkg_start_times = start_times
183 self.cache_right = right
184
185 def rosmake_pkg_times_to_string(self, start_times):
186 threads = []
187 for p, t in sorted(start_times.items(), key=itemgetter(1)): #py3k
188 threads.append("[ %s: %.1f sec ]"%(p, time.time() - t))
189
190 return " ".join(threads)
191
192 def set_status_from_cache(self):
193 if self.cache_argument:
194 self.set_status("[ make %s ] "%self.cache_argument + self.rosmake_pkg_times_to_string(self.pkg_start_times), self.cache_right)
195 else:
196 self.set_status("[ make ] " + self.rosmake_pkg_times_to_string(self.pkg_start_times), self.cache_right)
197
198 def set_status(self, left, right = ''):
199 header = "[ rosmake ] "
200 h = len(header)
201 l = len(left)
202 r = len(right)
203 w = self.terminal_width()
204 if l + r < w - h:
205 padding = w - h - l - r
206 self.status = header + left + " "*padding + right
207 else:
208 self.status = header + left[:(w - h - r - 4)] + "... " + right
209
210 def print_all(self, s, thread_name=None):
211 if thread_name is None:
212 str = "[ rosmake ] %s"%s
213
214
215 else:
216 str = "[rosmake-%s] %s"%(thread_name, s)
217 sys.stdout.write(self.pad_str_to_width(str, self.terminal_width())+"\n")
218 sys.stdout.flush()
219
220 def print_verbose(self, s, thread_name=None):
221 if self.verbose or self.full_verbose:
222 self.print_all(s, thread_name=thread_name)
223
224 def print_full_verbose(self, s):
225 if self.full_verbose:
226 print("[ rosmake ] %s"%(s))
227
228 def print_tail(self, s, tail_lines=40):
229 lines = s.splitlines()
230 if self.full_verbose:
231 tail_lines = len(lines)
232
233 num_lines = min(len(lines), tail_lines)
234 if num_lines == tail_lines:
235 print("[ rosmake ] Last %d lines"%(num_lines))
236 else:
237 print("[ rosmake ] All %d lines"%(num_lines))
238 print("{" + "-"*79)
239 for l in range(-num_lines, -1):
240 print(" %s"%(lines[l]))
241 print("-"*79 + "}")
242
243 def _print_status(self, s):
244 sys.stdout.write("%s\r"%(s))
245 sys.stdout.flush()
246
247 @staticmethod
248 def terminal_width():
249 """Estimate the width of the terminal"""
250 width = 0
251 try:
252 import struct, fcntl, termios
253 s = struct.pack('HHHH', 0, 0, 0, 0)
254 x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
255 width = struct.unpack('HHHH', x)[1]
256 except IOError:
257 pass
258 if width <= 0:
259 try:
260 width = int(os.environ['COLUMNS'])
261 except:
262 pass
263 if width <= 0:
264 width = 80
265
266 return width
267
268 @staticmethod
269 def pad_str_to_width(str, width):
270 """ Pad the string to be terminal width"""
271 length = len(str)
272 excess = 0
273 if length < width:
274 excess = width - length
275 return str + " "* excess
276
277
278
279 class RosMakeAll:
280 def __init__(self):
281 self._result_lock = threading.Lock()
282
283 self.rospack = rospkg.RosPack()
284 self.rosstack = rospkg.RosStack()
285
286 self.printer = Printer()
287 self.result = {}
288 self.paths = {}
289 self.dependency_tracker = parallel_build.DependencyTracker(rospack=self.rospack)
290 self.flag_tracker = package_stats.PackageFlagTracker(self.dependency_tracker)
291 self.output = {}
292 self.profile = {}
293 self.ros_parallel_jobs = 0
294 self.build_list = []
295 self.start_time = time.time()
296 self.log_dir = ""
297 self.logging_enabled = True
298
299 def num_packages_built(self):
300 """
301 @return: number of packages that were built
302 @rtype: int
303 """
304 return len(list(self.result[argument].keys())) #py3k
305
306 def update_status(self, argument, start_times, right):
307 self.printer.rosmake_cache_info(argument, start_times, right)
308
309 def build_or_recurse(self,p):
310 if p in self.build_list:
311 return
312 for d in self.dependency_tracker.get_deps_1(p):
313 self.build_or_recurse(d)
314 try: # append it ot the list only if present
315 self.rospack.get_path(p)
316 self.build_list.append(p)
317 except rospkg.ResourceNotFound as ex:
318 if not self.robust_build:
319 self.printer.print_all("Exiting due to missing package: %s"%ex)
320 sys.exit(-1)
321 else:
322 self.printer.print_all("!"*20 + " Package %s does not exist. %s"%(p, ex) + "!"*20)
323
324
325 def parallel_build_pkgs(self, build_queue, argument = None, threads = 1):
326 self.profile[argument] = {}
327 self.output[argument] = {}
328 with self._result_lock:
329 if argument not in self.result.keys():
330 self.result[argument] = {}
331
332 cts = []
333 for i in range(0, threads):
334 ct = parallel_build.CompileThread(str(i), build_queue, self, argument)
335 #print "TTTH starting thread ", ct
336 ct.start()
337 cts.append(ct)
338 for ct in cts:
339 try:
340 #print "TTTT Joining", ct
341 ct.join()
342 #print "TTTH naturally ended thread", ct
343 except KeyboardInterrupt:
344 self.printer.print_all( "TTTH Caught KeyboardInterrupt. Stopping build.")
345 build_queue.stop()
346 ct.join()
347 except: #catch all
348 self.printer.print_all("TTTH OTHER exception thrown!!!!!!!!!!!!!!!!!!!!!")
349 ct.join()
350 #print "All threads joined"
351 all_pkgs_passed = True
352 with self._result_lock:
353 for v in self.result[argument].values():
354 all_pkgs_passed = v and all_pkgs_passed
355
356 build_passed = build_queue.succeeded() and all_pkgs_passed
357 return build_passed
358
359 # This function taken from
360 # http://www.chiark.greenend.org.uk/ucgi/~cjwatson/blosxom/2009-07-02-python-sigpipe.html
361 def _subprocess_setup(self):
362 # Python installs a SIGPIPE handler by default. This is usually not
363 # what non-Python subprocesses expect.
364 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
365
366 def _build_package(self, package, argument=None):
367 """
368 Lower-level routine for building a package. Handles execution of actual build command.
369 @param package: package name
370 @type package: str
371 """
372 local_env = os.environ.copy()
373 if self.ros_parallel_jobs > 0:
374 local_env['ROS_PARALLEL_JOBS'] = "-j -l%d" % self.ros_parallel_jobs
375 elif "ROS_PARALLEL_JOBS" not in os.environ: #if no environment setup and no args fall back to # cpus
376 # num_cpus check can (on OS X) trigger a Popen(), which has
377 #the multithreading bug we wish to avoid on Py2.7.
378 with _popen_lock:
379 local_env['ROS_PARALLEL_JOBS'] = "-j -l%d" % parallel_build.num_cpus()
380 local_env['SVN_CMDLINE'] = "svn --non-interactive"
381 cmd = ["bash", "-c", "cd %s && %s "%(self.rospack.get_path(package), make_command()) ] #UNIXONLY
382 if argument:
383 cmd[-1] += argument
384 self.printer.print_full_verbose (cmd)
385 # #3883: make sure only one Popen command occurs at a time due to
386 # http://bugs.python.org/issue13817
387 with _popen_lock:
388 command_line = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=local_env, preexec_fn=self._subprocess_setup)
389 (pstd_out, pstd_err) = command_line.communicate() # pstd_err should be None due to pipe above
390 return (command_line.returncode, pstd_out)
391
392 def build(self, p, argument = None, robust_build=False):
393 """
394 Build package
395 @param p: package name
396 @type p: str
397 """
398 return_string = ""
399 try:
400 # warn if ROS_BUILD_BLACKLIST encountered if applicable
401 # do not build packages for which the build has failed
402 if argument == "test": # Tests are not build dependent
403 failed_packages = []
404 else:
405 with self._result_lock:
406 failed_packages = [j for j in self.result[argument] if not self.result[argument][j] == True]
407
408 (buildable, error, why) = self.flag_tracker.can_build(p, self.skip_blacklist, failed_packages)
409 if buildable or self.robust_build:
410 start_time = time.time()
411 (returncode, pstd_out) = self._build_package(p, argument)
412 self.profile[argument][p] = time.time() - start_time
413 self.output[argument][p] = pstd_out
414 if argument:
415 log_type = "build_%s"%argument
416 else:
417 log_type = "build"
418 if not returncode:
419 self.printer.print_full_verbose( pstd_out)
420 with self._result_lock:
421 self.result[argument][p] = True
422
423 num_warnings = len(re.findall("warning:", pstd_out))
424 if num_warnings > 0:
425 return_string = ("[PASS] [ %.2f seconds ] -- WARNING: %d compiler warnings"%(self.profile[argument][p], num_warnings))
426 else:
427 return_string = ("[PASS] [ %.2f seconds ]"%( self.profile[argument][p]))
428 self.output_to_file(p, log_type, pstd_out, num_warnings > 0)
429 else:
430 success = False
431 no_target = len(re.findall("No rule to make target", pstd_out)) > 0
432 interrupt = len(re.findall("Interrupt", pstd_out)) > 0
433 if no_target:
434 return_string = ( "[SKIP] No rule to make target %s"%( argument))
435 success = True
436 elif interrupt:
437 return_string = ("[Interrupted]" )
438 else:
439 return_string = ( "[FAIL] [ %.2f seconds ]"%( self.profile[argument][p]))
440 with self._result_lock:
441 self.result[argument][p] = True if no_target else False
442
443 if success == False: #don't print tail if [SKIP] target
444 self.printer.print_tail( pstd_out)
445 self.output_to_file(p, log_type, pstd_out, always_print= not (no_target or interrupt))
446
447 return (success, return_string)
448 else:
449 with self._result_lock:
450 self.result[argument][p] = error
451
452 return_string += why
453 return(error, return_string)
454 return (True, return_string) # this means that we didn't error in any case above
455 except rospkg.ResourceNotFound as ex:
456 with self._result_lock:
457 self.result[argument][p] = False
458 self.printer.print_verbose ("[SKIP] Package %s not found\n" % p)
459 self.output[argument][p] = "Package not found %s"%ex
460 return (False, return_string)
461
462
463 def output_to_file(self, package, log_type, stdout, always_print= False):
464 if not self.logging_enabled:
465 return
466 package_log_dir = os.path.join(self.log_dir, package)
467
468 std_out_filename = os.path.join(package_log_dir, log_type + "_output.log")
469 if not os.path.exists (package_log_dir):
470 makedirs_with_parent_perms(package_log_dir)
471 with open(std_out_filename, 'w') as stdout_file:
472 stdout_file.write(stdout)
473 print_string = "Output from build of package %s written to:\n[ rosmake ] %s"%(package, std_out_filename)
474 if always_print:
475 self.printer.print_all(print_string)
476 else:
477 self.printer.print_full_verbose(print_string)
478
479 def generate_summary_output(self, log_dir):
480 if not self.logging_enabled:
481 return
482
483 self.printer.print_all("Results:")
484 if 'clean' in self.result.keys():
485 self.printer.print_all("Cleaned %d packages."%len(self.result['clean']))
486 if None in self.result.keys():
487 build_failure_count = len([p for p in self.result[None].keys() if self.result[None][p] == False])
488 self.printer.print_all("Built %d packages with %d failures."%(len(self.result[None]), build_failure_count))
489 if 'test' in self.result.keys():
490 test_failure_count = len([p for p in self.result['test'].keys() if self.result['test'][p] == False])
491 self.printer.print_all("Tested %d packages with %d failures."%(len(self.result['test']), test_failure_count))
492 self.printer.print_all("Summary output to directory")
493 self.printer.print_all("%s"%self.log_dir)
494 if self.rejected_packages:
495 self.printer.print_all("WARNING: Skipped command line arguments: %s because they could not be resolved to a stack name or a package name. "%self.rejected_packages)
496
497
498
499 if None in self.result.keys():
500 if len(self.result[None].keys()) > 0:
501 buildfail_filename = os.path.join(log_dir, "buildfailures.txt")
502 with open(buildfail_filename, 'w') as bf:
503 bf.write("Build failures:\n")
504 for key in self.build_list:
505 if key in self.result[None].keys() and self.result[None][key] == False:
506 bf.write("%s\n"%key)
507 if None in self.output.keys():
508 buildfail_context_filename = os.path.join(log_dir, "buildfailures-with-context.txt")
509 with open(buildfail_context_filename, 'w') as bfwc:
510 bfwc.write("Build failures with context:\n")
511 for key in self.build_list:
512 if key in self.result[None].keys() and self.result[None][key] == False:
513 bfwc.write("---------------------\n")
514 bfwc.write("%s\n"%key)
515 if key in self.output[None]:
516 bfwc.write(self.output[None][key])
517
518 if "test" in self.result.keys():
519 if len(self.result["test"].keys()) > 0:
520 testfail_filename = os.path.join(log_dir, "testfailures.txt")
521 with open(testfail_filename, 'w') as btwc:
522 btwc.write("Test failures:\n")
523 for key in self.build_list:
524 if key in self.result["test"].keys() and self.result["test"][key] == False:
525 btwc.write("%s\n"%key)
526
527 if "test" in self.output.keys():
528 testfail_filename = os.path.join(log_dir, "testfailures-with-context.txt")
529 with open(testfail_filename, 'w') as btwc:
530 btwc.write("Test failures with context:\n")
531 for key in self.build_list:
532 if key in self.result["test"].keys() and self.result["test"][key] == False:
533 btwc.write("%s\n"%key)
534 if key in self.output["test"]:
535 btwc.write(self.output["test"][key])
536
537 profile_filename = os.path.join(log_dir, "profile.txt")
538 with open(profile_filename, 'w') as pf:
539 pf.write(self.get_profile_string())
540
541
542
543 def get_profile_string(self):
544 output = '--------------\nProfile\n--------------\n'
545 total = 0.0
546 count = 1
547 for key in self.build_list:
548 build_results = ["[Not Built ]", "[ Built ]", "[Build Fail]"];
549 test_results = ["[Untested ]", "[Test Pass]", "[Test Fail]"];
550 build_result = 0
551 test_result = 0
552 test_time = 0.0
553 build_time = 0.0
554
555 if None in self.result.keys():
556 if key in self.result[None].keys():
557 if self.result[None][key] == True:
558 build_result = 1
559 else:
560 build_result = 2
561
562 if "test" in self.profile.keys():
563 if key in self.result["test"].keys():
564 if self.result["test"][key] == True:
565 test_result = 1
566 else:
567 test_result = 2
568
569 if None in self.profile.keys():
570 if key in self.profile[None].keys():
571 build_time = self.profile[None][key]
572
573 if "test" in self.profile.keys():
574 if key in self.profile["test"].keys():
575 test_time = self.profile["test"][key]
576
577
578 output = output + "%3d: %s in %.2f %s in %.2f --- %s\n"% (count, build_results[build_result], build_time , test_results[test_result], test_time, key)
579 total = total + build_time
580 count = count + 1
581
582 elapsed_time = self.finish_time - self.start_time
583 output = output + "----------------\n" + "%.2f Cumulative, %.2f Elapsed, %.2f Speedup \n"%(total, elapsed_time, float(total) / float(elapsed_time))
584 return output
585
586 def main(self):
587 """
588 main command-line entrypoint
589 """
590 parser = OptionParser(usage="usage: %prog [options] [PACKAGE]...",
591 description="rosmake recursively builds all dependencies before building a package", prog='rosmake')
592 parser.add_option("--test-only", dest="test_only", default=False,
593 action="store_true", help="only run tests")
594 parser.add_option("-t", dest="test", default=False,
595 action="store_true", help="build and test packages")
596 parser.add_option("-a", "--all", dest="build_all", default=False,
597 action="store_true", help="select all packages")
598 parser.add_option("-i", "--mark-installed", dest="mark_installed", default=False,
599 action="store_true", help="On successful build, mark specified packages as installed with ROS_NOBUILD")
600 parser.add_option("-u", "--unmark-installed", dest="unmark_installed", default=False,
601 action="store_true", help="Remove ROS_NOBUILD from the specified packages. This will not build anything.")
602 parser.add_option("-v", dest="verbose", default=False,
603 action="store_true", help="display errored builds")
604 parser.add_option("-r","-k", "--robust", dest="best_effort", default=False,
605 action="store_true", help="do not stop build on error")
606 parser.add_option("--build-everything", dest="robust", default=False,
607 action="store_true", help="build all packages regardless of errors")
608 parser.add_option("-V", dest="full_verbose", default=False,
609 action="store_true", help="display all builds")
610 parser.add_option("-s", "--specified-only", dest="specified_only", default=False,
611 action="store_true", help="only build packages specified on the command line")
612 parser.add_option("--buildtest", dest="buildtest",
613 action="append", help="package to buildtest")
614 parser.add_option("--buildtest1", dest="buildtest1",
615 action="append", help="package to buildtest1")
616 parser.add_option("--output", dest="output_dir",
617 action="store", help="where to output results")
618 parser.add_option("--pre-clean", dest="pre_clean",
619 action="store_true", help="run make clean first")
620 parser.add_option("--bootstrap", dest="bootstrap", default=False,
621 action="store_true", help="DEPRECATED, UNUSED")
622 parser.add_option("--disable-logging", dest="logging_enabled", default=True,
623 action="store_false", help="turn off all logs")
624 parser.add_option("--target", dest="target",
625 action="store", help="run make with this target")
626 parser.add_option("--pjobs", dest="ros_parallel_jobs", type="int",
627 action="store", help="Override ROS_PARALLEL_JOBS environment variable with this number of jobs.")
628 parser.add_option("--threads", dest="threads", type="int", default = os.environ.get("ROSMAKE_THREADS", parallel_build.num_cpus()),
629 action="store", help="Build up to N packages in parallel")
630 parser.add_option("--profile", dest="print_profile", default=False,
631 action="store_true", help="print time profile after build")
632 parser.add_option("--skip-blacklist", dest="skip_blacklist",
633 default=False, action="store_true",
634 help="skip packages containing a file called ROS_BUILD_BLACKLIST (Default behavior will ignore the presence of ROS_BUILD_BLACKLIST)")
635 parser.add_option("--skip-blacklist-osx", dest="skip_blacklist_osx",
636 default=False, action="store_true",
637 help="deprecated option. it will do nothing, please use platform declarations and --require-platform instead")
638
639 parser.add_option("--status-rate", dest="status_update_rate",
640 action="store", help="How fast to update the status bar in Hz. Default: 5Hz")
641
642
643 options, args = parser.parse_args()
644 self.printer.print_all('rosmake starting...')
645
646 rospack = self.rospack
647 rosstack = self.rosstack
648
649 testing = False
650 building = True
651 if options.test_only:
652 testing = True
653 building = False
654 elif options.test:
655 testing = True
656
657 if options.ros_parallel_jobs:
658 self.ros_parallel_jobs = options.ros_parallel_jobs
659
660 self.robust_build = options.robust
661 self.best_effort = options.best_effort
662 self.threads = options.threads
663 self.skip_blacklist = options.skip_blacklist
664 if options.skip_blacklist_osx:
665 self.printer.print_all("Option --skip-blacklist-osx is deprecated. It will do nothing, please use platform declarations and --require-platform instead");
666 self.logging_enabled = options.logging_enabled
667
668 # pass through verbosity options
669 self.printer.full_verbose = options.full_verbose
670 self.printer.verbose = options.verbose
671 if options.status_update_rate:
672 if float(options.status_update_rate)> 0:
673 self.printer.duration = 1.0/float(options.status_update_rate)
674 else:
675 self.printer.duration = 0
676
677 packages = []
678 #load packages from arguments
679 if options.build_all:
680 packages = [x for x in rospack.list() if not self.rospack.get_manifest(x).is_catkin]
681 self.printer.print_all( "Building all packages")
682 else: # no need to extend if all already selected
683 if options.buildtest:
684 for p in options.buildtest:
685 packages.extend(self.rospack.get_depends_on(p))
686 self.printer.print_all( "buildtest requested for package %s adding it and all dependent packages: "%p)
687
688 if options.buildtest1:
689 for p in options.buildtest1:
690 packages.extend(self.rospack.get_depends_on(p, implicit=False))
691 self.printer.print_all( "buildtest1 requested for package %s adding it and all depends-on1 packages: "%p)
692
693 if len(packages) == 0 and len(args) == 0:
694 p = os.path.basename(os.path.abspath('.'))
695 try:
696 if os.path.samefile(rospack.get_path(p), '.'):
697 packages = [p]
698 self.printer.print_all( "No package specified. Building %s"%packages)
699 else:
700 self.printer.print_all("No package selected and the current directory is not the correct path for package '%s'."%p)
701
702 except rospkg.ResourceNotFound as ex:
703 try:
704 stack_dir = rosstack.get_path(p)
705 if os.path.samefile(stack_dir, '.'):
706 packages = [p]
707 self.printer.print_all( "No package specified. Building stack %s"%packages)
708 else:
709 self.printer.print_all("No package or stack arguments and the current directory is not the correct path for stack '%s'. Stack directory is: %s."%(p, rosstack.get_path(p)))
710 except:
711 self.printer.print_all("No package or stack specified. And current directory '%s' is not a package name or stack name."%p)
712 else:
713 packages.extend(args)
714
715 self.printer.print_all( "Packages requested are: %s"%packages)
716
717 # Setup logging
718 if self.logging_enabled:
719 date_time_stamp = "rosmake_output-" + time.strftime("%Y%m%d-%H%M%S")
720 if options.output_dir:
721 #self.log_dir = os.path.join(os.getcwd(), options.output_dir, date_time_stamp);
722 self.log_dir = os.path.abspath(options.output_dir)
723 else:
724 self.log_dir = os.path.join(rospkg.get_ros_home(), "rosmake", date_time_stamp);
725
726 self.printer.print_all("Logging to directory %s"%self.log_dir)
727 if os.path.exists (self.log_dir) and not os.path.isdir(self.log_dir):
728 self.printer.print_all( "Log destination %s is a file; please remove it or choose a new destination"%self.log_dir)
729 sys.exit(1)
730 if not os.path.exists (self.log_dir):
731 self.printer.print_verbose("%s doesn't exist: creating"%self.log_dir)
732 makedirs_with_parent_perms(self.log_dir)
733
734 self.printer.print_verbose("Finished setting up logging")
735
736 stacks_arguments = [s for s in packages if s in rosstack.list()]
737 (self.specified_packages, self.rejected_packages) = rospkg.expand_to_packages(packages, rospack, rosstack)
738
739 self.printer.print_all("Expanded args %s to:\n%s"%(packages, self.specified_packages))
740 if self.rejected_packages:
741 self.printer.print_all("WARNING: The following args could not be parsed as stacks or packages: %s"%self.rejected_packages)
742 if len(self.specified_packages) + len(stacks_arguments) == 0:
743 self.printer.print_all("ERROR: No arguments could be parsed into valid package or stack names.")
744 self.printer.running = False
745 return False
746
747 if options.unmark_installed:
748 for p in self.specified_packages:
749 if self.flag_tracker.remove_nobuild(p):
750 self.printer.print_all("Removed ROS_NOBUILD from %s"%p)
751 self.printer.running = False
752 return True
753
754 required_packages = self.specified_packages[:]
755
756 # catch dependent packages which are inside of zero sized stacks #3528
757 # add them to required list but not the specified list.
758 for s in stacks_arguments:
759 for d in rosstack.get_depends(s, implicit=False):
760 required_packages.extend(rosstack.packages_of(d))
761
762 # deduplicate required_packages
763 required_packages = list(set(required_packages))
764
765 # make sure all dependencies are satisfied and if not warn
766 buildable_packages = []
767 for p in required_packages:
768 (buildable, error, str) = self.flag_tracker.can_build(p, self.skip_blacklist, [], False)
769 if buildable:
770 buildable_packages.append(p)
771
772 #generate the list of packages necessary to build(in order of dependencies)
773 counter = 0
774 for p in required_packages:
775
776 counter = counter + 1
777 self.printer.print_verbose( "Processing %s and all dependencies(%d of %d requested)"%(p, counter, len(packages)))
778 self.build_or_recurse(p)
779
780 # remove extra packages if specified-only flag is set
781 if options.specified_only:
782 new_list = []
783 for pkg in self.build_list:
784 if pkg in self.specified_packages:
785 new_list.append(pkg)
786 self.dependency_tracker = parallel_build.DependencyTracker(self.specified_packages, rospack=self.rospack) # this will make the tracker only respond to packages in the list
787
788 self.printer.print_all("specified-only option was used, only building packages %s"%new_list)
789 self.build_list = new_list
790
791 if options.pre_clean:
792 build_queue = parallel_build.BuildQueue(self.build_list, parallel_build.DependencyTracker([], rospack=self.rospack), robust_build = True)
793 self.parallel_build_pkgs(build_queue, "clean", threads = options.threads)
794
795 build_passed = True
796
797 if building:
798 self.printer.print_verbose ("Building packages %s"% self.build_list)
799 build_queue = parallel_build.BuildQueue(self.build_list, self.dependency_tracker, robust_build = options.robust or options.best_effort)
800 if None not in self.result.keys():
801 self.result[None] = {}
802
803 build_passed = self.parallel_build_pkgs(build_queue, options.target, threads = options.threads)
804
805 tests_passed = True
806 if build_passed and testing:
807 self.printer.print_verbose ("Testing packages %s"% packages)
808 build_queue = parallel_build.BuildQueue(self.specified_packages, parallel_build.DependencyTracker(self.specified_packages, rospack=self.rospack), robust_build = True)
809 tests_passed = self.parallel_build_pkgs(build_queue, "test", threads = 1)
810
811
812 if options.mark_installed:
813 if build_passed and tests_passed:
814 for p in self.specified_packages:
815 if self.flag_tracker.add_nobuild(p):
816 self.printer.print_all("Marking %s as installed with a ROS_NOBUILD file"%p)
817 else:
818 self.printer.print_all("All builds and tests did not pass cannot mark packages as installed. ")
819
820
821 self.finish_time = time.time() #note: before profiling
822 self.generate_summary_output(self.log_dir)
823
824 if options.print_profile:
825 self.printer.print_all (self.get_profile_string())
826
827 self.printer.running = False
828 return build_passed and tests_passed
829
830
0 #! /usr/bin/env python
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 are met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 # * Neither the name of Willow Garage, Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28
29
30 # Author Tully Foote/tfoote@willowgarage.com
31
32 import os
33 import sys
34 import subprocess
35
36 import rospkg
37 import rospkg.os_detect
38
39 def _platform_supported(m, os, version):
40 for p in m.platforms:
41 if os == p.os and version == p.version:
42 return True
43 return False
44
45 def platform_supported(rospack, pkg, os, version):
46 """
47 Return whether the platform defined by os and version is marked as supported in the package
48 @param pkg The package to test for support
49 @param os The os name to test for support
50 @param version The os version to test for support
51 """
52 return _platform_supported(rospack.get_manifest(pkg), os, version)
53
54 class PackageFlagTracker:
55 """ This will use the dependency tracker to test if packages are
56 blacklisted and all their dependents. """
57 def __init__(self, dependency_tracker, os_name = None, os_version = None):
58 if not os_name and not os_version:
59 try:
60 osd = rospkg.os_detect.OsDetect()
61 self.os_name = osd.get_codename()
62 self.os_version = osd.get_version()
63 except rospkg.os_detect.OsNotDetected as ex:
64 sys.stderr.write("Could not detect OS. platform detection will not work\n")
65 else:
66 self.os_name = os_name
67 self.os_version = os_version
68
69 self.rospack = rospkg.RosPack()
70 self.blacklisted = {}
71 self.blacklisted_osx = {}
72 self.nobuild = set()
73 self.nomakefile = set()
74 self.packages_tested = set()
75 self.dependency_tracker = dependency_tracker
76 self.build_failed = set()
77
78 def register_blacklisted(self, blacklisted_package, dependent_package):
79 if dependent_package in self.blacklisted.keys():
80 self.blacklisted[dependent_package].append(blacklisted_package)
81 else:
82 self.blacklisted[dependent_package] = [blacklisted_package]
83
84 def register_blacklisted_osx(self, blacklisted_package, dependent_package):
85 if dependent_package in self.blacklisted_osx:
86 self.blacklisted_osx[dependent_package].append(blacklisted_package)
87 else:
88 self.blacklisted_osx[dependent_package] = [blacklisted_package]
89
90 def _check_package_flags(self, package):
91 if package in self.packages_tested:
92 return
93 rospack = self.rospack
94 path = rospack.get_path(package)
95
96 if os.path.exists(os.path.join(path, "ROS_BUILD_BLACKLIST")):
97 self.register_blacklisted(package, package)
98 for p in rospack.get_depends_on(package, implicit=True):
99 self.register_blacklisted(package, p)
100
101 if os.path.exists(os.path.join(path, "ROS_BUILD_BLACKLIST_OSX")):
102 self.register_blacklisted_osx(package, package)
103 for p in rospack.get_depends_on(package, implicit=True):
104 self.register_blacklisted_osx(package, p)
105
106 # NO_BUILD if marker file or catkin attribute in manifest
107 if os.path.exists(os.path.join(path, "ROS_NOBUILD")):
108 self.nobuild.add(package)
109 if self.rospack.get_manifest(package).is_catkin:
110 self.nobuild.add(package)
111
112 if not os.path.exists(os.path.join(path, "Makefile")):
113 self.nomakefile.add(package)
114
115 self.packages_tested.add(package)
116
117 def is_blacklisted(self, package):
118 # this will noop if already run
119 self._check_package_flags(package)
120
121 # make sure it's not dependent on a blacklisted package
122 for p in self.dependency_tracker.get_deps(package):
123 if p not in self.packages_tested:
124 self._check_package_flags(p)
125
126 # test result after checking all dependents.
127 if package in self.blacklisted:
128 return self.blacklisted[package]
129
130 return []
131
132 def is_blacklisted_osx(self, package):
133 # this will noop if already run
134 self._check_package_flags(package)
135
136 # make sure it's not dependent on a blacklisted_osx package
137 for p in self.dependency_tracker.get_deps(package):
138 if p not in self.packages_tested:
139 self._check_package_flags(p)
140
141 # test result after checking all dependents.
142 if package in self.blacklisted_osx:
143 return self.blacklisted_osx[package]
144
145 return []
146
147 def has_nobuild(self, package):
148 # this will noop if already run
149 self._check_package_flags(package)
150
151 # Short circuit if known result
152 if package in self.nobuild:
153 return True
154 return False
155
156 def has_makefile(self, package):
157 # this will noop if already run
158 self._check_package_flags(package)
159
160 # Short circuit if known result
161 if package in self.nomakefile:
162 return False
163 return True
164
165 def add_nobuild(self, package):
166 if self.has_nobuild(package):
167 return True
168 with open(os.path.join(self.rospack.get_path(package), "ROS_NOBUILD"), 'w') as f:
169 f.write("created by rosmake to mark as installed")
170 self.nobuild.add(package)
171 return True
172 return False
173
174 def remove_nobuild(self, package):
175 if not self.has_nobuild(package):
176 return True
177 try:
178 os.remove(os.path.join(self.rospack.get_path(package), "ROS_NOBUILD"))
179 self.nobuild.remove(package)
180 return True
181 except:
182 return False
183
184 def mark_build_failed(self, package):
185 self.build_failed.add(package)
186
187 def build_failed(self, package):
188 return package in self.build_failed
189
190 def can_build(self, pkg, use_blacklist = False, failed_packages = [], use_makefile = True):
191 """
192 Return (buildable, error, "reason why not")
193 """
194 output_str = ""
195 output_state = True
196 buildable = True
197
198 previously_failed_pkgs = [ pk for pk in failed_packages if pk in self.dependency_tracker.get_deps(pkg)]
199 if len(previously_failed_pkgs) > 0:
200 buildable = False
201 output_state = False
202 output_str += " Package %s cannot be built for dependent package(s) %s failed. \n"%(pkg, previously_failed_pkgs)
203
204
205 if use_blacklist:
206 black_listed_dependents = self.is_blacklisted(pkg)
207 if len(black_listed_dependents) > 0:
208 buildable = False
209 output_str += "Cannot build %s ROS_BUILD_BLACKLIST found in packages %s"%(pkg, black_listed_dependents)
210
211 if self.has_nobuild(pkg):
212 buildable = False
213 output_state = True # dependents are ok, it should already be built
214 output_str += "ROS_NOBUILD in package %s\n"%pkg
215
216
217 if use_makefile and not self.has_makefile(pkg):
218 output_state = True # dependents are ok no need to build
219 buildable = False
220 output_str += " No Makefile in package %s\n"%pkg
221
222 if output_str and output_str[-1] == '\n':
223 output_str = output_str[:-1]
224
225 return (buildable, output_state, output_str)
0 #! /usr/bin/env python
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 are met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 # * Neither the name of Willow Garage, Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28
29
30 # Author Tully Foote/tfoote@willowgarage.com
31
32 import os
33 import re
34 import sys
35 import subprocess
36 import time
37
38 import rospkg
39 import threading
40
41 if sys.hexversion > 0x03000000: #Python3
42 python3 = True
43 else:
44 python3 = False
45
46 def _read_stdout(cmd):
47 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
48 std_out, std_err = p.communicate()
49 if python3:
50 return std_out.decode()
51 else:
52 return std_out
53
54 def num_cpus():
55 """
56 Detects the number of CPUs on a system. Cribbed from pp.
57 """
58 # Linux, Unix and MacOS:
59 if hasattr(os, "sysconf"):
60 if "SC_NPROCESSORS_ONLN" in os.sysconf_names:
61 # Linux & Unix:
62 ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
63 if isinstance(ncpus, int) and ncpus > 0:
64 return ncpus
65 else: # OSX:
66 return int(_read_stdout(["sysctl", "-n", "hw.ncpu"])) or 1
67 # Windows:
68 if "NUMBER_OF_PROCESSORS" in os.environ:
69 ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
70 if ncpus > 0:
71 return ncpus
72 return 1 # Default
73
74 #TODO: may no longer need this now that we've ported to rospkg
75 class DependencyTracker:
76 """ Track dependencies between packages. This is basically a
77 caching way to call rospkg. It also will allow you to specifiy a
78 range of packages over which to track dependencies. This is useful
79 if you are only building a subset of the tree. For example with the
80 --specified-only option. """
81 def __init__(self, valid_packages=None, rospack=None):
82 """
83 @param valid_packages: defaults to rospack list
84 """
85 if rospack is None:
86 self.rospack = rospkg.RosPack()
87 else:
88 self.rospack = rospack
89 if valid_packages is None:
90 valid_packages = self.rospack.list()
91 self.valid_packages = valid_packages
92 self.deps_1 = {}
93 self.deps = {}
94
95 def get_deps_1(self, package):
96 if not package in self.deps_1:
97 self.deps_1[package] = []
98 try:
99 potential_dependencies = self.rospack.get_depends(package, implicit=False)
100 except rospkg.ResourceNotFound:
101 potential_dependencies = []
102 for p in potential_dependencies:
103 if p in self.valid_packages:
104 self.deps_1[package].append(p)
105
106 return self.deps_1[package]
107
108 def get_deps(self, package):
109 if not package in self.deps:
110 self.deps[package] = []
111 try:
112 potential_dependencies = self.rospack.get_depends(package)
113 except rospkg.ResourceNotFound:
114 potential_dependencies = []
115
116 for p in potential_dependencies:
117 if p in self.valid_packages:
118 self.deps[package].append(p)
119 return self.deps[package]
120
121 def load_fake_deps(self, deps, deps1):
122 self.deps = deps
123 self.deps_1 = deps1
124 return
125
126
127 class CompileThread(threading.Thread):
128 """ This is the class which is used as the thread for parallel
129 builds. This class will query the build queue object for new
130 commands and block on its calls until the build queue says that
131 building is done. """
132 def __init__(self, name, build_queue, rosmakeall, argument = None):
133 threading.Thread.__init__(self)
134 self.build_queue = build_queue
135 self.rosmakeall = rosmakeall
136 self.argument = argument
137 self.name = name
138 self.logging_enabled = True
139
140 def run(self):
141 while not self.build_queue.is_done():
142 pkg = self.build_queue.get_valid_package()
143 if not pkg:
144 if self.build_queue.succeeded():
145 self.rosmakeall.printer.print_verbose("[ Build Completed Thread Exiting ]", thread_name=self.name);
146 else:
147 self.rosmakeall.printer.print_verbose("[ Build Terminated Thread Exiting ]", thread_name=self.name)
148 break # no more packages must be done
149
150 # update status after accepting build
151 self.rosmakeall.update_status(self.argument ,
152 self.build_queue.get_started_threads(),
153 self.build_queue.progress_str())
154
155 if self.argument:
156 self.rosmakeall.printer.print_all ("Starting >>> %s [ make %s ]"%(pkg, self.argument), thread_name=self.name)
157 else:
158 self.rosmakeall.printer.print_all ("Starting >>> %s [ make ] "%pkg, thread_name=self.name)
159 (result, result_string) = self.rosmakeall.build(pkg, self.argument, self.build_queue.robust_build)
160 self.rosmakeall.printer.print_all("Finished <<< %s %s"%(pkg, result_string), thread_name= self.name)
161 #print "Finished2"
162 self.build_queue.return_built(pkg, result)
163 #print "returned"
164 if result or self.build_queue.robust_build:
165 pass#print "result", result, "robust", self.build_queue.robust_build
166 else:
167 if result_string.find("[Interrupted]") != -1:
168 self.rosmakeall.printer.print_all("Caught Interruption", thread_name=self.name)
169 self.build_queue.stop() #todo move this logic into BuildQueue itself
170 break # unnecessary since build_queue is done now while will quit
171 self.rosmakeall.printer.print_all("Halting due to failure in package %s. \n[ rosmake ] Waiting for other threads to complete."%pkg)
172 self.build_queue.stop()
173 break # unnecessary since build_queue is done now, while will quit
174 # update status after at end of build
175 #print "updating status"
176 self.rosmakeall.update_status(self.argument ,
177 self.build_queue.get_started_threads(),
178 self.build_queue.progress_str())
179 #print "done built", len(self.build_queue.built), self.build_queue.built
180 #print "failed", len(self.build_queue.failed), self.build_queue.failed
181 #print "to_build", len(self.build_queue.to_build), self.build_queue.to_build
182 #print "in progress", len(self.build_queue._started), self.build_queue._started
183
184 #print "last update"
185 # update status before ending thread
186 self.rosmakeall.update_status(self.argument ,
187 self.build_queue.get_started_threads(),
188 self.build_queue.progress_str())
189 #print "thread finished"
190
191 class BuildQueue:
192 """ This class provides a thread safe build queue. Which will do
193 the sequencing for many CompileThreads. """
194 def __init__(self, package_list, dependency_tracker, robust_build = False):
195 self._total_pkgs = len(package_list)
196 self.dependency_tracker = dependency_tracker
197 self.to_build = package_list[:] # do a copy not a reference
198 self.built = []
199 self.failed = []
200 self.condition = threading.Condition()
201 self._done = False
202 self.robust_build = robust_build
203 self._started = {}
204 self._hack_end_counter = 0
205
206 def progress_str(self):
207 return "[ %d Active %d/%d Complete ]"%(len(self._started), len(self.built), self._total_pkgs)
208
209 def get_started_threads(self): #TODO sort this other than hash order
210 return self._started.copy()
211
212 def is_completed(self):
213 """Return if the build queue has been completed """
214 return len(self.built)+ len(self.failed) == self._total_pkgs
215
216 def is_done(self):
217 """Return if the build queue has been completed """
218 return self.is_completed() or self._done # finished or halted
219
220 def succeeded(self):
221 """ Return whether the build queue has completed all packages successfully. """
222 return len(self.built) == self._total_pkgs #flag that we're finished
223
224 def stop(self):
225 """ Stop the build queue, including waking all blocking
226 threads. It will not stop in flight builds."""
227 self._done = True
228 with self.condition:
229 self.condition.notifyAll() # wake any blocking threads
230
231 def return_built(self, package, successful=True): # mark that a package is built
232 """ The thread which completes a package marks it as done with
233 this method."""
234 with self.condition:
235 if successful:
236 self.built.append(package)
237 else:
238 self.failed.append(package)
239 if package in self._started.keys():
240 self._started.pop(package)
241 else:
242 pass #used early on print "\n\n\nERROR THIS SHOULDN't RETURN %s\n\n\n"%package
243 if self.is_completed():
244 self._done = True
245 self.condition.notifyAll() #wake up any waiting threads
246
247 def get_valid_package(self): # blocking call to get a package to build returns none if done
248 """ This is a blocking call which will return a package which has
249 all dependencies met. If interrupted or done it will return
250 None"""
251 with self.condition:
252 while (not self.is_done() and len(self.to_build) > 0):
253 for p in self.to_build:
254 dependencies_met = True
255 for d in self.dependency_tracker.get_deps(p):
256 if d not in self.built and not (self.robust_build and d in self.failed):
257 dependencies_met = False
258 #print "Dependency %s not met for %s"%(d, p)
259 break
260 if dependencies_met: # all dependencies met
261 self.to_build.remove(p)
262 self._started[p] = time.time()
263 self._hack_end_counter = 0 #reset end counter if success
264 return p # break out and return package if found
265 elif len(self._started) == 0 and self._hack_end_counter > 2:
266 # we're hung with broken dependencies
267 return None
268 #print "TTGTTTTHTHT Waiting on condition"
269 self.condition.wait(1.0) # failed to find a package wait for a notify before looping
270
271 self._hack_end_counter += 1 # if we're here too often we will quit
272 if self.is_done():
273 break
274
275 return None
0 [nosetests]
1 with-coverage=1
2 cover-package=rosmake
3 with-xunit=1
0 #!/usr/bin/env python
1 # Copyright (c) 2009, 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 the 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 import sys
29 import unittest
30
31 from rosmake import parallel_build
32
33 class TestDependencyTracker(unittest.TestCase):
34 def setUp(self):
35 self.deps = {}
36 self.deps1 = {}
37 self.deps["a"] = [ "b", "c", "d","e"]
38 self.deps1["a"] = ["b"]
39 self.deps["b"] = ["c"]
40 self.deps1["b"] = ["c"]
41 self.deps["d"] = ["c", "e"]
42 self.deps1["d"] = ["c", "e"]
43 self.dt = parallel_build.DependencyTracker()
44 self.dt.load_fake_deps(self.deps, self.deps1)
45
46
47 def test_deps_1(self):
48 self.assertEquals(self.deps1["a"], self.dt.get_deps_1("a"))
49 self.assertEquals(self.deps1["b"], self.dt.get_deps_1("b"))
50 self.assertEquals(self.deps1["d"], self.dt.get_deps_1("d"))
51
52 def test_deps(self):
53 self.assertEquals(self.deps["a"], self.dt.get_deps("a"))
54 self.assertEquals(self.deps["b"], self.dt.get_deps("b"))
55 self.assertEquals(self.deps["d"], self.dt.get_deps("d"))
56
57 def test_not_package(self):
58 self.assertEquals([], self.dt.get_deps("This is not a valid package name"))
59 self.assertEquals([], self.dt.get_deps_1("This is not a valid package name"))
60
61
62 class TestBuildQueue(unittest.TestCase):
63
64 def setUp(self):
65 deps = {}
66 deps1 = {}
67 deps1["a"] = ["b"]
68 deps["a"] = ["b", "c", "d", "e", "f"]
69 deps1["b"] = ["c"]
70 deps["b"] = ["c", "d", "e", "f"]
71 deps1["c"] = ["d"]
72 deps["c"] = ["d", "e", "f"]
73 deps1["d"] = ["e"]
74 deps["d"] = ["e", "f"]
75 deps["e"] = ["f"]
76 deps1["e"] = ["f"]
77 deps["f"] = []
78 deps1["f"] = []
79
80 self.serial_tracker = parallel_build.DependencyTracker()
81 self.serial_tracker.load_fake_deps(deps, deps1)
82
83 deps = {}
84 deps1 = {}
85 deps["a"] = ["b", "c", "d", "e", "f"]
86 deps1["a"] = ["b", "c", "d", "e", "f"]
87 deps["b"] = []
88 deps1["b"] = []
89 deps["c"] = []
90 deps1["c"] = []
91 deps["d"] = []
92 deps1["d"] = []
93 deps["e"] = []
94 deps1["e"] = []
95 deps["f"] = []
96 deps1["f"] = []
97
98 self.parallel_tracker = parallel_build.DependencyTracker()
99 self.parallel_tracker.load_fake_deps(deps, deps1)
100
101 # full queue
102 def test_full_build(self):
103 bq = parallel_build.BuildQueue(["a", "b", "c", "d", "e", "f"], self.serial_tracker)
104 self.assertFalse(bq.is_done())
105 self.assertFalse(bq.succeeded())
106
107 self.assertEqual("f", bq.get_valid_package())
108 self.assertEqual(0, len(bq.built))
109 bq.return_built("f")
110 self.assertEqual(1, len(bq.built))
111 self.assertFalse(bq.is_done())
112 self.assertFalse(bq.succeeded())
113
114 self.assertEqual("e", bq.get_valid_package())
115 bq.return_built("e")
116 self.assertEqual(2, len(bq.built))
117 self.assertFalse(bq.is_done())
118 self.assertFalse(bq.succeeded())
119
120 self.assertEqual("d", bq.get_valid_package())
121 bq.return_built("d")
122 self.assertEqual(3, len(bq.built))
123 self.assertFalse(bq.is_done())
124 self.assertFalse(bq.succeeded())
125
126 self.assertEqual("c", bq.get_valid_package())
127 bq.return_built("c")
128 self.assertEqual(4, len(bq.built))
129 self.assertFalse(bq.is_done())
130 self.assertFalse(bq.succeeded())
131
132 self.assertEqual("b", bq.get_valid_package())
133 bq.return_built("b")
134 self.assertEqual(5, len(bq.built))
135 self.assertFalse(bq.is_done())
136 self.assertFalse(bq.succeeded())
137
138 self.assertEqual("a", bq.get_valid_package())
139 self.assertFalse(bq.is_done())
140 self.assertFalse(bq.succeeded())
141 bq.return_built("a")
142 self.assertEqual(6, len(bq.built))
143 self.assertTrue (bq.is_done())
144 self.assertTrue (bq.succeeded())
145
146
147 # partial build
148 def test_partial_build(self):
149 bq = parallel_build.BuildQueue(["d", "e", "f"], self.serial_tracker)
150 self.assertFalse(bq.is_done())
151 self.assertFalse(bq.succeeded())
152
153 self.assertEqual("f", bq.get_valid_package())
154 self.assertEqual(0, len(bq.built))
155 bq.return_built("f")
156 self.assertEqual(1, len(bq.built))
157 self.assertFalse(bq.is_done())
158 self.assertFalse(bq.succeeded())
159
160 self.assertEqual("e", bq.get_valid_package())
161 bq.return_built("e")
162 self.assertEqual(2, len(bq.built))
163 self.assertFalse(bq.is_done())
164 self.assertFalse(bq.succeeded())
165
166 self.assertEqual("d", bq.get_valid_package())
167 self.assertFalse(bq.is_done())
168 self.assertFalse(bq.succeeded())
169 bq.return_built("d")
170 self.assertEqual(3, len(bq.built))
171 self.assertTrue(bq.is_done())
172 self.assertTrue(bq.succeeded())
173
174
175 # abort early
176 def test_abort_early(self):
177 bq = parallel_build.BuildQueue(["a", "b", "c", "d", "e", "f"], self.serial_tracker)
178 self.assertFalse(bq.is_done())
179 self.assertFalse(bq.succeeded())
180 self.assertEqual(0, len(bq.built))
181
182 self.assertEqual("f", bq.get_valid_package())
183 bq.return_built("f")
184 self.assertEqual(1, len(bq.built))
185 self.assertFalse(bq.is_done())
186 self.assertFalse(bq.succeeded())
187
188 self.assertEqual("e", bq.get_valid_package())
189 bq.return_built("e")
190 self.assertEqual(2, len(bq.built))
191 self.assertFalse(bq.is_done())
192 self.assertFalse(bq.succeeded())
193
194 self.assertEqual("d", bq.get_valid_package())
195 bq.return_built("d")
196 self.assertEqual(3, len(bq.built))
197 self.assertFalse(bq.is_done())
198 self.assertFalse(bq.succeeded())
199
200 bq.stop()
201 self.assertTrue(bq.is_done())
202 self.assertFalse(bq.succeeded())
203
204 self.assertEqual(None, bq.get_valid_package())
205
206 # many parallel
207 def test_parallel_build(self):
208 bq = parallel_build.BuildQueue(["a", "b", "c", "d", "e", "f"], self.parallel_tracker)
209 self.assertFalse(bq.is_done())
210 self.assertFalse(bq.succeeded())
211
212 dependents = ["b", "c", "d", "e", "f"]
213 count = 0
214 total = 6
215 while len(dependents) > 0:
216 result= bq.get_valid_package()
217 done = len(bq.built)
218 pkgs = bq._total_pkgs
219 self.assertTrue(result in dependents)
220 #print result, done, pkgs
221 dependents.remove(result)
222 self.assertEqual(count, done)
223 self.assertEqual(total, pkgs)
224 self.assertFalse(bq.is_done())
225 self.assertFalse(bq.succeeded())
226 bq.return_built(result)
227 count = count + 1
228 self.assertFalse(bq.is_done())
229 self.assertFalse(bq.succeeded())
230
231
232 self.assertEqual("a", bq.get_valid_package())
233 self.assertFalse(bq.is_done())
234 self.assertFalse(bq.succeeded())
235 bq.return_built("a")
236 self.assertTrue (bq.is_done())
237 self.assertTrue (bq.succeeded())
238
239
240 # stalled(future)
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
32 import os
33 import sys
34 import subprocess
35
36 def test_Rosmake_commandline_usage():
37 assert 0 == subprocess.call(["rosmake", "-h"])
0 cmake_minimum_required(VERSION 2.8)
1 project(rosunit)
2 find_package(catkin REQUIRED)
3 catkin_package()
4 catkin_package_export(CFG_EXTRAS ${PROJECT_NAME}-extras.cmake)
5
6 catkin_python_setup()
7
8 install(PROGRAMS
9 scripts/test_results_dir.py
10 scripts/summarize_results.py
11 scripts/rosunit
12 scripts/pycoverage_to_html.py
13 scripts/clean_junit_xml.py
14 scripts/check_test_ran.py
15 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/scripts)
16
17 catkin_add_nosetests(test)
0 macro(rosunit_initialize_tests)
1 if (@BUILDSPACE@)
2 find_program_required(ROSUNIT_EXE rosunit
3 PATHS @PROJECT_SOURCE_DIR@/scripts
4 NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
5 else()
6 find_program_required(ROSUNIT_EXE rosunit
7 PATHS @CMAKE_INSTALL_PREFIX@/bin
8 NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
9 endif()
10 endmacro()
11
12 rosunit_initialize_tests()
13
14 function(add_pyunit file)
15
16 message(WARNING "add_pyunit() is deprecated. For Python tests, use add_nostests() instead.")
17
18 # Look for optional TIMEOUT argument, #2645
19 parse_arguments(_pyunit "TIMEOUT;WORKING_DIRECTORY" "" ${ARGN})
20 if(NOT _pyunit_TIMEOUT)
21 set(_pyunit_TIMEOUT 60.0)
22 endif()
23
24 # Check that the file exists, #1621
25 set(_file_name _file_name-NOTFOUND)
26 if(IS_ABSOLUTE ${file})
27 set(_file_name ${file})
28 else()
29 find_file(_file_name ${file}
30 PATHS ${CMAKE_CURRENT_SOURCE_DIR}
31 NO_DEFAULT_PATH
32 NO_CMAKE_FIND_ROOT_PATH) # for cross-compilation. thanks jeremy.
33 if(NOT _file_name)
34 message(FATAL_ERROR "Can't find pyunit file \"${file}\"")
35 endif()
36 endif()
37
38 # We look for ROS_TEST_COVERAGE=1
39 # to indicate that coverage reports are being requested.
40 if("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
41 set(_covarg "--cov")
42 else()
43 set(_covarg)
44 endif()
45
46 # Create a legal test name, in case the target name has slashes in it
47 string(REPLACE "/" "_" _testname ${file})
48 # We use rostest to call the executable to get process control, #1629
49 set(cmd "${ROSUNIT_EXE} --name=${_testname} --time-limit=${_pyunit_TIMEOUT} --package=${PROJECT_NAME} -- ${_file_name} ${_covarg}")
50 catkin_run_tests_target("rosunit" ${_testname} "rosunit-${_testname}.xml" COMMAND ${cmd} WORKING_DIRECTORY ${_pyunit_WORKING_DIRECTORY})
51 endfunction()
52
0 <package>
1 <description brief="Unit testing for ROS">
2
3 Unit-testing package for ROS. This is a lower-level library for rostest and handles unit tests, whereas rostest handles integration tests.
4
5 </description>
6 <author>Ken Conley</author>
7 <license>BSD</license>
8 <review status="Doc reviewed" notes="2011/01/12, REP 100"/>
9 <url>http://ros.org/wiki/rosunit</url>
10 <export>
11 <rosdoc config="rosdoc.yaml" />
12 </export>
13 <rosdep name="gtest"/>
14
15 </package>
16
17
0 <package>
1 <name>rosunit</name>
2 <version>1.9.11</version>
3 <description>
4 Unit-testing package for ROS. This is a lower-level library for rostest and handles unit tests, whereas rostest handles integration tests.
5 </description>
6 <maintainer email="dthomas@willowgarage.com">Dirk Thomas</maintainer>
7 <license>BSD</license>
8
9 <url type="website">http://ros.org/wiki/rosunit</url>
10 <url type="bugtracker">https://code.ros.org/trac/ros/query?status=assigned&amp;status=new&amp;status=reopened&amp;component=ros&amp;order=priority</url>
11 <url type="repository">https://code.ros.org/svn/ros/stacks/ros/trunk/tools/rosunit</url>
12
13 <author>Ken Conley</author>
14
15 <build_depend>catkin</build_depend>
16
17 <export>
18 <rosdoc config="rosdoc.yaml" />
19 </export>
20 </package>
0 - builder: epydoc
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34
35 """
36 Writes a test failure out to test file if it doesn't exist.
37 """
38
39 from __future__ import print_function
40 NAME="check_test_ran.py"
41
42 import os
43 import sys
44
45 import rospkg
46 import rosunit
47
48 def usage():
49 print("""Usage:
50 \t%s test-file.xml
51 or
52 \t%s --rostest pkg-name test-file.xml
53 """%(NAME, NAME), file=sys.stderr)
54 print(sys.argv)
55 sys.exit(getattr(os, 'EX_USAGE', 1))
56
57 def check_main():
58 if len(sys.argv) < 2:
59 usage()
60 if '--rostest' in sys.argv[1:]:
61 if len(sys.argv) != 4:
62 usage()
63 test_pkg, test_file = [a for a in sys.argv[1:] if a != '--rostest']
64 # this logic derives the output filename that rostest uses
65
66 r = rospkg.RosPack()
67 pkg_name = rospkg.get_package_name(test_file)
68 pkg_dir = r.get_path(pkg_name)
69
70 # compute test name for friendlier reporting
71 outname = rosunit.rostest_name_from_path(pkg_dir, test_file)
72
73 test_file = rosunit.xml_results_file(test_pkg, outname, is_rostest=True)
74 else:
75 if len(sys.argv) != 2:
76 usage()
77 test_file = sys.argv[1]
78
79 print("Checking for test results in %s"%test_file)
80
81 if not os.path.exists(test_file):
82 if not os.path.exists(os.path.dirname(test_file)):
83 os.makedirs(os.path.dirname(test_file))
84
85 print("Cannot find results, writing failure results to", test_file)
86
87 with open(test_file, 'w') as f:
88 test_name = os.path.basename(test_file)
89 d = {'test': test_name, 'test_file': test_file }
90 f.write("""<?xml version="1.0" encoding="UTF-8"?>
91 <testsuite tests="1" failures="1" time="1" errors="0" name="%(test)s">
92 <testcase name="test_ran" status="run" time="1" classname="Results">
93 <failure message="Unable to find test results for %(test)s, test did not run.\nExpected results in %(test_file)s" type=""/>
94 </testcase>
95 </testsuite>"""%d)
96
97 if __name__ == '__main__':
98 check_main()
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2009, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34
35 from __future__ import print_function
36
37 """
38 clean_junit_xml.py is a simple script that takes all the xml-formatted
39 Ant JUnit XML test output in test_results and aggregates them into
40 test_results/_hudson. In this process, it strips any characters that
41 tend to cause Hudson trouble.
42 """
43
44 PKG = 'rosunit'
45
46 import os
47 import sys
48
49 import rospkg
50 import rosunit.junitxml as junitxml
51
52 def prepare_dirs(output_dir_name):
53 test_results_dir = rospkg.get_test_results_dir()
54 print("will read test results from", test_results_dir)
55 output_dir = os.path.join(test_results_dir, output_dir_name)
56 if not os.path.exists(output_dir):
57 print("creating directory", output_dir)
58 os.makedirs(output_dir)
59 return test_results_dir, output_dir
60
61 def clean_results(test_results_dir, output_dir, filter):
62 """
63 Read results from test_results_dir and write them into output_dir.
64 """
65 for d in os.listdir(test_results_dir):
66 if filter and d in filter:
67 continue
68 print("looking at", d)
69 test_dir = os.path.join(test_results_dir, d)
70 if not os.path.isdir(test_dir):
71 continue
72 base_test_name = os.path.basename(test_dir)
73 # for each test result that a package generated, read it, then
74 # rewrite it to our output directory. This will invoke our
75 # cleaning rules on the XML that protect the result from Hudson
76 # issues.
77 for file in os.listdir(test_dir):
78 if file.endswith('.xml'):
79 test_name = base_test_name + '.' + file[:-4]
80 file = os.path.join(test_dir, file)
81 try:
82 result = junitxml.read(file, test_name)
83 output_path = os.path.join(output_dir, "%s.xml"%test_name)
84 with open(output_path, 'w') as f:
85 print("re-writing", output_path)
86 f.write(result.xml().encode('utf-8'))
87 except Exception as e:
88 sys.stderr.write("ignoring [%s]: %s\n"%(file, e))
89
90 def main():
91
92 print("[clean_junit_xml]: STARTING")
93
94 output_dir_name = '_hudson'
95 test_results_dir, output_dir = prepare_dirs(output_dir_name)
96
97 print("[clean_junit_xml]: writing aggregated test results to %s"%output_dir)
98
99 clean_results(test_results_dir, output_dir, [output_dir_name, '.svn'])
100
101 print("[clean_junit_xml]: FINISHED")
102
103 if __name__ == '__main__':
104 main()
105
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34
35 """
36 Generate HTML reports from coverage.py (aka python-coverage). This is
37 currently a no-frills backend tool.
38 """
39
40 import sys
41 import roslib
42
43 try:
44 import coverage
45 except ImportError, e:
46 sys.stderr.write("ERROR: cannot import python-coverage, coverage report will not run.\nTo install coverage, run 'easy_install coverage'\n")
47 sys.exit(1)
48
49 def coverage_html():
50 import os.path
51 if not os.path.isfile('.coverage-modules'):
52 sys.stderr.write("No .coverage-modules file; nothing to do\n")
53 return
54
55 with open('.coverage-modules','r') as f:
56 modules = [x for x in f.read().split('\n') if x.strip()]
57
58 cov = coverage.coverage()
59 cov.load()
60
61 # import everything
62 for m in modules:
63 try:
64 base = m.split('.')[0]
65 roslib.load_manifest(base)
66 __import__(m)
67 except:
68 sys.stderr.write("WARN: cannot import %s\n"%(base))
69
70 modlist = '\n'.join([" * %s"%m for m in modules])
71 sys.stdout.write("Generating for\n%s\n"%(modlist))
72
73 # load the module instances to pass to coverage so it can generate annotation html reports
74 mods = []
75
76 # TODO: rewrite, buggy
77 for m in modules:
78 mods.extend([v for v in sys.modules.values() if v and v.__name__.startswith(m) and not v in mods])
79
80 # dump the output to covhtml directory
81 cov.html_report(mods, directory="covhtml")
82
83 if __name__ == '__main__':
84 coverage_html()
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2010, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 import rosunit.rosunit_main
34 rosunit.rosunit_main.rosunitmain()
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34
35 """
36 Prints summary of aggregated test results to stdout. This is useful
37 when running several tests across a package.
38 """
39
40 import os
41 import sys
42 import cStringIO
43
44 import rospkg
45 import rosunit.junitxml as junitxml
46
47 def create_summary(result, packages):
48 buff = cStringIO.StringIO()
49
50 buff.write('-'*80+'\n')
51 buff.write('\033[1m[AGGREGATED TEST RESULTS SUMMARY]\033[0m\n\n')
52
53 errors_failures = [r for r in result.test_case_results if r.errors or r.failures]
54 if errors_failures:
55 buff.write('ERRORS/FAILURES:\n')
56 for tc_result in errors_failures:
57 buff.write(tc_result.description)
58
59 buff.write("PACKAGES: \n%s\n\n"%'\n'.join([" * %s"%p for p in packages]))
60
61 buff.write('\nSUMMARY\n')
62 if (result.num_errors + result.num_failures) == 0:
63 buff.write("\033[32m * RESULT: SUCCESS\033[0m\n")
64 else:
65 buff.write("\033[1;31m * RESULT: FAIL\033[0m\n")
66
67 # TODO: still some issues with the numbers adding up if tests fail to launch
68
69 # number of errors from the inner tests, plus add in count for tests
70 # that didn't run properly ('result' object).
71 buff.write(" * TESTS: %s\n"%result.num_tests)
72 if result.num_errors:
73 buff.write("\033[1;31m * ERRORS: %s\033[0m\n"%result.num_errors)
74 else:
75 buff.write(" * ERRORS: 0\n")
76 if result.num_failures:
77 buff.write("\033[1;31m * FAILURES: %s\033[0m\n"%result.num_failures)
78 else:
79 buff.write(" * FAILURES: 0\n")
80 return buff.getvalue()
81
82 def main():
83 from optparse import OptionParser
84 parser = OptionParser(usage="usage: summarize_results.py [options] package")
85 parser.add_option("--nodeps",
86 dest="no_deps", default=False,
87 action="store_true",
88 help="don't compute test results for the specified package only")
89 (options, args) = parser.parse_args()
90 if len(args) != 1:
91 parser.error("Only one package may be specified")
92
93 package = args[0]
94 r = rospkg.RosPack()
95 if options.no_deps:
96 packages = [package]
97 else:
98 packages = [package] + r.get_depends_on(package, implicit=True)
99 packages = [p for p in packages if p]
100
101 result = junitxml.read_all(packages)
102 print create_summary(result, packages)
103 if result.num_errors or result.num_failures:
104 sys.exit(1)
105
106 if __name__ == '__main__':
107 main()
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2009, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34
35 """
36 test_results_dir.py simply prints the directory that rosunit/rostest
37 results are stored in.
38 """
39
40 from __future__ import print_function
41 import rospkg
42 print(rospkg.get_test_results_dir())
0 #!/usr/bin/env python
1
2 from distutils.core import setup
3 from catkin_pkg.package import parse_package_for_distutils
4
5 d = parse_package_for_distutils()
6 d['packages'] = ['rosunit']
7 d['package_dir'] = {'': 'src'}
8 d['scripts'] = ['scripts/rosunit']
9 d['install_requires'] = ['rospkg']
10
11 setup(**d)
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 # Revision $Id$
33
34 """
35 Library and tools support unit testing in ROS.
36 """
37
38 # NOTE: while this makes some forward references to conventions used
39 # in rostest, it does not use rostest itself.
40 import os
41
42 from .core import xml_results_file, rostest_name_from_path, create_xml_runner, XML_OUTPUT_FLAG
43 from . import junitxml
44 from .baretest import print_runner_summary, print_unittest_summary
45 from .pyunit import unitrun
46
47 __version__ = '1.7.0'
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2010, 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 # Revision $Id$
33
34 """
35 rostest implementation of running bare (gtest-compatible) unit test
36 executables. These do not run in a ROS environment.
37 """
38
39 import os
40 import cStringIO
41 import unittest
42 import time
43 import signal
44 import subprocess
45 import traceback
46
47 import rospkg
48
49 from .core import xml_results_file, rostest_name_from_path, create_xml_runner, printlog, printerrlog, printlog_bold
50
51 from . import pmon
52 from . import junitxml
53
54 BARE_TIME_LIMIT = 60.
55 TIMEOUT_SIGINT = 15.0 #seconds
56 TIMEOUT_SIGTERM = 2.0 #seconds
57
58 class TestTimeoutException(Exception): pass
59
60 class BareTestCase(unittest.TestCase):
61
62 def __init__(self, exe, args, retry=0, time_limit=None, test_name=None, text_mode=False, package_name=None):
63 """
64 @param exe: path to executable to run
65 @type exe: str
66 @param args: arguments to exe
67 @type args: [str]
68 @type retry: int
69 @param time_limit: (optional) time limit for test. Defaults to BARE_TIME_LIMIT.
70 @type time_limit: float
71 @param test_name: (optional) override automatically generated test name
72 @type test_name: str
73 @param package_name: (optional) override automatically inferred package name
74 @type package_name: str
75 """
76 super(BareTestCase, self).__init__()
77 self.text_mode = text_mode
78 if package_name:
79 self.package = package_name
80 else:
81 self.package = rospkg.get_package_name(exe)
82 self.exe = os.path.abspath(exe)
83 if test_name is None:
84 self.test_name = os.path.basename(exe)
85 else:
86 self.test_name = test_name
87
88 # invoke pyunit tests with python executable
89 if self.exe.endswith('.py'):
90 self.args = ['python', self.exe] + args
91 else:
92 self.args = [self.exe] + args
93 if text_mode:
94 self.args = self.args + ['--text']
95
96 self.retry = retry
97 self.time_limit = time_limit or BARE_TIME_LIMIT
98 self.pmon = None
99 self.results = junitxml.Result(self.test_name)
100
101 def setUp(self):
102 self.pmon = pmon.start_process_monitor()
103
104 def tearDown(self):
105 if self.pmon is not None:
106 pmon.shutdown_process_monitor(self.pmon)
107 self.pmon = None
108
109 def runTest(self):
110 self.failIf(self.package is None, "unable to determine package of executable")
111
112 done = False
113 while not done:
114 test_name = self.test_name
115
116 printlog("Running test [%s]", test_name)
117
118 #setup the test
119 # - we pass in the output test_file name so we can scrape it
120 test_file = xml_results_file(self.package, test_name, False)
121 if os.path.exists(test_file):
122 printlog("removing previous test results file [%s]", test_file)
123 os.remove(test_file)
124
125 self.args.append('--gtest_output=xml:%s'%test_file)
126
127 # run the test, blocks until completion
128 printlog("running test %s"%test_name)
129 timeout_failure = False
130
131 run_id = None
132 #TODO: really need different, non-node version of LocalProcess instead of these extra args
133 process = LocalProcess(run_id, self.package, self.test_name, self.args, os.environ, False, cwd='cwd', is_node=False)
134
135 pm = self.pmon
136 pm.register(process)
137 success = process.start()
138 self.assert_(success, "test failed to start")
139
140 #poll until test terminates or alloted time exceed
141 timeout_t = time.time() + self.time_limit
142 try:
143 while process.is_alive():
144 #test fails on timeout
145 if time.time() > timeout_t:
146 raise TestTimeoutException("test max time allotted")
147 time.sleep(0.1)
148
149 except TestTimeoutException, e:
150 if self.retry:
151 timeout_failure = True
152 else:
153 raise
154
155 if not timeout_failure:
156 printlog("test [%s] finished"%test_name)
157 else:
158 printerrlog("test [%s] timed out"%test_name)
159
160
161 if self.text_mode:
162 results = self.results
163 elif not self.text_mode:
164 # load in test_file
165 if not timeout_failure:
166 self.assert_(os.path.isfile(test_file), "test [%s] did not generate test results"%test_name)
167 printlog("test [%s] results are in [%s]", test_name, test_file)
168 results = junitxml.read(test_file, test_name)
169 test_fail = results.num_errors or results.num_failures
170 else:
171 test_fail = True
172
173 if self.retry > 0 and test_fail:
174 self.retry -= 1
175 printlog("test [%s] failed, retrying. Retries left: %s"%(test_name, self.retry))
176 else:
177 done = True
178 self.results = results
179 printlog("test [%s] results summary: %s errors, %s failures, %s tests",
180 test_name, results.num_errors, results.num_failures, results.num_tests)
181
182 printlog("[ROSTEST] test [%s] done", test_name)
183
184
185 #TODO: this is a straight copy from roslaunch. Need to reduce, refactor
186 class LocalProcess(pmon.Process):
187 """
188 Process launched on local machine
189 """
190
191 def __init__(self, run_id, package, name, args, env, log_output, respawn=False, required=False, cwd=None, is_node=True):
192 """
193 @param run_id: unique run ID for this roslaunch. Used to
194 generate log directory location. run_id may be None if this
195 feature is not being used.
196 @type run_id: str
197 @param package: name of package process is part of
198 @type package: str
199 @param name: name of process
200 @type name: str
201 @param args: list of arguments to process
202 @type args: [str]
203 @param env: environment dictionary for process
204 @type env: {str : str}
205 @param log_output: if True, log output streams of process
206 @type log_output: bool
207 @param respawn: respawn process if it dies (default is False)
208 @type respawn: bool
209 @param cwd: working directory of process, or None
210 @type cwd: str
211 @param is_node: (optional) if True, process is ROS node and accepts ROS node command-line arguments. Default: True
212 @type is_node: False
213 """
214 super(LocalProcess, self).__init__(package, name, args, env, respawn, required)
215 self.run_id = run_id
216 self.popen = None
217 self.log_output = log_output
218 self.started = False
219 self.stopped = False
220 self.cwd = cwd
221 self.log_dir = None
222 self.pid = -1
223 self.is_node = is_node
224
225 # NOTE: in the future, info() is going to have to be sufficient for relaunching a process
226 def get_info(self):
227 """
228 Get all data about this process in dictionary form
229 """
230 info = super(LocalProcess, self).get_info()
231 info['pid'] = self.pid
232 if self.run_id:
233 info['run_id'] = self.run_id
234 info['log_output'] = self.log_output
235 if self.cwd is not None:
236 info['cwd'] = self.cwd
237 return info
238
239 def _configure_logging(self):
240 """
241 Configure logging of node's log file and stdout/stderr
242 @return: stdout log file name, stderr log file
243 name. Values are None if stdout/stderr are not logged.
244 @rtype: str, str
245 """
246 log_dir = rospkg.get_log_dir(env=os.environ)
247 if self.run_id:
248 log_dir = os.path.join(log_dir, self.run_id)
249 if not os.path.exists(log_dir):
250 try:
251 os.makedirs(log_dir)
252 except OSError, (errno, msg):
253 if errno == 13:
254 raise RLException("unable to create directory for log file [%s].\nPlease check permissions."%log_dir)
255 else:
256 raise RLException("unable to create directory for log file [%s]: %s"%(log_dir, msg))
257 # #973: save log dir for error messages
258 self.log_dir = log_dir
259
260 # send stdout/stderr to file. in the case of respawning, we have to
261 # open in append mode
262 # note: logfileerr: disabling in favor of stderr appearing in the console.
263 # will likely reinstate once roserr/rosout is more properly used.
264 logfileout = logfileerr = None
265
266 if self.log_output:
267 outf, errf = [os.path.join(log_dir, '%s-%s.log'%(self.name, n)) for n in ['stdout', 'stderr']]
268 if self.respawn:
269 mode = 'a'
270 else:
271 mode = 'w'
272 logfileout = open(outf, mode)
273 if is_child_mode():
274 logfileerr = open(errf, mode)
275
276 # #986: pass in logfile name to node
277 node_log_file = log_dir
278 if self.is_node:
279 # #1595: on respawn, these keep appending
280 self.args = _cleanup_remappings(self.args, '__log:=')
281 self.args.append("__log:=%s"%os.path.join(log_dir, "%s.log"%self.name))
282
283 return logfileout, logfileerr
284
285 def start(self):
286 """
287 Start the process.
288
289 @raise pmon.FatalProcessLaunch: if process cannot be started and it
290 is not likely to ever succeed
291 """
292 super(LocalProcess, self).start()
293 try:
294 self.lock.acquire()
295 self.started = self.stopped = False
296
297 full_env = self.env
298
299 # _configure_logging() can mutate self.args
300 try:
301 logfileout, logfileerr = self._configure_logging()
302 except Exception as e:
303 printerrlog("[%s] ERROR: unable to configure logging [%s]"%(self.name, str(e)))
304 # it's not safe to inherit from this process as
305 # rostest changes stdout to a StringIO, which is not a
306 # proper file.
307 logfileout, logfileerr = subprocess.PIPE, subprocess.PIPE
308
309 if self.cwd == 'node':
310 cwd = os.path.dirname(self.args[0])
311 elif self.cwd == 'cwd':
312 cwd = os.getcwd()
313 elif self.cwd == 'ros-root':
314 cwd = get_ros_root()
315 else:
316 cwd = rospkg.get_ros_home()
317
318 try:
319 self.popen = subprocess.Popen(self.args, cwd=cwd, stdout=logfileout, stderr=logfileerr, env=full_env, close_fds=True, preexec_fn=os.setsid)
320 except OSError, (errno, msg):
321 self.started = True # must set so is_alive state is correct
322 if errno == 8: #Exec format error
323 raise pmon.FatalProcessLaunch("Unable to launch [%s]. \nIf it is a script, you may be missing a '#!' declaration at the top."%self.name)
324 elif errno == 2: #no such file or directory
325 raise pmon.FatalProcessLaunch("""Roslaunch got a '%s' error while attempting to run:
326
327 %s
328
329 Please make sure that all the executables in this command exist and have
330 executable permission. This is often caused by a bad launch-prefix."""%(msg, ' '.join(self.args)))
331 else:
332 raise pmon.FatalProcessLaunch("unable to launch [%s]: %s"%(' '.join(self.args), msg))
333
334 self.started = True
335 # Check that the process is either still running (poll returns
336 # None) or that it completed successfully since when we
337 # launched it above (poll returns the return code, 0).
338 poll_result = self.popen.poll()
339 if poll_result is None or poll_result == 0:
340 self.pid = self.popen.pid
341 printlog_bold("process[%s]: started with pid [%s]"%(self.name, self.pid))
342 return True
343 else:
344 printerrlog("failed to start local process: %s"%(' '.join(self.args)))
345 return False
346 finally:
347 self.lock.release()
348
349 def is_alive(self):
350 """
351 @return: True if process is still running
352 @rtype: bool
353 """
354 if not self.started: #not started yet
355 return True
356 if self.stopped or self.popen is None:
357 return False
358 self.exit_code = self.popen.poll()
359 if self.exit_code is not None:
360 return False
361 return True
362
363 def get_exit_description(self):
364 """
365 @return: human-readable description of exit state
366 @rtype: str
367 """
368 # #973: include location of output location in message
369 if self.exit_code is not None:
370 if self.exit_code:
371 if self.log_dir:
372 return 'process has died [pid %s, exit code %s].\nlog files: %s*.log'%(self.pid, self.exit_code, os.path.join(self.log_dir, self.name))
373 else:
374 return 'process has died [pid %s, exit code %s]'%(self.pid, self.exit_code)
375 else:
376 if self.log_dir:
377 return 'process has finished cleanly.\nlog file: %s*.log'%(os.path.join(self.log_dir, self.name))
378 else:
379 return 'process has finished cleanly'
380 else:
381 return 'process has died'
382
383 def _stop_unix(self, errors):
384 """
385 UNIX implementation of process killing
386
387 @param errors: error messages. stop() will record messages into this list.
388 @type errors: [str]
389 """
390 self.exit_code = self.popen.poll()
391 if self.exit_code is not None:
392 #print "process[%s].stop(): process has already returned %s"%(self.name, self.exit_code)
393 self.popen = None
394 self.stopped = True
395 return
396
397 pid = self.popen.pid
398 pgid = os.getpgid(pid)
399
400 try:
401 # Start with SIGINT and escalate from there.
402 os.killpg(pgid, signal.SIGINT)
403 timeout_t = time.time() + TIMEOUT_SIGINT
404 retcode = self.popen.poll()
405 while time.time() < timeout_t and retcode is None:
406 time.sleep(0.1)
407 retcode = self.popen.poll()
408 # Escalate non-responsive process
409 if retcode is None:
410 printerrlog("[%s] escalating to SIGTERM"%self.name)
411 timeout_t = time.time() + TIMEOUT_SIGTERM
412 os.killpg(pgid, signal.SIGTERM)
413 retcode = self.popen.poll()
414 while time.time() < timeout_t and retcode is None:
415 time.sleep(0.2)
416 retcode = self.popen.poll()
417 if retcode is None:
418 printerrlog("[%s] escalating to SIGKILL"%self.name)
419 errors.append("process[%s, pid %s]: required SIGKILL. May still be running."%(self.name, pid))
420 try:
421 os.killpg(pgid, signal.SIGKILL)
422 # #2096: don't block on SIGKILL, because this results in more orphaned processes overall
423 except OSError as e:
424 if e.args[0] == 3:
425 printerrlog("no [%s] process with pid [%s]"%(self.name, pid))
426 else:
427 printerrlog("errors shutting down [%s]: %s"%(self.name, e))
428 finally:
429 self.popen = None
430
431 def stop(self, errors=[]):
432 """
433 Stop the process. Record any significant error messages in the errors parameter
434
435 @param errors: error messages. stop() will record messages into this list.
436 @type errors: [str]
437 """
438 super(LocalProcess, self).stop(errors)
439 self.lock.acquire()
440 try:
441 try:
442 if self.popen is None:
443 return
444 #NOTE: currently POSIX-only. Need to add in Windows code once I have a test environment:
445 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/347462
446 self._stop_unix(errors)
447 except:
448 printerrlog("[%s] EXCEPTION %s"%(self.name, traceback.format_exc()))
449 finally:
450 self.stopped = True
451 self.lock.release()
452
453 def print_runner_summary(runner_results, junit_results, runner_name='ROSUNIT'):
454 """
455 Print summary of runner results and actual test results to
456 stdout. For rosunit and rostest, the test is wrapped in an
457 external runner. The results from this runner are important if the
458 runner itself has a failure.
459
460 @param runner_result: unittest runner result object
461 @type runner_result: _XMLTestResult
462 @param junit_results: Parsed JUnit test results
463 @type junit_results: rosunit.junitxml.Result
464 """
465 # we have two separate result objects, which can be a bit
466 # confusing. 'result' counts successful _running_ of tests
467 # (i.e. doesn't check for actual test success). The 'r' result
468 # object contains results of the actual tests.
469
470 buff = cStringIO.StringIO()
471
472 buff.write("[%s]"%(runner_name)+'-'*71+'\n\n')
473 for tc_result in junit_results.test_case_results:
474 buff.write(tc_result.description)
475 for tc_result in runner_results.failures:
476 buff.write("[%s][failed]\n"%tc_result[0]._testMethodName)
477
478 buff.write('\nSUMMARY\n')
479 if runner_results.wasSuccessful() and (junit_results.num_errors + junit_results.num_failures) == 0:
480 buff.write("\033[32m * RESULT: SUCCESS\033[0m\n")
481 else:
482 buff.write("\033[1;31m * RESULT: FAIL\033[0m\n")
483
484 # TODO: still some issues with the numbers adding up if tests fail to launch
485
486 # number of errors from the inner tests, plus add in count for tests
487 # that didn't run properly ('result' object).
488 buff.write(" * TESTS: %s\n"%junit_results.num_tests)
489 num_errors = junit_results.num_errors+len(runner_results.errors)
490 if num_errors:
491 buff.write("\033[1;31m * ERRORS: %s\033[0m\n"%num_errors)
492 else:
493 buff.write(" * ERRORS: 0\n")
494 num_failures = junit_results.num_failures+len(runner_results.failures)
495 if num_failures:
496 buff.write("\033[1;31m * FAILURES: %s\033[0m\n"%num_failures)
497 else:
498 buff.write(" * FAILURES: 0\n")
499
500 if runner_results.failures:
501 buff.write("\nERROR: The following tests failed to run:\n")
502 for tc_result in runner_results.failures:
503 buff.write(" * " +tc_result[0]._testMethodName + "\n")
504
505 print buff.getvalue()
506
507 def _format_errors(errors):
508 formatted = []
509 for e in errors:
510 if '_testMethodName' in e[0].__dict__:
511 formatted.append(e[0]._testMethodName)
512 elif 'description' in e[0].__dict__:
513 formatted.append('%s: %s\n' % (str(e[0].description), str(e[1])))
514 else:
515 formatted.append(str(e[0].__dict__))
516 return formatted
517
518 def print_unittest_summary(result):
519 """
520 Print summary of python unittest result to stdout
521 @param result: test results
522 """
523 buff = cStringIO.StringIO()
524 buff.write("-------------------------------------------------------------\nSUMMARY:\n")
525 if result.wasSuccessful():
526 buff.write("\033[32m * RESULT: SUCCESS\033[0m\n")
527 else:
528 buff.write(" * RESULT: FAIL\n")
529 buff.write(" * TESTS: %s\n"%result.testsRun)
530 buff.write(" * ERRORS: %s [%s]\n"%(len(result.errors), ', '.join(_format_errors(result.errors))))
531 buff.write(" * FAILURES: %s [%s]\n"%(len(result.failures), ', '.join(_format_errors(result.failures))))
532 print buff.getvalue()
533
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 # Revision $Id$
33
34 import os
35 import sys
36 import logging
37
38 import rospkg
39
40 from .xmlrunner import XMLTestRunner
41
42 XML_OUTPUT_FLAG = '--gtest_output=xml:' #use gtest-compatible flag
43
44 def printlog(msg, *args):
45 if args:
46 msg = msg%args
47 print "[ROSUNIT]"+msg
48
49 def printlog_bold(msg, *args):
50 if args:
51 msg = msg%args
52 print '\033[1m[ROSUNIT]%s\033[0m'%msg
53
54 def printerrlog(msg, *args):
55 if args:
56 msg = msg%args
57 print >> sys.stderr, "[ROSUNIT]"+msg
58
59 # this is a copy of the roslogging utility. it's been moved here as it is a common
60 # routine for programs using accessing ROS directories
61 def makedirs_with_parent_perms(p):
62 """
63 Create the directory using the permissions of the nearest
64 (existing) parent directory. This is useful for logging, where a
65 root process sometimes has to log in the user's space.
66 @param p: directory to create
67 @type p: str
68 """
69 p = os.path.abspath(p)
70 parent = os.path.dirname(p)
71 # recurse upwards, checking to make sure we haven't reached the
72 # top
73 if not os.path.exists(p) and p and parent != p:
74 makedirs_with_parent_perms(parent)
75 s = os.stat(parent)
76 os.mkdir(p)
77
78 # if perms of new dir don't match, set anew
79 s2 = os.stat(p)
80 if s.st_uid != s2.st_uid or s.st_gid != s2.st_gid:
81 os.chown(p, s.st_uid, s.st_gid)
82 if s.st_mode != s2.st_mode:
83 os.chmod(p, s.st_mode)
84
85 def xml_results_file(test_pkg, test_name, is_rostest=False):
86 """
87 @param test_pkg: name of test's package
88 @type test_pkg: str
89 @param test_name str: name of test
90 @type test_name: str
91 @param is_rostest: True if the results file is for a rostest-generated unit instance
92 @type is_rostest: bool
93 @return: name of xml results file for specified test
94 @rtype: str
95 """
96 test_dir = os.path.join(rospkg.get_test_results_dir(), test_pkg)
97 if not os.path.exists(test_dir):
98 try:
99 makedirs_with_parent_perms(test_dir)
100 except OSError:
101 raise IOError("cannot create test results directory [%s]. Please check permissions."%(test_dir))
102
103 # #576: strip out chars that would bork the filename
104 # this is fairly primitive, but for now just trying to catch some common cases
105 for c in ' "\'&$!`/\\':
106 if c in test_name:
107 test_name = test_name.replace(c, '_')
108 if is_rostest:
109 return os.path.join(test_dir, 'rostest-%s.xml'%test_name)
110 else:
111 return os.path.join(test_dir, 'rosunit-%s.xml'%test_name)
112
113 def rostest_name_from_path(pkg_dir, test_file):
114 """
115 Derive name of rostest based on file name/path. rostest follows a
116 certain convention defined above.
117
118 @return: name of test
119 @rtype: str
120 """
121 test_file_abs = os.path.abspath(test_file)
122 if test_file_abs.startswith(pkg_dir):
123 # compute package-relative path
124 test_file = test_file_abs[len(pkg_dir):]
125 if test_file[0] == os.sep:
126 test_file = test_file[1:]
127 outname = test_file.replace(os.sep, '_')
128 if '.' in outname:
129 outname = outname[:outname.rfind('.')]
130 return outname
131
132 def create_xml_runner(test_pkg, test_name, results_file=None, is_rostest=False):
133 """
134 Create the unittest test runner with XML output
135 @param test_pkg: package name
136 @type test_pkg: str
137 @param test_name: test name
138 @type test_name: str
139 @param is_rostest: if True, use naming scheme for rostest itself instead of individual unit test naming
140 @type is_rostest: bool
141 """
142 test_name = os.path.basename(test_name)
143 # determine output xml file name
144 if not results_file:
145 results_file = xml_results_file(test_pkg, test_name, is_rostest)
146 test_dir = os.path.abspath(os.path.dirname(results_file))
147 if not os.path.exists(test_dir):
148 try:
149 makedirs_with_parent_perms(test_dir) #NOTE: this will pass up an error exception if it fails
150 except OSError:
151 raise IOError("cannot create test results directory [%s]. Please check permissions."%(test_dir))
152
153 elif os.path.isfile(test_dir):
154 raise Exception("ERROR: cannot run test suite, file is preventing creation of test dir: %s"%test_dir)
155
156 print "[ROSUNIT] Outputting test results to %s"%results_file
157 outstream = open(results_file, 'w')
158 outstream.write('<?xml version="1.0" encoding="utf-8"?>\n')
159 return XMLTestRunner(stream=outstream)
160
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id$
34
35 """
36 Library for reading and manipulating Ant JUnit XML result files.
37 """
38
39 from __future__ import print_function
40
41 import os
42 import sys
43 import cStringIO
44 import string
45 import codecs
46 import re
47
48 from xml.dom.minidom import parse, parseString
49 from xml.dom import Node as DomNode
50
51 import rospkg
52
53 class TestInfo(object):
54 """
55 Common container for 'error' and 'failure' results
56 """
57
58 def __init__(self, type_, text):
59 """
60 @param type_: type attribute from xml
61 @type type_: str
62 @param text: text property from xml
63 @type text: str
64 """
65 self.type = type_
66 self.text = text
67
68 class TestError(TestInfo):
69 """
70 'error' result container
71 """
72 def xml(self):
73 return u'<error type="%s"><![CDATA[%s]]></error>'%(self.type, self.text)
74
75 class TestFailure(TestInfo):
76 """
77 'failure' result container
78 """
79 def xml(self):
80 return u'<failure type="%s"><![CDATA[%s]]></failure>'%(self.type, self.text)
81
82
83 class TestCaseResult(object):
84 """
85 'testcase' result container
86 """
87
88 def __init__(self, name):
89 """
90 @param name: name of testcase
91 @type name: str
92 """
93 self.name = name
94 self.failures = []
95 self.errors = []
96 self.time = 0.0
97 self.classname = ''
98
99 def _passed(self):
100 """
101 @return: True if test passed
102 @rtype: bool
103 """
104 return not self.errors and not self.failures
105 ## bool: True if test passed without errors or failures
106 passed = property(_passed)
107
108 def _failure_description(self):
109 """
110 @return: description of testcase failure
111 @rtype: str
112 """
113 if self.failures:
114 tmpl = "[%s][FAILURE]"%self.name
115 tmpl = tmpl + '-'*(80-len(tmpl))
116 tmpl = tmpl+"\n%s\n"+'-'*80+"\n\n"
117 return '\n'.join(tmpl%x.text for x in self.failures)
118 return ''
119
120 def _error_description(self):
121 """
122 @return: description of testcase error
123 @rtype: str
124 """
125 if self.errors:
126 tmpl = "[%s][ERROR]"%self.name
127 tmpl = tmpl + '-'*(80-len(tmpl))
128 tmpl = tmpl+"\n%s\n"+'-'*80+"\n\n"
129 return '\n'.join(tmpl%x.text for x in self.errors)
130 return ''
131
132 def _description(self):
133 """
134 @return: description of testcase result
135 @rtype: str
136 """
137 if self.passed:
138 return "[%s][passed]\n"%self.name
139 else:
140 return self._failure_description()+\
141 self._error_description()
142 ## str: printable description of testcase result
143 description = property(_description)
144 def add_failure(self, failure):
145 """
146 @param failure TestFailure
147 """
148 self.failures.append(failure)
149
150 def add_error(self, error):
151 """
152 @param failure TestError
153 """
154 self.errors.append(error)
155
156 def xml(self):
157 return u' <testcase classname="%s" name="%s" time="%s">\n'%(self.classname, self.name, self.time)+\
158 '\n '.join([f.xml() for f in self.failures])+\
159 '\n '.join([e.xml() for e in self.errors])+\
160 ' </testcase>'
161
162 class Result(object):
163 __slots__ = ['name', 'num_errors', 'num_failures', 'num_tests', \
164 'test_case_results', 'system_out', 'system_err', 'time']
165 def __init__(self, name, num_errors=0, num_failures=0, num_tests=0):
166 self.name = name
167 self.num_errors = num_errors
168 self.num_failures = num_failures
169 self.num_tests = num_tests
170 self.test_case_results = []
171 self.system_out = ''
172 self.system_err = ''
173 self.time = 0.0
174
175 def accumulate(self, r):
176 """
177 Add results from r to this result
178 @param r: results to aggregate with this result
179 @type r: Result
180 """
181 self.num_errors += r.num_errors
182 self.num_failures += r.num_failures
183 self.num_tests += r.num_tests
184 self.time += r.time
185 self.test_case_results.extend(r.test_case_results)
186 if r.system_out:
187 self.system_out += '\n'+r.system_out
188 if r.system_err:
189 self.system_err += '\n'+r.system_err
190
191 def add_test_case_result(self, r):
192 """
193 Add results from a testcase to this result container
194 @param r: TestCaseResult
195 @type r: TestCaseResult
196 """
197 self.test_case_results.append(r)
198
199 def xml(self):
200 """
201 @return: document as unicode (UTF-8 declared) XML according to Ant JUnit spec
202 """
203 return u'<?xml version="1.0" encoding="utf-8"?>'+\
204 '<testsuite name="%s" tests="%s" errors="%s" failures="%s" time="%s">'%\
205 (self.name, self.num_tests, self.num_errors, self.num_failures, self.time)+\
206 '\n'.join([tc.xml() for tc in self.test_case_results])+\
207 ' <system-out><![CDATA[%s]]></system-out>'%self.system_out+\
208 ' <system-err><![CDATA[%s]]></system-err>'%self.system_err+\
209 '</testsuite>'
210
211 def _text(tag):
212 return reduce(lambda x, y: x + y, [c.data for c in tag.childNodes if c.nodeType in [DomNode.TEXT_NODE, DomNode.CDATA_SECTION_NODE]], "").strip()
213
214 def _load_suite_results(test_suite_name, test_suite, result):
215 nodes = [n for n in test_suite.childNodes \
216 if n.nodeType == DomNode.ELEMENT_NODE]
217 for node in nodes:
218 name = node.tagName
219 if name == 'testsuite':
220 # for now we flatten this hierarchy
221 _load_suite_results(test_suite_name, node, result)
222 elif name == 'system-out':
223 if _text(node):
224 system_out = "[%s] stdout"%test_suite_name + "-"*(71-len(test_suite_name))
225 system_out += '\n'+_text(node)
226 result.system_out += system_out
227 elif name == 'system-err':
228 if _text(node):
229 system_err = "[%s] stderr"%test_suite_name + "-"*(71-len(test_suite_name))
230 system_err += '\n'+_text(node)
231 result.system_err += system_err
232 elif name == 'testcase':
233 name = node.getAttribute('name') or 'unknown'
234 classname = node.getAttribute('classname') or 'unknown'
235
236 # mangle the classname for some sense of uniformity
237 # between rostest/unittest/gtest
238 if '__main__.' in classname:
239 classname = classname[classname.find('__main__.')+9:]
240 if classname == 'rostest.rostest.RosTest':
241 classname = 'rostest'
242 elif not classname.startswith(result.name):
243 classname = "%s.%s"%(result.name,classname)
244
245 time = float(node.getAttribute('time')) or 0.0
246 tc_result = TestCaseResult("%s/%s"%(test_suite_name,name))
247 tc_result.classname = classname
248 tc_result.time = time
249 result.add_test_case_result(tc_result)
250 for d in [n for n in node.childNodes \
251 if n.nodeType == DomNode.ELEMENT_NODE]:
252 # convert 'message' attributes to text elements to keep
253 # python unittest and gtest consistent
254 if d.tagName == 'failure':
255 message = d.getAttribute('message') or ''
256 text = _text(d) or message
257 x = TestFailure(d.getAttribute('type') or '', text)
258 tc_result.add_failure(x)
259 elif d.tagName == 'error':
260 message = d.getAttribute('message') or ''
261 text = _text(d) or message
262 x = TestError(d.getAttribute('type') or '', text)
263 tc_result.add_error(x)
264
265 ## #603: unit test suites are not good about screening out illegal
266 ## unicode characters. This little recipe I from http://boodebr.org/main/python/all-about-python-and-unicode#UNI_XML
267 ## screens these out
268 RE_XML_ILLEGAL = u'([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])' + \
269 u'|' + \
270 u'([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])' % \
271 (unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
272 unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
273 unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff))
274 _safe_xml_regex = re.compile(RE_XML_ILLEGAL)
275
276 def _read_file_safe_xml(test_file):
277 """
278 read in file, screen out unsafe unicode characters
279 """
280 f = None
281 try:
282 # this is ugly, but the files in question that are problematic
283 # do not declare unicode type.
284 if not os.path.isfile(test_file):
285 raise Exception("test file does not exist")
286 try:
287 f = codecs.open(test_file, "r", "utf-8" )
288 x = f.read()
289 except:
290 if f is not None:
291 f.close()
292 f = codecs.open(test_file, "r", "iso8859-1" )
293 x = f.read()
294
295 for match in _safe_xml_regex.finditer(x):
296 x = x[:match.start()] + "?" + x[match.end():]
297 return x.encode("utf-8")
298 finally:
299 if f is not None:
300 f.close()
301
302 def read(test_file, test_name):
303 """
304 Read in the test_result file
305 @param test_file: test file path
306 @type test_file: str
307 @param test_name: name of test
308 @type test_name: str
309 @return: test results
310 @rtype: Result
311 """
312 try:
313 xml_str = _read_file_safe_xml(test_file)
314 if not xml_str.strip():
315 print("WARN: test result file is empty [%s]"%(test_file))
316 return Result(test_name, 0, 0, 0)
317 test_suites = parseString(xml_str).getElementsByTagName('testsuite')
318 except Exception as e:
319 print("WARN: cannot read test result file [%s]: %s"%(test_file, str(e)))
320 return Result(test_name, 0, 0, 0)
321 if not test_suites:
322 print("WARN: test result file [%s] contains no results"%(test_file))
323 return Result(test_name, 0, 0, 0)
324
325 results = Result(test_name, 0, 0, 0)
326 for index, test_suite in enumerate(test_suites):
327 # skip test suites which are already covered by a parent test suite
328 if index > 0 and test_suite.parentNode in test_suites[0:index]:
329 continue
330
331 #test_suite = test_suite[0]
332 vals = [test_suite.getAttribute(attr) for attr in ['errors', 'failures', 'tests']]
333 vals = [v or 0 for v in vals]
334 err, fail, tests = [string.atoi(val) for val in vals]
335
336 result = Result(test_name, err, fail, tests)
337 result.time = 0.0 if not len(test_suite.getAttribute('time')) else float(test_suite.getAttribute('time'))
338
339 # Create a prefix based on the test result filename. The idea is to
340 # disambiguate the case when tests of the same name are provided in
341 # different .xml files. We use the name of the parent directory
342 test_file_base = os.path.basename(os.path.dirname(os.path.abspath(test_file)))
343 fname = os.path.basename(test_file)
344 if fname.startswith('TEST-'):
345 fname = fname[5:]
346 if fname.endswith('.xml'):
347 fname = fname[:-4]
348 test_file_base = "%s.%s"%(test_file_base, fname)
349 _load_suite_results(test_file_base, test_suite, result)
350 results.accumulate(result)
351 return results
352
353 def read_all(filter_=[]):
354 """
355 Read in the test_results and aggregate into a single Result object
356 @param filter_: list of packages that should be processed
357 @type filter_: [str]
358 @return: aggregated result
359 @rtype: L{Result}
360 """
361 dir_ = rospkg.get_test_results_dir()
362 root_result = Result('ros', 0, 0, 0)
363 if not os.path.exists(dir_):
364 return root_result
365 for d in os.listdir(dir_):
366 if filter_ and not d in filter_:
367 continue
368 subdir = os.path.join(dir_, d)
369 if os.path.isdir(subdir):
370 for filename in os.listdir(subdir):
371 if filename.endswith('.xml'):
372 filename = os.path.join(subdir, filename)
373 result = read(filename, os.path.basename(subdir))
374 root_result.accumulate(result)
375 return root_result
376
377
378 def test_failure_junit_xml(test_name, message, stdout=None):
379 """
380 Generate JUnit XML file for a unary test suite where the test failed
381
382 @param test_name: Name of test that failed
383 @type test_name: str
384 @param message: failure message
385 @type message: str
386 @param stdout: stdout data to include in report
387 @type stdout: str
388 """
389 if not stdout:
390 return """<?xml version="1.0" encoding="UTF-8"?>
391 <testsuite tests="1" failures="1" time="1" errors="0" name="%s">
392 <testcase name="test_ran" status="run" time="1" classname="Results">
393 <failure message="%s" type=""/>
394 </testcase>
395 </testsuite>"""%(test_name, message)
396 else:
397 return """<?xml version="1.0" encoding="UTF-8"?>
398 <testsuite tests="1" failures="1" time="1" errors="0" name="%s">
399 <testcase name="test_ran" status="run" time="1" classname="Results">
400 <failure message="%s" type=""/>
401 </testcase>
402 <system-out><![CDATA[[
403 %s
404 ]]></system-out>
405 </testsuite>"""%(test_name, message, stdout)
406
407 def test_success_junit_xml(test_name):
408 """
409 Generate JUnit XML file for a unary test suite where the test succeeded.
410
411 @param test_name: Name of test that passed
412 @type test_name: str
413 """
414 return """<?xml version="1.0" encoding="UTF-8"?>
415 <testsuite tests="1" failures="0" time="1" errors="0" name="%s">
416 <testcase name="test_ran" status="run" time="1" classname="Results">
417 </testcase>
418 </testsuite>"""%(test_name)
419
420 def print_summary(junit_results, runner_name='ROSUNIT'):
421 """
422 Print summary of junitxml results to stdout.
423 """
424 # we have two separate result objects, which can be a bit
425 # confusing. 'result' counts successful _running_ of tests
426 # (i.e. doesn't check for actual test success). The 'r' result
427 # object contains results of the actual tests.
428
429 buff = cStringIO.StringIO()
430 buff.write("[%s]"%runner_name+'-'*71+'\n\n')
431 for tc_result in junit_results.test_case_results:
432 buff.write(tc_result.description)
433
434 buff.write('\nSUMMARY\n')
435 if (junit_results.num_errors + junit_results.num_failures) == 0:
436 buff.write("\033[32m * RESULT: SUCCESS\033[0m\n")
437 else:
438 buff.write("\033[1;31m * RESULT: FAIL\033[0m\n")
439
440 # TODO: still some issues with the numbers adding up if tests fail to launch
441
442 # number of errors from the inner tests, plus add in count for tests
443 # that didn't run properly ('result' object).
444 buff.write(" * TESTS: %s\n"%junit_results.num_tests)
445 num_errors = junit_results.num_errors
446 if num_errors:
447 buff.write("\033[1;31m * ERRORS: %s\033[0m\n"%num_errors)
448 else:
449 buff.write(" * ERRORS: 0\n")
450 num_failures = junit_results.num_failures
451 if num_failures:
452 buff.write("\033[1;31m * FAILURES: %s\033[0m\n"%num_failures)
453 else:
454 buff.write(" * FAILURES: 0\n")
455
456 print(buff.getvalue())
457
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 # Revision $Id$
33
34 """
35 Process monitor
36 """
37
38 from __future__ import with_statement
39
40 import os
41 import sys
42 import time
43 import traceback
44 import Queue
45 import atexit
46 from threading import Thread, RLock, Lock
47
48 from .core import printlog, printlog_bold, printerrlog
49
50 class PmonException(Exception): pass
51
52 class FatalProcessLaunch(PmonException):
53 """
54 Exception to indicate that a process launch has failed in a fatal
55 manner (i.e. relaunch is unlikely to succeed)
56 """
57 pass
58
59 # start/shutdown ################################################
60
61 _pmons = []
62 _pmon_counter = 0
63 _shutting_down = False
64 def start_process_monitor():
65 global _pmon_counter
66 if _shutting_down:
67 return None
68 _pmon_counter += 1
69 name = "ProcessMonitor-%s"%_pmon_counter
70 process_monitor = ProcessMonitor(name)
71 with _shutdown_lock:
72 # prevent race condition with pmon_shutdown() being triggered
73 # as we are starting a ProcessMonitor (i.e. user hits ctrl-C
74 # during startup)
75 _pmons.append(process_monitor)
76 process_monitor.start()
77 return process_monitor
78
79 def shutdown_process_monitor(process_monitor):
80 """
81 @param process_monitor: process monitor to kill
82 @type process_monitor: L{ProcessMonitor}
83 @return: True if process_monitor was successfully
84 shutdown. False if it could not be shutdown cleanly or if there is
85 a problem with process_monitor
86 parameter. shutdown_process_monitor() does not throw any exceptions
87 as this is shutdown-critical code.
88 @rtype: bool
89 """
90 try:
91 if process_monitor is None or process_monitor.is_shutdown:
92 return False
93
94 process_monitor.shutdown()
95 process_monitor.join(20.0)
96 if process_monitor.isAlive():
97 return False
98 else:
99 return True
100 except Exception, e:
101 return False
102
103 _shutdown_lock = Lock()
104 def pmon_shutdown():
105 global _pmons
106 with _shutdown_lock:
107 if not _pmons:
108 return
109 for p in _pmons:
110 shutdown_process_monitor(p)
111 del _pmons[:]
112
113 atexit.register(pmon_shutdown)
114
115 # ##############################################################
116
117 class Process(object):
118 """
119 Basic process representation for L{ProcessMonitor}. Must be subclassed
120 to provide actual start()/stop() implementations.
121 """
122
123 def __init__(self, package, name, args, env, respawn=False, required=False):
124 self.package = package
125 self.name = name
126 self.args = args
127 self.env = env
128 self.respawn = respawn
129 self.required = required
130 self.lock = Lock()
131 self.exit_code = None
132 # for keeping track of respawning
133 self.spawn_count = 0
134
135 def __str__(self):
136 return "Process<%s>"%(self.name)
137
138 # NOTE: get_info() is going to have to be sufficient for
139 # generating respawn requests, so we must be complete about it
140
141 def get_info(self):
142 """
143 Get all data about this process in dictionary form
144 @return: dictionary of all relevant process properties
145 @rtype: dict { str: val }
146 """
147 info = {
148 'spawn_count': self.spawn_count,
149 'args': self.args,
150 'env': self.env,
151 'package': self.package,
152 'name': self.name,
153 'alive': self.is_alive(),
154 'respawn': self.respawn,
155 'required': self.required,
156 }
157 if self.exit_code is not None:
158 info['exit_code'] = self.exit_code
159 return info
160
161 def start(self):
162 self.spawn_count += 1
163
164 def is_alive(self):
165 return False
166
167 def stop(self, errors=[]):
168 """
169 Stop the process. Record any significant error messages in the errors parameter
170
171 @param errors: error messages. stop() will record messages into this list.
172 @type errors: [str]
173 """
174 pass
175
176 def get_exit_description(self):
177 if self.exit_code is not None:
178 if self.exit_code:
179 return 'process has died [exit code %s]'%self.exit_code
180 else:
181 # try not to scare users about process exit
182 return 'process has finished cleanly'
183 else:
184 return 'process has died'
185
186 class DeadProcess(Process):
187 """
188 Container class to maintain information about a process that has died. This
189 container allows us to delete the actual Process but still maintain the metadata
190 """
191 def __init__(self, p):
192 super(DeadProcess, self).__init__(p.package, p.name, p.args, p.env, p.respawn)
193 self.exit_code = p.exit_code
194 self.lock = None
195 self.spawn_count = p.spawn_count
196 self.info = p.get_info()
197 def get_info(self):
198 return self.info
199 def start(self):
200 raise Exception("cannot call start on a dead process!")
201 def is_alive(self):
202 return False
203
204 class ProcessListener(object):
205 """
206 Listener class for L{ProcessMonitor}
207 """
208
209 def process_died(self, process_name, exit_code):
210 """
211 Notifies listener that process has died. This callback only
212 occurs for processes that die during normal process monitor
213 execution -- processes that are forcibly killed during
214 ProcessMonitor shutdown are not reported.
215 @param process_name: name of process
216 @type process_name: str
217 @param exit_code: exit code of process. If None, it means
218 that ProcessMonitor was unable to determine an exit code.
219 @type exit_code: int
220 """
221 pass
222
223 class ProcessMonitor(Thread):
224
225 def __init__(self, name="ProcessMonitor"):
226 Thread.__init__(self, name=name)
227 self.procs = []
228 self.plock = RLock()
229 self.is_shutdown = False
230 self.done = False
231 self.setDaemon(True)
232 self.listeners = []
233 self.dead_list = []
234 # #885: ensure core procs
235 self.core_procs = []
236 # #642: flag to prevent process monitor exiting prematurely
237 self._registrations_complete = False
238
239 def add_process_listener(self, l):
240 """
241 Listener for process events. MUST be called before
242 ProcessMonitor is running.See ProcessListener class.
243 @param l: listener instance
244 @type l: L{ProcessListener}
245 """
246 self.listeners.append(l)
247
248 def register(self, p):
249 """
250 Register process with L{ProcessMonitor}
251 @param p: Process
252 @type p: L{Process}
253 @raise PmonException: if process with same name is already registered
254 """
255 e = None
256 with self.plock:
257 if self.has_process(p.name):
258 e = PmonException("cannot add process with duplicate name '%s'"%p.name)
259 elif self.is_shutdown:
260 e = PmonException("cannot add process [%s] after process monitor has been shut down"%p.name)
261 else:
262 self.procs.append(p)
263 if e:
264 raise e
265
266 def register_core_proc(self, p):
267 """
268 Register core process with ProcessMonitor. Coreprocesses
269 have special shutdown semantics. They are killed after all
270 other processes, in reverse order in which they are added.
271 @param p Process
272 @type p: L{Process}
273 @raise PmonException: if process with same name is already registered
274 """
275 self.register(p)
276 self.core_procs.append(p)
277
278 def registrations_complete(self):
279 """
280 Inform the process monitor that registrations are complete.
281 After the registrations_complete flag is set, process monitor
282 will exit if there are no processes left to monitor.
283 """
284 self._registrations_complete = True
285
286 def unregister(self, p):
287 with self.plock:
288 self.procs.remove(p)
289
290 def has_process(self, name):
291 """
292 @return: True if process is still be monitored. If False, process
293 has died or was never registered with process
294 @rtype: bool
295 """
296 return len([p for p in self.procs if p.name == name]) > 0
297
298 def get_process(self, name):
299 """
300 @return: process registered under \a name, or None
301 @rtype: L{Process}
302 """
303 with self.plock:
304 v = [p for p in self.procs if p.name == name]
305 if v:
306 return v[0]
307
308 def kill_process(self, name):
309 """
310 Kill process that matches name. NOTE: a killed process will
311 continue to show up as active until the process monitor thread
312 has caught that it has died.
313 @param name: Process name
314 @type name: str
315 @return: True if a process named name was removed from
316 process monitor. A process is considered killed if its stop()
317 method was called.
318 @rtype: bool
319 """
320 if not isinstance(name, basestring):
321 raise PmonException("kill_process takes in a process name but was given: %s"%name)
322 printlog("[%s] kill requested"%name)
323 with self.plock:
324 p = self.get_process(name)
325 if p:
326 try:
327 # no need to accumulate errors, so pass in []
328 p.stop([])
329 except Exception as e:
330 printerrlog("Exception: %s"%(str(e)))
331 return True
332 else:
333 return False
334
335 def shutdown(self):
336 """
337 Shutdown the process monitor thread
338 """
339 self.is_shutdown = True
340
341 def get_active_names(self):
342 """
343 @return [str]: list of active process names
344 """
345 with self.plock:
346 retval = [p.name for p in self.procs]
347 return retval
348
349 def get_process_names_with_spawn_count(self):
350 """
351 @return: Two lists, where first
352 list of active process names along with the number of times
353 that process has been spawned. Second list contains dead process names
354 and their spawn count.
355 @rtype: [[(str, int),], [(str,int),]]
356 """
357 with self.plock:
358 actives = [(p.name, p.spawn_count) for p in self.procs]
359 deads = [(p.name, p.spawn_count) for p in self.dead_list]
360 retval = [actives, deads]
361 return retval
362
363 def run(self):
364 """
365 thread routine of the process monitor.
366 """
367 try:
368 #don't let exceptions bomb thread, interferes with exit
369 try:
370 self._run()
371 except:
372 traceback.print_exc()
373 finally:
374 self._post_run()
375
376 def _run(self):
377 """
378 Internal run loop of ProcessMonitor
379 """
380 plock = self.plock
381 dead = []
382 respawn = []
383 while not self.is_shutdown:
384 with plock: #copy self.procs
385 procs = self.procs[:]
386 if self.is_shutdown:
387 break
388
389 for p in procs:
390 try:
391 if not p.is_alive():
392 exit_code_str = p.get_exit_description()
393 if p.respawn:
394 printlog_bold("[%s] %s\nrespawning..."%(p.name, exit_code_str))
395 respawn.append(p)
396 elif p.required:
397 printerrlog('='*80+"REQUIRED process [%s] has died!\n%s\nInitiating shutdown!\n"%(p.name, exit_code_str)+'='*80)
398 self.is_shutdown = True
399 else:
400 if p.exit_code:
401 printerrlog("[%s] %s"%(p.name, exit_code_str))
402 else:
403 printlog_bold("[%s] %s"%(p.name, exit_code_str))
404 dead.append(p)
405
406 ## no need for lock as we require listeners be
407 ## added before process monitor is launched
408 for l in self.listeners:
409 l.process_died(p.name, p.exit_code)
410
411 except Exception, e:
412 traceback.print_exc()
413 #don't respawn as this is an internal error
414 dead.append(p)
415 if self.is_shutdown:
416 break #stop polling
417 for d in dead:
418 try:
419 self.unregister(d)
420 # stop process, don't accumulate errors
421 d.stop([])
422
423 # save process data to dead list
424 with plock:
425 self.dead_list.append(DeadProcess(d))
426 except Exception as e:
427 printerrlog("Exception: %s"%(str(e)))
428
429 # dead check is to make sure that ProcessMonitor at least
430 # waits until its had at least one process before exiting
431 if self._registrations_complete and dead and not self.procs and not respawn:
432 printlog("all processes on machine have died, roslaunch will exit")
433 self.is_shutdown = True
434 del dead[:]
435 for r in respawn:
436 try:
437 if self.is_shutdown:
438 break
439 printlog("[%s] restarting process"%r.name)
440 # stop process, don't accumulate errors
441 r.stop([])
442 r.start()
443 except:
444 traceback.print_exc()
445 del respawn[:]
446 time.sleep(0.1) #yield thread
447 #moved this to finally block of _post_run
448 #self._post_run() #kill all processes
449
450 def _post_run(self):
451 # this is already true entering, but go ahead and make sure
452 self.is_shutdown = True
453 # killall processes on run exit
454
455 q = Queue.Queue()
456 q.join()
457
458 with self.plock:
459 # make copy of core_procs for threadsafe usage
460 core_procs = self.core_procs[:]
461
462 # enqueue all non-core procs in reverse order for parallel kill
463 # #526/885: ignore core procs
464 [q.put(p) for p in reversed(self.procs) if not p in core_procs]
465
466 # use 10 workers
467 killers = []
468 for i in range(10):
469 t = _ProcessKiller(q, i)
470 killers.append(t)
471 t.start()
472
473 # wait for workers to finish
474 q.join()
475 shutdown_errors = []
476
477 # accumulate all the shutdown errors
478 for t in killers:
479 shutdown_errors.extend(t.errors)
480 del killers[:]
481
482 # #526/885: kill core procs last
483 # we don't want to parallelize this as the master has to be last
484 for p in reversed(core_procs):
485 _kill_process(p, shutdown_errors)
486
487 # delete everything except dead_list
488 with self.plock:
489 del core_procs[:]
490 del self.procs[:]
491 del self.core_procs[:]
492
493 self.done = True
494
495 if shutdown_errors:
496 printerrlog("Shutdown errors:\n"+'\n'.join([" * %s"%e for e in shutdown_errors]))
497
498 def _kill_process(p, errors):
499 """
500 Routine for kill Process p with appropriate logging to screen and logfile
501
502 @param p: process to kill
503 @type p: Process
504 @param errors: list of error messages from killed process
505 @type errors: [str]
506 """
507 try:
508 printlog("[%s] killing on exit"%p.name)
509 # we accumulate errors from each process so that we can print these at the end
510 p.stop(errors)
511 except Exception as e:
512 printerrlog("Exception: %s"%(str(e)))
513
514 class _ProcessKiller(Thread):
515
516 def __init__(self, q, i):
517 Thread.__init__(self, name="ProcessKiller-%s"%i)
518 self.q = q
519 self.errors = []
520
521 def run(self):
522 q = self.q
523 while not q.empty():
524 try:
525 p = q.get(False)
526 _kill_process(p, self.errors)
527 q.task_done()
528 except Queue.Empty:
529 pass
530
531
532
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 # Revision $Id$
33
34 """
35 Wrapper for running Python unittest within rosunit/rostest framework.
36 """
37 from __future__ import with_statement
38
39 import sys
40
41 from .core import create_xml_runner, XML_OUTPUT_FLAG
42 from .baretest import print_unittest_summary
43
44 def unitrun(package, test_name, test, sysargs=None, coverage_packages=None):
45 """
46 Wrapper routine from running python unitttests with
47 JUnit-compatible XML output. This is meant for unittests that do
48 not not need a running ROS graph (i.e. offline tests only).
49
50 This enables JUnit-compatible test reporting so that
51 test results can be reported to higher-level tools.
52
53 WARNING: unitrun() will trigger a sys.exit() on test failure in
54 order to properly exit with an error code. This routine is meant
55 to be used as a main() routine, not as a library.
56
57 @param package: name of ROS package that is running the test
58 @type package: str
59 @param coverage_packages: list of Python package to compute coverage results for. Defaults to package
60 @type coverage_packages: [str]
61 @param sysargs: (optional) alternate sys.argv
62 @type sysargs: [str]
63 """
64 if sysargs is None:
65 # lazy-init sys args
66 import sys
67 sysargs = sys.argv
68
69 import unittest
70
71 if coverage_packages is None:
72 coverage_packages = [package]
73
74 #parse sysargs
75 result_file = None
76 for arg in sysargs:
77 if arg.startswith(XML_OUTPUT_FLAG):
78 result_file = arg[len(XML_OUTPUT_FLAG):]
79 text_mode = '--text' in sysargs
80
81 coverage_mode = '--cov' in sysargs or '--covhtml' in sysargs
82 if coverage_mode:
83 start_coverage(coverage_packages)
84
85 # create and run unittest suite with our xmllrunner wrapper
86 suite = unittest.TestLoader().loadTestsFromTestCase(test)
87 if text_mode:
88 result = unittest.TextTestRunner(verbosity=2).run(suite)
89 else:
90 result = create_xml_runner(package, test_name, result_file).run(suite)
91 if coverage_mode:
92 cov_html_dir = 'covhtml' if '--covhtml' in sysargs else None
93 stop_coverage(coverage_packages, html=cov_html_dir)
94
95 # test over, summarize results and exit appropriately
96 print_unittest_summary(result)
97
98 if not result.wasSuccessful():
99 import sys
100 sys.exit(1)
101
102 # coverage instance
103 _cov = None
104 def start_coverage(packages):
105 global _cov
106 try:
107 import coverage
108 try:
109 _cov = coverage.coverage()
110 # load previous results as we need to accumulate
111 _cov.load()
112 _cov.start()
113 except coverage.CoverageException:
114 print >> sys.stderr, "WARNING: you have an older version of python-coverage that is not support. Please update to the version provided by 'easy_install coverage'"
115 except ImportError, e:
116 print >> sys.stderr, """WARNING: cannot import python-coverage, coverage tests will not run.
117 To install coverage, run 'easy_install coverage'"""
118
119 def stop_coverage(packages, html=None):
120 """
121 @param packages: list of packages to generate coverage reports for
122 @type packages: [str]
123 @param html: (optional) if not None, directory to generate html report to
124 @type html: str
125 """
126 if _cov is None:
127 return
128 import sys, os
129 try:
130 _cov.stop()
131 # accumulate results
132 _cov.save()
133
134 # - update our own .coverage-modules file list for
135 # coverage-html tool. The reason we read and rewrite instead
136 # of append is that this does a uniqueness check to keep the
137 # file from growing unbounded
138 if os.path.exists('.coverage-modules'):
139 with open('.coverage-modules','r') as f:
140 all_packages = set([x for x in f.read().split('\n') if x.strip()] + packages)
141 else:
142 all_packages = set(packages)
143 with open('.coverage-modules','w') as f:
144 f.write('\n'.join(all_packages)+'\n')
145
146 try:
147 # list of all modules for html report
148 all_mods = []
149
150 # iterate over packages to generate per-package console reports
151 for package in packages:
152 pkg = __import__(package)
153 m = [v for v in sys.modules.values() if v and v.__name__.startswith(package)]
154 all_mods.extend(m)
155
156 # generate overall report and per module analysis
157 _cov.report(m, show_missing=0)
158 for mod in m:
159 res = _cov.analysis(mod)
160 print "\n%s:\nMissing lines: %s"%(res[0], res[3])
161
162 if html:
163
164 print "="*80+"\ngenerating html coverage report to %s\n"%html+"="*80
165 _cov.html_report(all_mods, directory=html)
166 except ImportError, e:
167 print >> sys.stderr, "WARNING: cannot import '%s', will not generate coverage report"%package
168 except ImportError, e:
169 print >> sys.stderr, """WARNING: cannot import python-coverage, coverage tests will not run.
170 To install coverage, run 'easy_install coverage'"""
171
172
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 # Revision $Id$
33
34 from __future__ import with_statement
35
36 import os
37 import sys
38 import time
39 import unittest
40 import logging
41
42 import rospkg
43
44 from . import pmon
45 from . core import xml_results_file, create_xml_runner
46
47 from .junitxml import print_summary, Result
48 from .baretest import BareTestCase, print_runner_summary
49
50
51 _NAME = 'rosunit'
52
53 def rosunitmain():
54 from optparse import OptionParser
55 parser = OptionParser(usage="usage: %prog [options] <file> [test args...]", prog=_NAME)
56 parser.add_option("-t", "--text",
57 action="store_true", dest="text_mode", default=False,
58 help="Run with stdout output instead of XML output")
59 parser.add_option("--time-limit", metavar="TIME_LIMIT",
60 dest="time_limit", default=60,
61 help="Set time limit for test")
62 parser.add_option("--name", metavar="TEST_NAME",
63 dest="test_name", default=None,
64 help="Test name")
65 parser.add_option("--package", metavar="PACKAGE_NAME",
66 dest="pkg", default=None,
67 help="Package name (optional)")
68 (options, args) = parser.parse_args()
69
70 if len(args) < 1:
71 parser.error("You must supply a test file.")
72
73 test_file = args[0]
74
75 if options.test_name:
76 test_name = options.test_name
77 else:
78 test_name = os.path.basename(test_file)
79 if '.' in test_name:
80 test_name = test_name[:test_name.rfind('.')]
81 time_limit = float(options.time_limit) if options.time_limit else None
82
83 # If the caller didn't tell us the package name, we'll try to infer it.
84 # compute some common names we'll be using to generate test names and files
85 pkg = options.pkg
86 if not pkg:
87 pkg = rospkg.get_package_name(test_file)
88 if not pkg:
89 print "Error: failed to determine package name for file '%s'; maybe you should supply the --package argument to rosunit?"%(test_file)
90 sys.exit(1)
91
92 try:
93 runner_result = None
94 results = Result('rosunit', 0, 0, 0)
95
96 test_case = BareTestCase(test_file, args[1:], \
97 retry=0, time_limit=time_limit, \
98 test_name=test_name, text_mode=options.text_mode, package_name=pkg)
99 suite = unittest.TestSuite()
100 suite.addTest(test_case)
101
102 if options.text_mode:
103 result = unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite)
104 else:
105 results_file = xml_results_file(pkg, test_name, True)
106 # the is_rostest really just means "wrapper"
107 xml_runner = create_xml_runner(pkg, test_name, \
108 results_file=results_file, \
109 is_rostest=True)
110 runner_result = xml_runner.run(suite)
111 finally:
112 pmon.pmon_shutdown()
113
114 # summary is worthless if textMode is on as we cannot scrape .xml results
115 results = test_case.results
116 if not options.text_mode:
117 print_runner_summary(runner_result, results)
118 else:
119 print "WARNING: overall test result is not accurate when --text is enabled"
120
121 if runner_result is not None and not runner_result.wasSuccessful():
122 sys.exit(1)
123 elif results.num_errors or results.num_failures:
124 sys.exit(2)
125
126 if __name__ == '__main__':
127 rosunitmain()
0 """
1 XML Test Runner for PyUnit
2 """
3
4 # Written by Sebastian Rittau <srittau@jroger.in-berlin.de> and placed in
5 # the Public Domain. With contributions by Paolo Borelli.
6
7 __revision__ = "$Id$"
8
9 import os.path
10 import re
11 import sys
12 import time
13 import traceback
14 import unittest
15 from StringIO import StringIO
16 from xml.sax.saxutils import escape
17
18 from StringIO import StringIO
19
20
21 class _TestInfo(object):
22
23 """Information about a particular test.
24
25 Used by _XMLTestResult.
26
27 """
28
29 def __init__(self, test, time):
30 (self._class, self._method) = test.id().rsplit(".", 1)
31 self._time = time
32 self._error = None
33 self._failure = None
34
35 @staticmethod
36 def create_success(test, time):
37 """Create a _TestInfo instance for a successful test."""
38 return _TestInfo(test, time)
39
40 @staticmethod
41 def create_failure(test, time, failure):
42 """Create a _TestInfo instance for a failed test."""
43 info = _TestInfo(test, time)
44 info._failure = failure
45 return info
46
47 @staticmethod
48 def create_error(test, time, error):
49 """Create a _TestInfo instance for an erroneous test."""
50 info = _TestInfo(test, time)
51 info._error = error
52 return info
53
54 def print_report(self, stream):
55 """Print information about this test case in XML format to the
56 supplied stream.
57
58 """
59 stream.write(' <testcase classname="%(class)s" name="%(method)s" time="%(time).4f">' % \
60 {
61 "class": self._class,
62 "method": self._method,
63 "time": self._time,
64 })
65 if self._failure != None:
66 self._print_error(stream, 'failure', self._failure)
67 if self._error != None:
68 self._print_error(stream, 'error', self._error)
69 stream.write('</testcase>\n')
70
71 def print_report_text(self, stream):
72 #stream.write(' <testcase classname="%(class)s" name="%(method)s" time="%(time).4f">' % \
73 # {
74 # "class": self._class,
75 # "method": self._method,
76 # "time": self._time,
77 # })
78 stream.write(self._method)
79 if self._failure != None:
80 stream.write(' ... FAILURE!\n')
81 self._print_error_text(stream, 'failure', self._failure)
82 if self._error != None:
83 stream.write(' ... ERROR!\n')
84 self._print_error_text(stream, 'error', self._error)
85 if self._failure == None and self._error == None:
86 stream.write(' ... ok\n')
87
88 def _print_error(self, stream, tagname, error):
89 """Print information from a failure or error to the supplied stream."""
90 text = escape(str(error[1]))
91 stream.write('\n')
92 stream.write(' <%s type="%s">%s\n' \
93 % (tagname, str(error[0].__name__), text))
94 tb_stream = StringIO()
95 traceback.print_tb(error[2], None, tb_stream)
96 stream.write(escape(tb_stream.getvalue()))
97 stream.write(' </%s>\n' % tagname)
98 stream.write(' ')
99
100 def _print_error_text(self, stream, tagname, error):
101 """Print information from a failure or error to the supplied stream."""
102 text = escape(str(error[1]))
103 stream.write('%s: %s\n' \
104 % (tagname.upper(), text))
105 tb_stream = StringIO()
106 traceback.print_tb(error[2], None, tb_stream)
107 stream.write(escape(tb_stream.getvalue()))
108 stream.write('-'*80 + '\n')
109
110 class _XMLTestResult(unittest.TestResult):
111
112 """A test result class that stores result as XML.
113
114 Used by XMLTestRunner.
115
116 """
117
118 def __init__(self, classname):
119 unittest.TestResult.__init__(self)
120 self._test_name = classname
121 self._start_time = None
122 self._tests = []
123 self._error = None
124 self._failure = None
125
126 def startTest(self, test):
127 unittest.TestResult.startTest(self, test)
128 self._error = None
129 self._failure = None
130 self._start_time = time.time()
131
132 def stopTest(self, test):
133 time_taken = time.time() - self._start_time
134 unittest.TestResult.stopTest(self, test)
135 if self._error:
136 info = _TestInfo.create_error(test, time_taken, self._error)
137 elif self._failure:
138 info = _TestInfo.create_failure(test, time_taken, self._failure)
139 else:
140 info = _TestInfo.create_success(test, time_taken)
141 self._tests.append(info)
142
143 def addError(self, test, err):
144 unittest.TestResult.addError(self, test, err)
145 self._error = err
146
147 def addFailure(self, test, err):
148 unittest.TestResult.addFailure(self, test, err)
149 self._failure = err
150
151 def print_report(self, stream, time_taken, out, err):
152 """Prints the XML report to the supplied stream.
153
154 The time the tests took to perform as well as the captured standard
155 output and standard error streams must be passed in.a
156
157 """
158 stream.write('<testsuite errors="%(e)d" failures="%(f)d" ' % \
159 { "e": len(self.errors), "f": len(self.failures) })
160 stream.write('name="%(n)s" tests="%(t)d" time="%(time).3f">\n' % \
161 {
162 "n": self._test_name,
163 "t": self.testsRun,
164 "time": time_taken,
165 })
166 for info in self._tests:
167 info.print_report(stream)
168 stream.write(' <system-out><![CDATA[%s]]></system-out>\n' % out)
169 stream.write(' <system-err><![CDATA[%s]]></system-err>\n' % err)
170 stream.write('</testsuite>\n')
171
172 def print_report_text(self, stream, time_taken, out, err):
173 """Prints the text report to the supplied stream.
174
175 The time the tests took to perform as well as the captured standard
176 output and standard error streams must be passed in.a
177
178 """
179 #stream.write('<testsuite errors="%(e)d" failures="%(f)d" ' % \
180 # { "e": len(self.errors), "f": len(self.failures) })
181 #stream.write('name="%(n)s" tests="%(t)d" time="%(time).3f">\n' % \
182 # {
183 # "n": self._test_name,
184 # "t": self.testsRun,
185 # "time": time_taken,
186 # })
187 for info in self._tests:
188 info.print_report_text(stream)
189
190
191 class XMLTestRunner(object):
192
193 """A test runner that stores results in XML format compatible with JUnit.
194
195 XMLTestRunner(stream=None) -> XML test runner
196
197 The XML file is written to the supplied stream. If stream is None, the
198 results are stored in a file called TEST-<module>.<class>.xml in the
199 current working directory (if not overridden with the path property),
200 where <module> and <class> are the module and class name of the test class.
201
202 """
203
204 def __init__(self, stream=None):
205 self._stream = stream
206 self._path = "."
207
208 def run(self, test):
209 """Run the given test case or test suite."""
210 class_ = test.__class__
211 classname = class_.__module__ + "." + class_.__name__
212 if self._stream == None:
213 filename = "TEST-%s.xml" % classname
214 stream = file(os.path.join(self._path, filename), "w")
215 stream.write('<?xml version="1.0" encoding="utf-8"?>\n')
216 else:
217 stream = self._stream
218
219 result = _XMLTestResult(classname)
220 start_time = time.time()
221
222 # TODO: Python 2.5: Use the with statement
223 old_stdout = sys.stdout
224 old_stderr = sys.stderr
225 sys.stdout = StringIO()
226 sys.stderr = StringIO()
227
228 try:
229 test(result)
230 try:
231 out_s = sys.stdout.getvalue()
232 except AttributeError:
233 out_s = ""
234 try:
235 err_s = sys.stderr.getvalue()
236 except AttributeError:
237 err_s = ""
238 finally:
239 sys.stdout = old_stdout
240 sys.stderr = old_stderr
241
242 time_taken = time.time() - start_time
243 result.print_report(stream, time_taken, out_s, err_s)
244
245 result.print_report_text(sys.stdout, time_taken, out_s, err_s)
246
247 if self._stream == None:
248 stream.close()
249
250 return result
251
252 def _set_path(self, path):
253 self._path = path
254
255 path = property(lambda self: self._path, _set_path, None,
256 """The path where the XML files are stored.
257
258 This property is ignored when the XML file is written to a file
259 stream.""")
260
261
262 class XMLTestRunnerTest(unittest.TestCase):
263 def setUp(self):
264 self._stream = StringIO()
265
266 def _try_test_run(self, test_class, expected):
267
268 """Run the test suite against the supplied test class and compare the
269 XML result against the expected XML string. Fail if the expected
270 string doesn't match the actual string. All time attribute in the
271 expected string should have the value "0.000". All error and failure
272 messages are reduced to "Foobar".
273
274 """
275
276 runner = XMLTestRunner(self._stream)
277 runner.run(unittest.makeSuite(test_class))
278
279 got = self._stream.getvalue()
280 # Replace all time="X.YYY" attributes by time="0.000" to enable a
281 # simple string comparison.
282 got = re.sub(r'time="\d+\.\d+"', 'time="0.000"', got)
283 # Likewise, replace all failure and error messages by a simple "Foobar"
284 # string.
285 got = re.sub(r'(?s)<failure (.*?)>.*?</failure>', r'<failure \1>Foobar</failure>', got)
286 got = re.sub(r'(?s)<error (.*?)>.*?</error>', r'<error \1>Foobar</error>', got)
287
288 self.assertEqual(expected, got)
289
290 def test_no_tests(self):
291 """Regression test: Check whether a test run without any tests
292 matches a previous run.
293
294 """
295 class TestTest(unittest.TestCase):
296 pass
297 self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="0" time="0.000">
298 <system-out><![CDATA[]]></system-out>
299 <system-err><![CDATA[]]></system-err>
300 </testsuite>
301 """)
302
303 def test_success(self):
304 """Regression test: Check whether a test run with a successful test
305 matches a previous run.
306
307 """
308 class TestTest(unittest.TestCase):
309 def test_foo(self):
310 pass
311 self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
312 <testcase classname="__main__.TestTest" name="test_foo" time="0.000"></testcase>
313 <system-out><![CDATA[]]></system-out>
314 <system-err><![CDATA[]]></system-err>
315 </testsuite>
316 """)
317
318 def test_failure(self):
319 """Regression test: Check whether a test run with a failing test
320 matches a previous run.
321
322 """
323 class TestTest(unittest.TestCase):
324 def test_foo(self):
325 self.assert_(False)
326 self._try_test_run(TestTest, """<testsuite errors="0" failures="1" name="unittest.TestSuite" tests="1" time="0.000">
327 <testcase classname="__main__.TestTest" name="test_foo" time="0.000">
328 <failure type="exceptions.AssertionError">Foobar</failure>
329 </testcase>
330 <system-out><![CDATA[]]></system-out>
331 <system-err><![CDATA[]]></system-err>
332 </testsuite>
333 """)
334
335 def test_error(self):
336 """Regression test: Check whether a test run with a erroneous test
337 matches a previous run.
338
339 """
340 class TestTest(unittest.TestCase):
341 def test_foo(self):
342 raise IndexError()
343 self._try_test_run(TestTest, """<testsuite errors="1" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
344 <testcase classname="__main__.TestTest" name="test_foo" time="0.000">
345 <error type="exceptions.IndexError">Foobar</error>
346 </testcase>
347 <system-out><![CDATA[]]></system-out>
348 <system-err><![CDATA[]]></system-err>
349 </testsuite>
350 """)
351
352 def test_stdout_capture(self):
353 """Regression test: Check whether a test run with output to stdout
354 matches a previous run.
355
356 """
357 class TestTest(unittest.TestCase):
358 def test_foo(self):
359 print "Test"
360 self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
361 <testcase classname="__main__.TestTest" name="test_foo" time="0.000"></testcase>
362 <system-out><![CDATA[Test
363 ]]></system-out>
364 <system-err><![CDATA[]]></system-err>
365 </testsuite>
366 """)
367
368 def test_stderr_capture(self):
369 """Regression test: Check whether a test run with output to stderr
370 matches a previous run.
371
372 """
373 class TestTest(unittest.TestCase):
374 def test_foo(self):
375 print >>sys.stderr, "Test"
376 self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
377 <testcase classname="__main__.TestTest" name="test_foo" time="0.000"></testcase>
378 <system-out><![CDATA[]]></system-out>
379 <system-err><![CDATA[Test
380 ]]></system-err>
381 </testsuite>
382 """)
383
384 class NullStream(object):
385 """A file-like object that discards everything written to it."""
386 def write(self, buffer):
387 pass
388
389 def test_unittests_changing_stdout(self):
390 """Check whether the XMLTestRunner recovers gracefully from unit tests
391 that change stdout, but don't change it back properly.
392
393 """
394 class TestTest(unittest.TestCase):
395 def test_foo(self):
396 sys.stdout = XMLTestRunnerTest.NullStream()
397
398 runner = XMLTestRunner(self._stream)
399 runner.run(unittest.makeSuite(TestTest))
400
401 def test_unittests_changing_stderr(self):
402 """Check whether the XMLTestRunner recovers gracefully from unit tests
403 that change stderr, but don't change it back properly.
404
405 """
406 class TestTest(unittest.TestCase):
407 def test_foo(self):
408 sys.stderr = XMLTestRunnerTest.NullStream()
409
410 runner = XMLTestRunner(self._stream)
411 runner.run(unittest.makeSuite(TestTest))
412
413
414 class XMLTestProgram(unittest.TestProgram):
415 def runTests(self):
416 if self.testRunner is None:
417 self.testRunner = XMLTestRunner()
418 unittest.TestProgram.runTests(self)
419
420 main = XMLTestProgram
421
422
423 if __name__ == "__main__":
424 main(module=None)
0 [nosetests]
1 with-coverage=1
2 cover-package=rosunit
3 with-xunit=1
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id: $
34
35 import os
36 import io
37 import sys
38 import unittest
39 import tempfile
40 import shutil
41
42 junitxml = None
43
44 ## Basic test of xmlresult functionality of reading gtest xml files and
45 ## summarizing their results into a new file.
46 class MockResult():
47 def __init__(self, directory, filename, suites = [], noSuitesRoot = False):
48 self.filename = os.path.join(directory, filename)
49 self.suites = suites
50 # whether to suppress <testsuites> root node
51 self.noSuitesRoot = noSuitesRoot
52
53 class MockSuite():
54 def __init__(self, cases, name, tests = 0, errors = 0, fail = 0, time = 1):
55 self.cases = cases
56 self.tests = tests
57 self.time = time
58 self.fail = fail
59 self.errors = errors
60 self.name = name
61
62
63 class MockCase():
64 def __init__(self, name, errorList = [], classname="", time = 1):
65 self.classname = classname
66 self.name = name
67 self.time = time
68 self.errorList = errorList
69
70 class MockErrorType(Exception):
71 def __init__(self, value, etype = ''):
72 self.value = value
73 self.__name__ = value
74 self.type = etype
75
76 def _writeMockResultFile(result):
77 "writes a test result as a gtest compatible test runner would do"
78 with open(result.filename, 'w') as f:
79 f.write(u"""<?xml version="1.0" encoding="UTF-8"?>""")
80 if len(result.suites) > 1 or result.noSuitesRoot == False:
81 f.write(u"""<testsuites>\n""")
82 for suite in result.suites:
83 f.write('<testsuite tests="'+str(suite.tests)+'" failures="'+str(suite.fail)+'" time="'+str(suite.time)+'" errors="'+str(suite.errors)+'" name="'+suite.name+'">\n')
84 for case in suite.cases:
85 f.write('<testcase name="'+case.name+'" status="run" time="'+str(case.time)+'" classname="'+case.classname+'">\n')
86 for error in case.errorList:
87 f.write('<failure message="'+error.value+'" type="'+error.value+'"/>\n')
88 f.write('</testcase>\n')
89 f.write('</testsuite>\n')
90 if len(result.suites) > 1 or result.noSuitesRoot == False:
91 f.write(u"""</testsuites>\n""")
92
93
94 class XmlResultTestRead(unittest.TestCase):
95
96 def setUp(self):
97 # lazy-import to get coverage
98 global junitxml
99 if junitxml is None:
100 import rosunit.junitxml
101 junitxml = rosunit.junitxml
102
103 self.directory = tempfile.mkdtemp()
104
105 # setting up mock results as dict so results can be checked individually
106 self.mockresults={
107 "empty": MockResult(self.directory, "empty.xml", []),
108 "emptysuite": MockResult(self.directory, "emptysuite.xml", [MockSuite([], "emptySuite", 0, 0, 0, 0)]),
109 "succ1": MockResult(self.directory, "succ1.xml", [MockSuite([MockCase("succCase")],"succ1suite", 1, 0, 0, 1)]),
110 "err1": MockResult(self.directory, "err1.xml", [MockSuite([MockCase("errCase")],"err1suite", 1, 1, 0, 1)]),
111 "fail1": MockResult(self.directory, "fail1.xml", [MockSuite([MockCase("failCase")],"fail1suite", 1, 0, 1, 1)]),
112 "noroot": MockResult(self.directory, "succ1.xml", [MockSuite([MockCase("succCase")],"succ1suite", 1, 0, 0, 1)], noSuitesRoot = True),
113 "multicase": MockResult(self.directory,
114 "multicase.xml",
115 [MockSuite([MockCase("succCase"),
116 MockCase("errCase"),
117 MockCase("failCase")],
118 "succ1suite", 3, 1, 1, time = 3)]),
119 "multisuite": MockResult(self.directory,
120 "multisuite.xml",
121 [MockSuite([MockCase("succCase")],"succ1suite", 1, 0, 0, 1),
122 MockSuite([MockCase("errCase")],"err1suite", 1, 1, 0, 1),
123 MockSuite([MockCase("failCase")],"fail1suite", 1, 0, 1, 1)])
124 }
125
126
127 for name, result in self.mockresults.items():
128 _writeMockResultFile(result)
129
130 def tearDown(self):
131 shutil.rmtree(self.directory)
132 #pass
133
134 def testReadNoSuites(self):
135 result = junitxml.read(self.mockresults["empty"].filename, "fooname")
136 self.assert_(result is not None)
137 self.assertEquals(0.0, result.time)
138 self.assertEquals(0, result.num_tests)
139 self.assertEquals(0, result.num_errors)
140 self.assertEquals(0, result.num_failures)
141
142 def testReadEmptySuite(self):
143 result = junitxml.read(self.mockresults["emptysuite"].filename, "fooname")
144 self.assert_(result is not None)
145 self.assertEquals(0.0, result.time)
146 self.assertEquals(0, result.num_tests)
147 self.assertEquals(0, result.num_errors)
148 self.assertEquals(0, result.num_failures)
149
150 def testReadSuccess(self):
151 result = junitxml.read(self.mockresults["succ1"].filename, "fooname")
152 self.assert_(result is not None)
153 self.assertEquals(1.0, result.time)
154 self.assertEquals(1, result.num_tests)
155 self.assertEquals(0, result.num_errors)
156 self.assertEquals(0, result.num_failures)
157
158 def testReadError(self):
159 result = junitxml.read(self.mockresults["err1"].filename, "fooname")
160 self.assert_(result is not None)
161 self.assertEquals(1.0, result.time)
162 self.assertEquals(1, result.num_tests)
163 self.assertEquals(1, result.num_errors)
164 self.assertEquals(0, result.num_failures)
165
166 def testReadFail(self):
167 result = junitxml.read(self.mockresults["fail1"].filename, "fooname")
168 self.assert_(result is not None)
169 self.assertEquals(1.0, result.time)
170 self.assertEquals(1, result.num_tests)
171 self.assertEquals(0, result.num_errors)
172 self.assertEquals(1, result.num_failures)
173
174 def testReadMulticase(self):
175 result = junitxml.read(self.mockresults["multicase"].filename, "fooname")
176 self.assert_(result is not None)
177 self.assertEquals(3.0, result.time)
178 self.assertEquals(3, result.num_tests)
179 self.assertEquals(1, result.num_errors)
180 self.assertEquals(1, result.num_failures)
181
182 def testReadMultisuite(self):
183 result = junitxml.read(self.mockresults["multisuite"].filename, "fooname")
184 self.assert_(result is not None)
185 self.assertEquals(3.0, result.time)
186 self.assertEquals(3, result.num_tests)
187 self.assertEquals(1, result.num_errors)
188 self.assertEquals(1, result.num_failures)