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 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 os
43 import sys
44 import stat
45 import string
46
47 from subprocess import Popen, PIPE
48
49 import rospkg
50
51 import roslib.manifest
52
53 SRC_DIR = 'src'
54
55 CATKIN_SOURCE_DIR = 'CATKIN_SOURCE_DIR'
56 CATKIN_BINARY_DIR = 'CATKIN_BINARY_DIR'
57
58
59 ROS_PACKAGE_PATH = rospkg.environment.ROS_PACKAGE_PATH
60 ROS_ROOT = rospkg.environment.ROS_ROOT
61
63 """
64 Base class of package-related errors.
65 """
66 pass
68 """
69 Exception that indicates that a ROS package does not exist
70 """
71 pass
73 """
74 Exception that indicates that multiple ROS nodes by the same name are in the same package.
75 """
76 pass
77
78
79
80 MANIFEST_FILE = 'manifest.xml'
81
82
83
84
85
87 """
88 Get the package that the directory is contained within. This is
89 determined by finding the nearest parent manifest.xml file. This
90 isn't 100% reliable, but symlinks can fool any heuristic that
91 relies on ROS_ROOT.
92 @param d: directory path
93 @type d: str
94 @return: (package_directory, package) of the specified directory, or None,None if not in a package
95 @rtype: (str, str)
96 """
97
98
99 parent = os.path.dirname(os.path.realpath(d))
100
101 while not os.path.exists(os.path.join(d, MANIFEST_FILE)) and parent != d:
102 d = parent
103 parent = os.path.dirname(d)
104 if os.path.exists(os.path.join(d, MANIFEST_FILE)):
105 pkg = os.path.basename(os.path.abspath(d))
106 return d, pkg
107 return None, None
108
109 _pkg_dir_cache = {}
110
111 -def get_pkg_dir(package, required=True, ros_root=None, ros_package_path=None):
112 """
113 Locate directory package is stored in. This routine uses an
114 internal cache.
115
116 NOTE: cache does *not* rebuild if packages are relocated after
117 this process is initiated.
118
119 @param package: package name
120 @type package: str
121 @param required: if True, an exception will be raised if the
122 package directory cannot be located.
123 @type required: bool
124 @param ros_root: if specified, override ROS_ROOT
125 @type ros_root: str
126 @param ros_package_path: if specified, override ROS_PACKAGE_PATH
127 @type ros_package_path: str
128 @return: directory containing package or None if package cannot be found and required is False.
129 @rtype: str
130 @raise InvalidROSPkgException: if required is True and package cannot be located
131 """
132
133
134
135 try:
136 penv = os.environ.copy()
137 if ros_root:
138 ros_root = rospkg.environment._resolve_path(ros_root)
139 penv[ROS_ROOT] = ros_root
140 elif ROS_ROOT in os.environ:
141
142 ros_root = os.environ[ROS_ROOT]
143
144
145 rospack = 'rospack'
146
147 if ros_package_path is not None:
148 ros_package_path = rospkg.environment._resolve_paths(ros_package_path)
149 penv[ROS_PACKAGE_PATH] = ros_package_path
150 elif ROS_PACKAGE_PATH in os.environ:
151
152 ros_package_path = os.environ[ROS_PACKAGE_PATH]
153
154
155 if not _pkg_dir_cache:
156 _read_rospack_cache(_pkg_dir_cache, ros_root, ros_package_path)
157
158
159 if package in _pkg_dir_cache:
160 dir_, rr, rpp = _pkg_dir_cache[package]
161 if rr == ros_root and rpp == ros_package_path:
162 if os.path.isfile(os.path.join(dir_, MANIFEST_FILE)):
163 return dir_
164 else:
165
166 _invalidate_cache(_pkg_dir_cache)
167
168 rpout, rperr = Popen([rospack, 'find', package], \
169 stdout=PIPE, stderr=PIPE, env=penv).communicate()
170
171 pkg_dir = (rpout or '').strip()
172
173 if (isinstance(pkg_dir, bytes)):
174 pkg_dir = pkg_dir.decode()
175 if not pkg_dir:
176 raise InvalidROSPkgException("Cannot locate installation of package %s: %s. ROS_ROOT[%s] ROS_PACKAGE_PATH[%s]"%(package, rperr.strip(), ros_root, ros_package_path))
177
178 if not os.path.exists(pkg_dir):
179 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))
180 elif not os.path.isdir(pkg_dir):
181 raise InvalidROSPkgException("Package %s is invalid: file [%s] is in the way"%(package, pkg_dir))
182
183
184
185 return pkg_dir
186 except OSError as e:
187 if required:
188 raise InvalidROSPkgException("Environment configuration is invalid: cannot locate rospack (%s)"%e)
189 return None
190 except Exception as e:
191 if required:
192 raise
193 return None
194
196 """
197 @param required: if True, will attempt to create the subdirectory
198 if it does not exist. An exception will be raised if this fails.
199 @type required: bool
200 @param package_dir: directory of package
201 @type package_dir: str
202 @param subdir: name of subdirectory to locate
203 @type subdir: str
204 @param env: override os.environ dictionary
205 @type env: dict
206 @param required: if True, directory must exist
207 @type required: bool
208 @return: Package subdirectory if package exist, otherwise None.
209 @rtype: str
210 @raise InvalidROSPkgException: if required is True and directory does not exist
211 """
212 if env is None:
213 env = os.environ
214 try:
215 if not package_dir:
216 raise Exception("Cannot create a '%(subdir)s' directory in %(package_dir)s: package %(package) cannot be located"%locals())
217 d = os.path.join(package_dir, subdir)
218 if required and os.path.isfile(d):
219 raise Exception("""Package '%(package)s' is improperly configured:
220 file %(d)s is preventing the creation of a directory"""%locals())
221 elif required and not os.path.isdir(d):
222 try:
223 os.makedirs(d)
224 except error:
225 raise Exception("""Package '%(package)s' is improperly configured:
226 Cannot create a '%(subdir)s' directory in %(package_dir)s.
227 Please check permissions and try again.
228 """%locals())
229 return d
230 except Exception as e:
231 if required:
232 raise
233 return None
234
236 """
237 @param required: if True, will attempt to create the subdirectory
238 if it does not exist. An exception will be raised if this fails.
239 @type required: bool
240 @param package: name of package
241 @type package: str
242 @param env: override os.environ dictionary
243 @type env: dict
244 @param required: if True, directory must exist
245 @type required: bool
246 @return: Package subdirectory if package exist, otherwise None.
247 @rtype: str
248 @raise InvalidROSPkgException: if required is True and directory does not exist
249 """
250 if env is None:
251 env = os.environ
252 pkg_dir = get_pkg_dir(package, required, ros_root=env[ROS_ROOT])
253 return _get_pkg_subdir_by_dir(pkg_dir, subdir, required, env)
254
255
256
257
258
260 """
261 @param subdir: name of subdir -- these should be one of the
262 string constants, e.g. MSG_DIR
263 @type subdir: str
264 @return: path to resource in the specified subdirectory of the
265 package, or None if the package does not exists
266 @rtype: str
267 @raise roslib.packages.InvalidROSPkgException: If package does not exist
268 """
269 d = get_pkg_subdir(package, subdir, False)
270 if d is None:
271 raise InvalidROSPkgException(package)
272 return os.path.join(d, resource_name)
273
275 """
276 Internal routine to update global package directory cache
277
278 @return: True if cache is valid
279 @rtype: bool
280 """
281 if env is None:
282 env = os.environ
283 cache = _pkg_dir_cache
284 if cache:
285 return True
286 ros_root = env[ROS_ROOT]
287 ros_package_path = env.get(ROS_PACKAGE_PATH, '')
288 return _read_rospack_cache(cache, ros_root, ros_package_path)
289
291
292
293 cache.clear()
294
296 """
297 Read in rospack_cache data into cache. On-disk cache specifies a
298 ROS_ROOT and ROS_PACKAGE_PATH, which must match the requested
299 environment.
300
301 @param cache: empty dictionary to store package list in.
302 If no cache argument provided, will use internal _pkg_dir_cache
303 and will return cached answers if available.
304 The format of the cache is {package_name: dir_path, ros_root, ros_package_path}.
305 @type cache: {str: str, str, str}
306 @param ros_package_path: ROS_ROOT value
307 @type ros_root: str
308 @param ros_package_path: ROS_PACKAGE_PATH value or '' if not specified
309 @type ros_package_path: str
310 @return: True if on-disk cache matches and was loaded, false otherwise
311 @rtype: bool
312 """
313 try:
314 with open(os.path.join(rospkg.get_ros_home(), 'rospack_cache')) as f:
315 for l in f.readlines():
316 l = l[:-1]
317 if not len(l):
318 continue
319 if l[0] == '#':
320
321 if l.startswith('#ROS_ROOT='):
322 if not l[len('#ROS_ROOT='):] == ros_root:
323 return False
324 elif l.startswith('#ROS_PACKAGE_PATH='):
325 if not l[len('#ROS_PACKAGE_PATH='):] == ros_package_path:
326 return False
327 else:
328 cache[os.path.basename(l)] = l, ros_root, ros_package_path
329 return True
330 except:
331 pass
332
334 """
335 List ROS packages within the specified path.
336
337 Optionally, a cache dictionary can be provided, which will be
338 updated with the package->path mappings. list_pkgs_by_path() does
339 NOT returned cached results -- it only updates the cache.
340
341 @param path: path to list packages in
342 @type path: str
343 @param packages: list of packages to append to. If package is
344 already present in packages, it will be ignored.
345 @type packages: [str]
346 @param cache: (optional) package path cache to update. Maps package name to directory path.
347 @type cache: {str: str}
348 @return: complete list of package names in ROS environment. Same as packages parameter.
349 @rtype: [str]
350 """
351 if packages is None:
352 packages = []
353 if env is None:
354 env = os.environ
355
356 ros_root = env[ROS_ROOT]
357 ros_package_path = env.get(ROS_PACKAGE_PATH, '')
358
359 path = os.path.abspath(path)
360 for d, dirs, files in os.walk(path, topdown=True):
361 if MANIFEST_FILE in files:
362 package = os.path.basename(d)
363 if package not in packages:
364 packages.append(package)
365 if cache is not None:
366 cache[package] = d, ros_root, ros_package_path
367 del dirs[:]
368 continue
369 elif 'rospack_nosubdirs' in files:
370 del dirs[:]
371 continue
372
373 elif '.svn' in dirs:
374 dirs.remove('.svn')
375 elif '.git' in dirs:
376 dirs.remove('.git')
377
378 for sub_d in dirs:
379
380
381 sub_p = os.path.join(d, sub_d)
382 if os.path.islink(sub_p):
383 packages.extend(list_pkgs_by_path(sub_p, cache=cache))
384
385 return packages
386
387 -def find_node(pkg, node_type, rospack=None, catkin_packages_cache=None):
388 """
389 Warning: unstable API due to catkin.
390
391 Locate the executable that implements the node
392
393 :param node_type: type of node, ``str``
394 :returns: path to node or None if node is not in the package ``str``
395 :raises: :exc:rospkg.ResourceNotFound` If package does not exist
396 """
397
398 if rospack is None:
399 rospack = rospkg.RosPack()
400 return find_resource(pkg, node_type, filter_fn=_executable_filter,
401 rospack=rospack, catkin_packages_cache=catkin_packages_cache)
402
404 s = os.stat(test_path)
405 return (s.st_mode & (stat.S_IRUSR | stat.S_IXUSR) == (stat.S_IRUSR | stat.S_IXUSR))
406
407
409 """
410 env[CATKIN_BINARY_DIR] *must* be set
411
412 :param env: OS environment (defaults to os.environ if None/not set)
413 :param catkin_packages_cache: dictionary to read cache into.
414 Contents of dictionary will be replaced if cache is read. ``dict``
415
416 :raises: :exc:`KeyError` if env[CATKIN_BINARY_DIR] is not set
417 """
418 if env is None:
419 env=os.environ
420 prefix = env[CATKIN_BINARY_DIR]
421 cache_file = os.path.join(prefix, 'etc', 'packages.list')
422 if os.path.isfile(cache_file):
423 catkin_packages_cache.clear()
424 with open(cache_file, 'r') as f:
425 for l in f.readlines():
426 l = l.strip()
427
428
429 if not l:
430 continue
431 idx = l.find(' ')
432 catkin_packages_cache[l[:idx]] = os.path.join(prefix, l[idx+1:])
433
435 """
436 subroutine of find_resource
437 """
438 matches = []
439
440 if sys.platform in ['win32', 'cygwin']:
441
442
443
444
445
446
447
448
449
450
451
452
453 resource_name = resource_name.lower()
454 patterns = [resource_name, resource_name+'.exe', resource_name+'.bat']
455 for p, dirs, files in os.walk(d):
456
457 files = [f.lower() for f in files]
458 for name in patterns:
459 if name in files:
460 test_path = os.path.join(p, name)
461 if filter_fn is not None:
462 if filter_fn(test_path):
463 matches.append(test_path)
464 else:
465 matches.append(test_path)
466
467 to_prune = [x for x in dirs if x.startswith('.')]
468 for x in to_prune:
469 dirs.remove(x)
470 else:
471 for p, dirs, files in os.walk(d):
472 if resource_name in files:
473 test_path = os.path.join(p, resource_name)
474 if filter_fn is not None:
475 if filter_fn(test_path):
476 matches.append(test_path)
477 else:
478 matches.append(test_path)
479
480 to_prune = [x for x in dirs if x.startswith('.')]
481 for x in to_prune:
482 dirs.remove(x)
483 return [os.path.abspath(m) for m in matches]
484
485
486
487
488 -def find_resource(pkg, resource_name, filter_fn=None, rospack=None, catkin_packages_cache=None):
489 """
490 Warning: unstable API due to catkin.
491
492 Locate the file named resource_name in package, optionally
493 matching specified filter. find_resource() will return a list of
494 matches, but only for a given scope. If the resource is found in
495 the binary build directory, it will only return matches in that
496 directory; it will not return matches from the ROS_PACKAGE_PATH as
497 well in this case.
498
499 :param filter: function that takes in a path argument and
500 returns True if the it matches the desired resource, ``fn(str)``
501 :param rospack: `rospkg.RosPack` instance to use
502 :param catkin_packages_cache: dictionary for caching catkin packages.list
503 :returns: lists of matching paths for resource within a given scope, ``[str]``
504 :raises: :exc:`rospkg.ResourceNotFound` If package does not exist
505 """
506
507
508
509
510
511
512
513
514
515
516 if rospack is None:
517 rospack = rospkg.RosPack()
518 if catkin_packages_cache is None:
519 catkin_packages_cache = {}
520
521
522 pkg_path = rospack.get_path(pkg)
523
524
525 if CATKIN_BINARY_DIR in os.environ and not catkin_packages_cache:
526 _load_catkin_packages_cache(catkin_packages_cache)
527
528
529
530 matches = []
531 if pkg in catkin_packages_cache:
532 matches.extend(_find_resource(catkin_packages_cache[pkg], resource_name, filter_fn=filter_fn))
533 matches.extend(_find_resource(pkg_path, resource_name, filter_fn=filter_fn))
534
535 return list(set(matches))
536