| Home | Trees | Indices | Help |
|
|---|
|
|
1 #! /usr/bin/env python
2 # Software License Agreement (BSD License)
3 #
4 # Copyright (c) 2008, Willow Garage, Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following
15 # disclaimer in the documentation and/or other materials provided
16 # with the distribution.
17 # * Neither the name of Willow Garage, Inc. nor the names of its
18 # contributors may be used to endorse or promote products derived
19 # from this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 #
34 # Revision $Id: stacks.py 9275 2010-04-21 00:45:06Z kwc $
35
36 """
37 Python utilities for manipulating ROS Stacks.
38 See: U{http://ros.org/wiki/Stacks}
39
40 Warning: this API is still fairly experimental and incomplete.
41 """
42
43 import os
44 import sys
45
46 import roslib.exceptions
47 import roslib.packages
48 from roslib.rosenv import ROS_ROOT, ROS_PACKAGE_PATH
49
50
51 STACK_FILE = 'stack.xml'
52 ROS_STACK = 'ros'
53
56
58 """
59 @return: name of stack that pkg is in, or None if pkg is not part of a stack
60 @rtype: str
61 @raise roslib.packages.InvalidROSPkgException: if pkg cannot be located
62 """
63 pkg_dir = roslib.packages.get_pkg_dir(pkg)
64 dir = os.path.dirname(pkg_dir)
65 while dir and os.path.dirname(dir) != dir:
66 stack_file = os.path.join(dir, STACK_FILE)
67 if os.path.exists(stack_file):
68 #TODO: need to resolve issues regarding whether the
69 #stack.xml or the directory defines the stack name
70 return os.path.basename(dir)
71 dir = os.path.dirname(dir)
72
74 """
75 @return: name of packages that are part of stack
76 @rtype: [str]
77 @raise InvalidROSStackException: if stack cannot be located
78 @raise ValueError: if stack name is invalid
79 """
80 # record settings for error messages
81 ros_root = env[ROS_ROOT]
82 ros_package_path = env.get(ROS_PACKAGE_PATH, '')
83
84 if not stack:
85 raise ValueError("stack name not specified")
86 stack_dir = get_stack_dir(stack)
87 if stack_dir is None:
88 raise InvalidROSStackException("Cannot locate installation of stack %s. ROS_ROOT[%s] ROS_PACKAGE_PATH[%s]"%(stack, ros_root,ros_package_path))
89 packages = []
90 l = [os.path.join(stack_dir, d) for d in os.listdir(stack_dir)]
91 # kwc: this really is just a 1-directory reimplementation of
92 # list_pkgs(). Should merge implementations, though have to deal
93 # with issues of cache, etc...
94 while l:
95 d = l.pop()
96 if os.path.isdir(d):
97 if roslib.packages.is_pkg_dir(d):
98 p = os.path.basename(d)
99 # this is sometimes true if we've descended into a build directory
100 if not p in packages:
101 packages.append(p)
102 elif os.path.exists(os.path.join(d, 'rospack_nosubdirs')):
103 # don't descend
104 pass
105 elif os.path.basename(d) not in ['build', '.svn', '.git']: #recurse
106 l.extend([os.path.join(d, e) for e in os.listdir(d)])
107 return packages
108
110 """
111 Get the directory of a ROS stack. This will initialize an internal
112 cache and return cached results if possible.
113
114 This routine is not thread-safe to os.environ changes.
115
116 @param stack: name of ROS stack to locate on disk
117 @type stack: str
118 @return: directory of stack.
119 @rtype: str
120 @raise InvalidROSStackException: if stack cannot be located.
121 """
122
123 # it's possible to get incorrect results from this cache
124 # implementation by manipulating the environment and calling this
125 # from multiple threads. as that is an unusual use case and would
126 # require a slower implmentation, it's not supported. the
127 # interpretation of this routine is get_stack_dir for the
128 # environment this process was launched in.
129 global _dir_cache_marker
130
131 env = os.environ
132 if stack in _dir_cache:
133 ros_root = env[ROS_ROOT]
134 ros_package_path = env.get(ROS_PACKAGE_PATH, '')
135
136 # we don't attempt to be thread-safe to environment changes,
137 # however we do need to be threadsafe to cache invalidation.
138 try:
139 if _dir_cache_marker == (ros_root, ros_package_path):
140 d = _dir_cache[stack]
141 if os.path.isfile(os.path.join(d, STACK_FILE)):
142 return d
143 else:
144 # invalidate the cache
145 _dir_cache_marker = None
146 _dir_cache.clear()
147 except KeyError:
148 pass
149 _update_stack_cache() #update cache
150 val = _dir_cache.get(stack, None)
151 if val is None:
152 raise InvalidROSStackException("Cannot location installation of stack %s. ROS_ROOT[%s] ROS_PACKAGE_PATH[%s]"%(stack, env[ROS_ROOT], env.get(ROS_PACKAGE_PATH, '')))
153 return val
154
155 # rosstack directory cache
156 _dir_cache = {}
157 # stores ROS_ROOT, ROS_PACKAGE_PATH of _dir_cache
158 _dir_cache_marker = None
159
161 """
162 Update _dir_cache if environment has changed since last cache build.
163
164 @param force: force cache rebuild regardless of environment variables
165 @type force: bool
166 """
167 global _dir_cache_marker
168 env = os.environ
169 ros_root = env[ROS_ROOT]
170 ros_package_path = env.get(ROS_PACKAGE_PATH, '')
171
172 if _dir_cache_marker == (ros_root, ros_package_path):
173 return
174 _dir_cache.clear()
175 _dir_cache_marker = ros_root, ros_package_path
176
177 pkg_dirs = roslib.packages.get_package_paths(env=env)
178 # ros is assumed to be at ROS_ROOT
179 if os.path.exists(os.path.join(ros_root, 'stack.xml')):
180 _dir_cache['ros'] = ros_root
181 pkg_dirs.remove(ros_root)
182
183 # pass in accumulated stacks list to each call. This ensures
184 # precedence (i.e. that stacks first on pkg_dirs path win).
185 stacks = []
186 for pkg_root in pkg_dirs:
187 # list_stacks_by_path will append list into stacks, so that
188 # each call accumulates in it.
189 list_stacks_by_path(pkg_root, stacks, cache=_dir_cache)
190
192 """
193 Get list of all ROS stacks. This uses an internal cache.
194
195 This routine is not thread-safe to os.environ changes.
196
197 @return: complete list of stacks names in ROS environment
198 @rtype: [str]
199 """
200 _update_stack_cache()
201 return _dir_cache.keys()
202
204 """
205 List ROS stacks within the specified path.
206
207 Optionally, a cache dictionary can be provided, which will be
208 updated with the stack->path mappings. list_stacks_by_path() does
209 NOT returned cached results -- it only updates the cache.
210
211 @param path: path to list stacks in
212 @type path: str
213 @param stacks: list of stacks to append to. If stack is
214 already present in stacks, it will be ignored.
215 @type stacks: [str]
216 @param cache: (optional) stack path cache to update. Maps stack name to directory path.
217 @type cache: {str: str}
218 @return: complete list of stack names in ROS environment. Same as stacks parameter.
219 @rtype: [str]
220 """
221 if stacks is None:
222 stacks = []
223 MANIFEST_FILE = roslib.packages.MANIFEST_FILE
224 basename = os.path.basename
225 for d, dirs, files in os.walk(path, topdown=True):
226 if STACK_FILE in files:
227 stack = basename(d)
228 if stack not in stacks:
229 stacks.append(stack)
230 if cache is not None:
231 cache[stack] = d
232 del dirs[:]
233 continue #leaf
234 elif MANIFEST_FILE in files:
235 del dirs[:]
236 continue #leaf
237 elif 'rospack_nosubdirs' in files:
238 del dirs[:]
239 continue #leaf
240 # remove hidden dirs (esp. .svn/.git)
241 [dirs.remove(di) for di in dirs if di[0] == '.']
242 for sub_d in dirs:
243 # followlinks=True only available in Python 2.6, so we
244 # have to implement manually
245 sub_p = os.path.join(d, sub_d)
246 if os.path.islink(sub_p):
247 stacks.extend(list_stacks_by_path(sub_p, cache=cache))
248 return stacks
249
250 # #2022
252 """
253 Expand names into a list of packages. Names can either be of packages or stacks.
254
255 @param names: names of stacks or packages
256 @type names: [str]
257 @return: ([packages], [not_found]). expand_packages() returns two
258 lists. The first is of packages names. The second is a list of
259 names for which no matching stack or package was found. Lists may have duplicates.
260 @rtype: ([str], [str])
261 """
262 if type(names) not in [tuple, list]:
263 raise ValueError("names must be a list of strings")
264
265 # do full package list first. This forces an entire tree
266 # crawl. This is less efficient for a small list of names, but
267 # much more efficient for many names.
268 package_list = roslib.packages.list_pkgs()
269 valid = []
270 invalid = []
271 for n in names:
272 if not n in package_list:
273 try:
274 valid.extend(roslib.stacks.packages_of(n))
275 except roslib.stacks.InvalidROSStackException, e:
276 invalid.append(n)
277 else:
278 valid.append(n)
279 return valid, invalid
280
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Jan 11 10:12:16 2013 | http://epydoc.sourceforge.net |