1# Copyright (c) 2011, Willow Garage, Inc. 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above copyright 10# notice, this list of conditions and the following disclaimer in the 11# documentation and/or other materials provided with the distribution. 12# * Neither the name of the Willow Garage, Inc. nor the names of its 13# contributors may be used to endorse or promote products derived from 14# this software without specific prior written permission. 15# 16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26# POSSIBILITY OF SUCH DAMAGE. 27 28# Author Ken Conley/kwc@willowgarage.com 29 30""" 31Library for loading rosdep files from the ROS package/stack 32filesystem. 33""" 34 35from __future__ import print_function 36 37import os 38 39import catkin_pkg.package 40import rospkg 41 42from .loader import RosdepLoader 43 44# Default view key is the view that packages that are not in stacks 45# see. It is the root of all dependencies. It is superceded by an 46# explicit underlay_key. 47DEFAULT_VIEW_KEY = '*default*' 48 49# Implementation details: this API was originally conceived under the 50# rosdep 1 design. It has since been retrofitted for the rosdep 2 51# design, which means it is a bit overbuilt. There really is no need 52# for a notion of views for rospkg -- all rospkgs have the same view. 53# It we be nice to refactor this API into something much, much 54# simpler, which would probably involve merging RosPkgLoader and 55# SourcesListLoader. RosPkgLoader would provide identification of 56# resources and SourcesListLoader would build a *single* view that was 57# no longer resource-dependent. 58 59 60class RosPkgLoader(RosdepLoader): 61 62 def __init__(self, rospack=None, rosstack=None, underlay_key=None): 63 """ 64 :param underlay_key: If set, all views loaded by this loader 65 will depend on this key. 66 """ 67 if rospack is None: 68 rospack = rospkg.RosPack() 69 if rosstack is None: 70 rosstack = rospkg.RosStack() 71 72 self._rospack = rospack 73 self._rosstack = rosstack 74 self._rosdep_yaml_cache = {} 75 self._underlay_key = underlay_key 76 77 # cache computed list of loadable resources 78 self._loadable_resource_cache = None 79 self._catkin_packages_cache = None 80 81 def load_view(self, view_name, rosdep_db, verbose=False): 82 """ 83 Load view data into *rosdep_db*. If the view has already 84 been loaded into *rosdep_db*, this method does nothing. If 85 view has no rosdep data, it will be initialized with an empty 86 data map. 87 88 :raises: :exc:`InvalidData` if view rosdep.yaml is invalid 89 :raises: :exc:`rospkg.ResourceNotFound` if view cannot be located 90 91 :returns: ``True`` if view was loaded. ``False`` if view 92 was already loaded. 93 """ 94 if rosdep_db.is_loaded(view_name): 95 return 96 if view_name not in self.get_loadable_views(): 97 raise rospkg.ResourceNotFound(view_name) 98 elif view_name == 'invalid': 99 raise rospkg.ResourceNotFound('FOUND' + view_name + str(self.get_loadable_views())) 100 if verbose: 101 print('loading view [%s] with rospkg loader' % (view_name)) 102 # chain into underlay if set 103 if self._underlay_key: 104 view_dependencies = [self._underlay_key] 105 else: 106 view_dependencies = [] 107 # no rospkg view has actual data 108 rosdep_db.set_view_data(view_name, {}, view_dependencies, '<nodata>') 109 110 def get_loadable_views(self): 111 """ 112 'Views' map to ROS stack names. 113 """ 114 return list(self._rosstack.list()) + [DEFAULT_VIEW_KEY] 115 116 def get_loadable_resources(self): 117 """ 118 'Resources' map to ROS packages names. 119 """ 120 if not self._loadable_resource_cache: 121 self._loadable_resource_cache = list(self._rospack.list()) 122 return self._loadable_resource_cache 123 124 def get_catkin_paths(self): 125 if not self._catkin_packages_cache: 126 def find_catkin_paths(src): 127 return map(lambda x: (x, src.get_path(x)), 128 filter(lambda x: src.get_manifest(x).is_catkin, src.list())) 129 self._catkin_packages_cache = dict(find_catkin_paths(self._rospack)) 130 self._catkin_packages_cache.update(find_catkin_paths(self._rosstack)) 131 return self._catkin_packages_cache 132 133 def get_rosdeps(self, resource_name, implicit=True): 134 """ 135 If *resource_name* is a stack, returns an empty list. 136 137 :raises: :exc:`rospkg.ResourceNotFound` if *resource_name* cannot be found. 138 """ 139 140 if resource_name in self.get_catkin_paths(): 141 pkg = catkin_pkg.package.parse_package(self.get_catkin_paths()[resource_name]) 142 pkg.evaluate_conditions(os.environ) 143 deps = pkg.build_depends + pkg.buildtool_depends + pkg.run_depends + pkg.test_depends 144 return [d.name for d in deps if d.evaluated_condition] 145 elif resource_name in self.get_loadable_resources(): 146 return self._rospack.get_rosdeps(resource_name, implicit=implicit) 147 elif resource_name in self._rosstack.list(): 148 # stacks currently do not have rosdeps of their own, implicit or otherwise 149 return [] 150 else: 151 raise rospkg.ResourceNotFound(resource_name) 152 153 def is_metapackage(self, resource_name): 154 if resource_name in self._rosstack.list(): 155 m = self._rosstack.get_manifest(resource_name) 156 return m.is_catkin 157 return False 158 159 def get_view_key(self, resource_name): 160 """ 161 Map *resource_name* to a view key. In rospkg, this maps the 162 DEFAULT_VIEW_KEY if *resource_name* exists. 163 164 :raises: :exc:`rospkg.ResourceNotFound` 165 """ 166 if ( 167 resource_name in self.get_catkin_paths() or 168 resource_name in self.get_loadable_resources() 169 ): 170 return DEFAULT_VIEW_KEY 171 else: 172 raise rospkg.ResourceNotFound(resource_name) 173