1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 import re
46
47 import roslib.exceptions
48 import roslib.packages
49 import roslib.stack_manifest
50 from roslib.rosenv import ROS_ROOT, ROS_PACKAGE_PATH
51
52 STACK_FILE = 'stack.xml'
53 ROS_STACK = 'ros'
54
57
59 """
60 @param env: override environment variables
61 @type env: {str: str}
62 @return: name of stack that pkg is in, or None if pkg is not part of a stack
63 @rtype: str
64 @raise roslib.packages.InvalidROSPkgException: if pkg cannot be located
65 """
66 if env is None:
67 env = os.environ
68 pkg_dir = roslib.packages.get_pkg_dir(pkg, ros_root=env[ROS_ROOT], ros_package_path=env.get(ROS_PACKAGE_PATH, None))
69 d = pkg_dir
70 while d and os.path.dirname(d) != d:
71 stack_file = os.path.join(d, STACK_FILE)
72 if os.path.exists(stack_file):
73
74
75 return os.path.basename(d)
76 d = os.path.dirname(d)
77
79 """
80 @param env: override environment variables
81 @type env: {str: str}
82 @return: name of packages that are part of stack
83 @rtype: [str]
84 @raise InvalidROSStackException: if stack cannot be located
85 @raise ValueError: if stack name is invalid
86 """
87
88 if env is None:
89 env = os.environ
90 ros_root = env[ROS_ROOT]
91 ros_package_path = env.get(ROS_PACKAGE_PATH, '')
92
93 if not stack:
94 raise ValueError("stack name not specified")
95 stack_dir = get_stack_dir(stack, env=env)
96 if stack_dir is None:
97 raise InvalidROSStackException("Cannot locate installation of stack %s. ROS_ROOT[%s] ROS_PACKAGE_PATH[%s]"%(stack, ros_root,ros_package_path))
98
99 if roslib.packages.is_pkg_dir(stack_dir):
100 return [stack]
101 packages = []
102 l = [os.path.join(stack_dir, d) for d in os.listdir(stack_dir)]
103
104
105
106 while l:
107 d = l.pop()
108 if os.path.isdir(d):
109 if roslib.packages.is_pkg_dir(d):
110 p = os.path.basename(d)
111
112 if not p in packages:
113 packages.append(p)
114 elif os.path.exists(os.path.join(d, 'rospack_nosubdirs')):
115
116 pass
117 elif os.path.basename(d) not in ['build', '.svn', '.git']:
118 l.extend([os.path.join(d, e) for e in os.listdir(d)])
119 return packages
120
122 """
123 Get the directory of a ROS stack. This will initialize an internal
124 cache and return cached results if possible.
125
126 This routine is not thread-safe to os.environ changes.
127
128 @param env: override environment variables
129 @type env: {str: str}
130 @param stack: name of ROS stack to locate on disk
131 @type stack: str
132 @return: directory of stack.
133 @rtype: str
134 @raise InvalidROSStackException: if stack cannot be located.
135 """
136
137
138
139
140
141
142
143 global _dir_cache_marker
144
145 if env is None:
146 env = os.environ
147 if stack in _dir_cache:
148 ros_root = env[ROS_ROOT]
149 ros_package_path = env.get(ROS_PACKAGE_PATH, '')
150
151
152
153 try:
154 if _dir_cache_marker == (ros_root, ros_package_path):
155 d = _dir_cache[stack]
156 if os.path.isfile(os.path.join(d, STACK_FILE)):
157 return d
158 else:
159
160 _dir_cache_marker = None
161 _dir_cache.clear()
162 except KeyError:
163 pass
164 _update_stack_cache(env=env)
165 try:
166 val = _dir_cache[stack]
167 except KeyError:
168 raise InvalidROSStackException("Cannot location installation of stack %s. ROS_ROOT[%s] ROS_PACKAGE_PATH[%s]"%(stack, env[ROS_ROOT], env.get(ROS_PACKAGE_PATH, '')))
169 return val
170
171
172 _dir_cache = {}
173
174 _dir_cache_marker = None
175
209
211 """
212 Get list of all ROS stacks. This uses an internal cache.
213
214 This routine is not thread-safe to os.environ changes.
215
216 @param env: override environment variables
217 @type env: {str: str}
218 @return: complete list of stacks names in ROS environment
219 @rtype: [str]
220 """
221 _update_stack_cache(env=env)
222 return list(_dir_cache.keys())
223
225 """
226 List ROS stacks within the specified path.
227
228 Optionally, a cache dictionary can be provided, which will be
229 updated with the stack->path mappings. list_stacks_by_path() does
230 NOT returned cached results -- it only updates the cache.
231
232 @param path: path to list stacks in
233 @type path: str
234 @param stacks: list of stacks to append to. If stack is
235 already present in stacks, it will be ignored.
236 @type stacks: [str]
237 @param cache: (optional) stack path cache to update. Maps stack name to directory path.
238 @type cache: {str: str}
239 @return: complete list of stack names in ROS environment. Same as stacks parameter.
240 @rtype: [str]
241 """
242 if stacks is None:
243 stacks = []
244 MANIFEST_FILE = roslib.packages.MANIFEST_FILE
245 basename = os.path.basename
246 for d, dirs, files in os.walk(path, topdown=True):
247 if STACK_FILE in files:
248 stack = basename(d)
249 if stack not in stacks:
250 stacks.append(stack)
251 if cache is not None:
252 cache[stack] = d
253 del dirs[:]
254 continue
255 elif MANIFEST_FILE in files:
256 del dirs[:]
257 continue
258 elif 'rospack_nosubdirs' in files:
259 del dirs[:]
260 continue
261
262 [dirs.remove(di) for di in dirs if di[0] == '.']
263 for sub_d in dirs:
264
265
266 sub_p = os.path.join(d, sub_d)
267 if os.path.islink(sub_p):
268 stacks.extend(list_stacks_by_path(sub_p, cache=cache))
269 return stacks
270
271
273 """
274 Expand names into a list of packages. Names can either be of packages or stacks.
275
276 @param names: names of stacks or packages
277 @type names: [str]
278 @return: ([packages], [not_found]). expand_packages() returns two
279 lists. The first is of packages names. The second is a list of
280 names for which no matching stack or package was found. Lists may have duplicates.
281 @rtype: ([str], [str])
282 """
283 if type(names) not in [tuple, list]:
284 raise ValueError("names must be a list of strings")
285
286
287
288
289 package_list = roslib.packages.list_pkgs(env=env)
290 valid = []
291 invalid = []
292 for n in names:
293 if not n in package_list:
294 try:
295 valid.extend(roslib.stacks.packages_of(n, env=env))
296 except roslib.stacks.InvalidROSStackException as e:
297 invalid.append(n)
298 else:
299 valid.append(n)
300 return valid, invalid
301
303 """
304 @param env: override environment variables
305 @type env: {str: str}
306
307 @return: version number of stack, or None if stack is unversioned.
308 @rtype: str
309 """
310 return get_stack_version_by_dir(get_stack_dir(stack, env=env))
311
313 """
314 Get stack version where stack_dir points to root directory of stack.
315
316 @param env: override environment variables
317 @type env: {str: str}
318
319 @return: version number of stack, or None if stack is unversioned.
320 @rtype: str
321 """
322
323 manifest_filename = os.path.join(stack_dir, STACK_FILE)
324 if os.path.isfile(manifest_filename):
325 m = roslib.stack_manifest.parse_file(manifest_filename)
326 if m.version:
327 return m.version
328
329 cmake_filename = os.path.join(stack_dir, 'CMakeLists.txt')
330 if os.path.isfile(cmake_filename):
331 with open(cmake_filename) as f:
332 return _get_cmake_version(f.read())
333 else:
334 return None
335
337 for l in text.split('\n'):
338 if l.strip().startswith('rosbuild_make_distribution'):
339 x_re = re.compile(r'[()]')
340 lsplit = x_re.split(l.strip())
341 if len(lsplit) < 2:
342 raise ReleaseException("couldn't find version number in CMakeLists.txt:\n\n%s"%l)
343 return lsplit[1]
344