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 from catkin.find_in_workspaces import find_in_workspaces as catkin_find
50 import rospkg
51
52 import roslib.manifest
53
54 SRC_DIR = 'src'
55
56
57 ROS_PACKAGE_PATH = rospkg.environment.ROS_PACKAGE_PATH
58 ROS_ROOT = rospkg.environment.ROS_ROOT
59
61 """
62 Base class of package-related errors.
63 """
64 pass
66 """
67 Exception that indicates that a ROS package does not exist
68 """
69 pass
71 """
72 Exception that indicates that multiple ROS nodes by the same name are in the same package.
73 """
74 pass
75
76
77
78 MANIFEST_FILE = 'manifest.xml'
79 PACKAGE_FILE = 'package.xml'
80
81
82
83
84
86 """
87 Get the package that the directory is contained within. This is
88 determined by finding the nearest parent manifest.xml file. This
89 isn't 100% reliable, but symlinks can fool any heuristic that
90 relies on ROS_ROOT.
91 @param d: directory path
92 @type d: str
93 @return: (package_directory, package) of the specified directory, or None,None if not in a package
94 @rtype: (str, str)
95 """
96
97
98 parent = os.path.dirname(os.path.realpath(d))
99
100 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:
101 d = parent
102 parent = os.path.dirname(d)
103 if os.path.exists(os.path.join(d, MANIFEST_FILE)) or os.path.exists(os.path.join(d, PACKAGE_FILE)):
104 pkg = os.path.basename(os.path.abspath(d))
105 return d, pkg
106 return None, None
107
108 _pkg_dir_cache = {}
109
110 -def get_pkg_dir(package, required=True, ros_root=None, ros_package_path=None):
111 """
112 Locate directory package is stored in. This routine uses an
113 internal cache.
114
115 NOTE: cache does *not* rebuild if packages are relocated after
116 this process is initiated.
117
118 @param package: package name
119 @type package: str
120 @param required: if True, an exception will be raised if the
121 package directory cannot be located.
122 @type required: bool
123 @param ros_root: if specified, override ROS_ROOT
124 @type ros_root: str
125 @param ros_package_path: if specified, override ROS_PACKAGE_PATH
126 @type ros_package_path: str
127 @return: directory containing package or None if package cannot be found and required is False.
128 @rtype: str
129 @raise InvalidROSPkgException: if required is True and package cannot be located
130 """
131
132
133
134 try:
135 penv = os.environ.copy()
136 if ros_root:
137 ros_root = rospkg.environment._resolve_path(ros_root)
138 penv[ROS_ROOT] = ros_root
139 elif ROS_ROOT in os.environ:
140
141 ros_root = os.environ[ROS_ROOT]
142
143
144 rospack = 'rospack'
145
146 if ros_package_path is not None:
147 ros_package_path = rospkg.environment._resolve_paths(ros_package_path)
148 penv[ROS_PACKAGE_PATH] = ros_package_path
149 elif ROS_PACKAGE_PATH in os.environ:
150
151 ros_package_path = os.environ[ROS_PACKAGE_PATH]
152
153
154 if not _pkg_dir_cache:
155 _read_rospack_cache(_pkg_dir_cache, ros_root, ros_package_path)
156
157
158 if package in _pkg_dir_cache:
159 dir_, rr, rpp = _pkg_dir_cache[package]
160 if rr == ros_root and rpp == ros_package_path:
161 if os.path.isfile(os.path.join(dir_, MANIFEST_FILE)):
162 return dir_
163 else:
164
165 _invalidate_cache(_pkg_dir_cache)
166
167 rpout, rperr = Popen([rospack, 'find', package], \
168 stdout=PIPE, stderr=PIPE, env=penv).communicate()
169
170 pkg_dir = (rpout or '').strip()
171
172 if (isinstance(pkg_dir, bytes)):
173 pkg_dir = pkg_dir.decode()
174 if not pkg_dir:
175 raise InvalidROSPkgException("Cannot locate installation of package %s: %s. ROS_ROOT[%s] ROS_PACKAGE_PATH[%s]"%(package, rperr.strip(), ros_root, ros_package_path))
176
177 pkg_dir = os.path.normpath(pkg_dir)
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
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, rospack=rospack)
401
403 s = os.stat(test_path)
404 flags = stat.S_IRUSR | stat.S_IXUSR
405 if os.name == 'nt' and os.path.splitext(test_path)[1] == '.py':
406 flags = stat.S_IRUSR
407 return (s.st_mode & flags) == flags
408
410 """
411 subroutine of find_resource
412 """
413 matches = []
414
415 if sys.platform in ['win32', 'cygwin']:
416
417
418
419
420
421
422
423
424
425
426
427
428 resource_name = resource_name.lower()
429 patterns = [resource_name, resource_name+'.exe', resource_name+'.bat', resource_name+'.py']
430 for p, dirs, files in os.walk(d):
431
432 files = [f.lower() for f in files]
433 for name in patterns:
434 if name in files:
435 test_path = os.path.join(p, name)
436 if filter_fn is not None:
437 if filter_fn(test_path):
438 matches.append(test_path)
439 else:
440 matches.append(test_path)
441
442 to_prune = [x for x in dirs if x.startswith('.')]
443 for x in to_prune:
444 dirs.remove(x)
445 else:
446 for p, dirs, files in os.walk(d):
447 if resource_name in files:
448 test_path = os.path.join(p, resource_name)
449 if filter_fn is not None:
450 if filter_fn(test_path):
451 matches.append(test_path)
452 else:
453 matches.append(test_path)
454
455 to_prune = [x for x in dirs if x.startswith('.')]
456 for x in to_prune:
457 dirs.remove(x)
458 return [os.path.abspath(m) for m in matches]
459
460
461
462
463 -def find_resource(pkg, resource_name, filter_fn=None, rospack=None):
464 """
465 Warning: unstable API due to catkin.
466
467 Locate the file named resource_name in package, optionally
468 matching specified filter. find_resource() will return a list of
469 matches, but only for a given scope. If the resource is found in
470 the binary build directory, it will only return matches in that
471 directory; it will not return matches from the ROS_PACKAGE_PATH as
472 well in this case.
473
474 :param filter: function that takes in a path argument and
475 returns True if the it matches the desired resource, ``fn(str)``
476 :param rospack: `rospkg.RosPack` instance to use
477 :returns: lists of matching paths for resource within a given scope, ``[str]``
478 :raises: :exc:`rospkg.ResourceNotFound` If package does not exist
479 """
480
481
482
483
484
485
486
487
488
489
490
491 if rospack is None:
492 rospack = rospkg.RosPack()
493
494
495 pkg_path = rospack.get_path(pkg)
496
497 source_path_to_packages = rospack.get_custom_cache('source_path_to_packages', {})
498
499
500
501 matches = []
502 search_paths = catkin_find(
503 search_dirs=['libexec', 'share'], project=pkg, first_matching_workspace_only=True,
504 source_path_to_packages=source_path_to_packages)
505
506
507 if source_path_to_packages:
508 rospack.set_custom_cache('source_path_to_packages', source_path_to_packages)
509
510 for search_path in search_paths:
511 matches.extend(_find_resource(search_path, resource_name, filter_fn=filter_fn))
512
513 matches.extend(_find_resource(pkg_path, resource_name, filter_fn=filter_fn))
514
515
516 unique_matches = []
517 for match in matches:
518 if match not in unique_matches:
519 unique_matches.append(match)
520 return unique_matches
521