# Copyright (c) 2011, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# Author Ken Conley/kwc@willowgarage.com
"""
Library for loading rosdep files from the ROS package/stack
filesystem.
"""
import os
import catkin_pkg.package
import rospkg
from .catkin_packages import VALID_DEPENDENCY_TYPES
from .loader import RosdepLoader
# Default view key is the view that packages that are not in stacks
# see. It is the root of all dependencies. It is superceded by an
# explicit underlay_key.
DEFAULT_VIEW_KEY = '*default*'
# Implementation details: this API was originally conceived under the
# rosdep 1 design. It has since been retrofitted for the rosdep 2
# design, which means it is a bit overbuilt. There really is no need
# for a notion of views for rospkg -- all rospkgs have the same view.
# It we be nice to refactor this API into something much, much
# simpler, which would probably involve merging RosPkgLoader and
# SourcesListLoader. RosPkgLoader would provide identification of
# resources and SourcesListLoader would build a *single* view that was
# no longer resource-dependent.
[docs]
class RosPkgLoader(RosdepLoader):
def __init__(self, rospack=None, rosstack=None, underlay_key=None, dependency_types=[]):
"""
:param underlay_key: If set, all views loaded by this loader
will depend on this key.
"""
if rospack is None:
rospack = rospkg.RosPack()
if rosstack is None:
rosstack = rospkg.RosStack()
self._rospack = rospack
self._rosstack = rosstack
self._rosdep_yaml_cache = {}
self._underlay_key = underlay_key
# cache computed list of loadable resources
self._loadable_resource_cache = None
self._catkin_packages_cache = None
default_dep_types = VALID_DEPENDENCY_TYPES - {'doc'}
self.include_dep_types = VALID_DEPENDENCY_TYPES.intersection(set(dependency_types)) if dependency_types else default_dep_types
[docs]
def load_view(self, view_name, rosdep_db, verbose=False):
"""
Load view data into *rosdep_db*. If the view has already
been loaded into *rosdep_db*, this method does nothing. If
view has no rosdep data, it will be initialized with an empty
data map.
:raises: :exc:`InvalidData` if view rosdep.yaml is invalid
:raises: :exc:`rospkg.ResourceNotFound` if view cannot be located
:returns: ``True`` if view was loaded. ``False`` if view
was already loaded.
"""
if rosdep_db.is_loaded(view_name):
return
if view_name not in self.get_loadable_views():
raise rospkg.ResourceNotFound(view_name)
elif view_name == 'invalid':
raise rospkg.ResourceNotFound('FOUND' + view_name + str(self.get_loadable_views()))
if verbose:
print('loading view [%s] with rospkg loader' % (view_name))
# chain into underlay if set
if self._underlay_key:
view_dependencies = [self._underlay_key]
else:
view_dependencies = []
# no rospkg view has actual data
rosdep_db.set_view_data(view_name, {}, view_dependencies, '<nodata>')
[docs]
def get_loadable_views(self):
"""
'Views' map to ROS stack names.
"""
return list(self._rosstack.list()) + [DEFAULT_VIEW_KEY]
[docs]
def get_loadable_resources(self):
"""
'Resources' map to ROS packages names.
"""
if not self._loadable_resource_cache:
self._loadable_resource_cache = list(self._rospack.list())
return self._loadable_resource_cache
def get_catkin_paths(self):
if not self._catkin_packages_cache:
def find_catkin_paths(src):
return [(x, src.get_path(x)) for x in
filter(lambda x: src.get_manifest(x).is_catkin, src.list())]
self._catkin_packages_cache = dict(find_catkin_paths(self._rospack))
self._catkin_packages_cache.update(find_catkin_paths(self._rosstack))
return self._catkin_packages_cache
[docs]
def get_rosdeps(self, resource_name, implicit=True):
"""
If *resource_name* is a stack, returns an empty list.
:raises: :exc:`rospkg.ResourceNotFound` if *resource_name* cannot be found.
"""
if resource_name in self.get_catkin_paths():
pkg = catkin_pkg.package.parse_package(self.get_catkin_paths()[resource_name])
pkg.evaluate_conditions(os.environ)
deps = sum((getattr(pkg, '{}_depends'.format(d)) for d in self.include_dep_types), [])
return [d.name for d in deps if d.evaluated_condition]
elif resource_name in self.get_loadable_resources():
rosdeps = set(self._rospack.get_rosdeps(resource_name, implicit=False))
if implicit:
# This resource is a manifest.xml, but it might depend on things with a package.xml
# Make sure they get a chance to evaluate conditions
for dep in self._rospack.get_depends(resource_name):
rosdeps = rosdeps.union(set(self.get_rosdeps(dep, implicit=True)))
return list(rosdeps)
elif resource_name in self._rosstack.list():
# stacks currently do not have rosdeps of their own, implicit or otherwise
return []
else:
raise rospkg.ResourceNotFound(resource_name)
def is_metapackage(self, resource_name):
if resource_name in self._rosstack.list():
m = self._rosstack.get_manifest(resource_name)
return m.is_catkin
return False
[docs]
def get_view_key(self, resource_name):
"""
Map *resource_name* to a view key. In rospkg, this maps the
DEFAULT_VIEW_KEY if *resource_name* exists.
:raises: :exc:`rospkg.ResourceNotFound`
"""
if (
resource_name in self.get_catkin_paths() or
resource_name in self.get_loadable_resources()
):
return DEFAULT_VIEW_KEY
else:
raise rospkg.ResourceNotFound(resource_name)