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