--- /dev/null
+Metadata-Version: 1.1
+Name: rospkg
+Version: 1.2.8
+Summary: ROS package library
+Home-page: http://wiki.ros.org/rospkg
+Author: Ken Conley
+Author-email: kwc@willowgarage.com
+License: BSD
+Description: Library for retrieving information about ROS packages and stacks.
+
+Keywords: ROS
+Platform: UNKNOWN
+Classifier: Programming Language :: Python
+Classifier: License :: OSI Approved :: BSD License
--- /dev/null
+rospkg
+-----
+
+Standalone Python library for the ROS package system.
+
+[ROS Packages Users/Developers Guide](http://docs.ros.org/independent/api/rospkg/html/)
--- /dev/null
+[egg_info]
+tag_build =
+tag_date = 0
+
--- /dev/null
+#!/usr/bin/env python
+
+import os
+import sys
+
+from setuptools import setup
+
+install_requires = ['catkin_pkg', 'PyYAML']
+
+if (
+ 'SKIP_PYTHON_MODULES' not in os.environ and
+ 'SKIP_PYTHON_SCRIPTS' not in os.environ
+):
+ install_requires.append('distro')
+
+kwargs = {
+ 'name': 'rospkg',
+ # same version as in:
+ # - src/rospkg/__init__.py
+ # - stdeb.cfg
+ 'version': '1.2.8',
+ 'packages': ['rospkg'],
+ 'package_dir': {'': 'src'},
+ 'entry_points': {
+ 'console_scripts': ['rosversion=rospkg.rosversion:main'],
+ },
+ 'install_requires': install_requires,
+ 'author': 'Ken Conley',
+ 'author_email': 'kwc@willowgarage.com',
+ 'url': 'http://wiki.ros.org/rospkg',
+ 'keywords': ['ROS'],
+ 'classifiers': [
+ 'Programming Language :: Python',
+ 'License :: OSI Approved :: BSD License'],
+ 'description': 'ROS package library',
+ 'long_description': """\
+ Library for retrieving information about ROS packages and stacks.
+ """,
+ 'license': 'BSD'
+}
+
+if sys.version_info[0] == 2 and sys.version_info[1] < 7:
+ kwargs['install_requires'].append('argparse')
+
+if 'SKIP_PYTHON_MODULES' in os.environ:
+ kwargs['packages'] = []
+ kwargs['package_dir'] = {}
+ kwargs['install_requires'].remove('catkin_pkg')
+if 'SKIP_PYTHON_SCRIPTS' in os.environ:
+ kwargs['name'] += '_modules'
+ kwargs['install_requires'].remove('catkin_pkg')
+ kwargs['scripts'] = []
+ kwargs['entry_points']['console_scripts'] = []
+
+setup(**kwargs)
--- /dev/null
+Metadata-Version: 1.1
+Name: rospkg
+Version: 1.2.8
+Summary: ROS package library
+Home-page: http://wiki.ros.org/rospkg
+Author: Ken Conley
+Author-email: kwc@willowgarage.com
+License: BSD
+Description: Library for retrieving information about ROS packages and stacks.
+
+Keywords: ROS
+Platform: UNKNOWN
+Classifier: Programming Language :: Python
+Classifier: License :: OSI Approved :: BSD License
--- /dev/null
+README.md
+setup.py
+src/rospkg/__init__.py
+src/rospkg/common.py
+src/rospkg/distro.py
+src/rospkg/environment.py
+src/rospkg/manifest.py
+src/rospkg/os_detect.py
+src/rospkg/rospack.py
+src/rospkg/rosversion.py
+src/rospkg/stack.py
+src/rospkg.egg-info/PKG-INFO
+src/rospkg.egg-info/SOURCES.txt
+src/rospkg.egg-info/dependency_links.txt
+src/rospkg.egg-info/entry_points.txt
+src/rospkg.egg-info/requires.txt
+src/rospkg.egg-info/top_level.txt
+test/test_rospkg_catkin_packages.py
+test/test_rospkg_common.py
+test/test_rospkg_distro.py
+test/test_rospkg_distro_vcs_config.py
+test/test_rospkg_environment.py
+test/test_rospkg_manifest.py
+test/test_rospkg_os_detect.py
+test/test_rospkg_packages.py
+test/test_rospkg_stacks.py
\ No newline at end of file
--- /dev/null
+[console_scripts]
+rosversion = rospkg.rosversion:main
+
--- /dev/null
+catkin_pkg
+PyYAML
+distro
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+"""
+Base ROS python library for manipulating ROS packages and stacks.
+"""
+
+from .common import MANIFEST_FILE, ResourceNotFound, STACK_FILE
+from .environment import get_etc_ros_dir, get_log_dir, get_ros_home, \
+ get_ros_package_path, get_ros_paths, get_ros_root, \
+ get_test_results_dir, on_ros_path
+from .manifest import InvalidManifest, Manifest, parse_manifest_file
+from .rospack import expand_to_packages, get_package_name, \
+ get_stack_version_by_dir, list_by_path, RosPack, RosStack
+
+# same version as in:
+# - setup.py
+# - stdeb.cfg
+__version__ = '1.2.8'
+
+__all__ = (
+ 'MANIFEST_FILE', 'ResourceNotFound', 'STACK_FILE',
+ 'get_etc_ros_dir', 'get_log_dir', 'get_ros_home',
+ 'get_ros_package_path', 'get_ros_paths', 'get_ros_root',
+ 'get_test_results_dir', 'on_ros_path',
+ 'InvalidManifest', 'Manifest', 'parse_manifest_file',
+ 'get_package_name', 'RosPack', 'RosStack',
+ 'list_by_path', 'expand_to_packages', 'get_stack_version_by_dir',
+)
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+"""
+Common definitions for rospkg modules.
+"""
+
+MANIFEST_FILE = 'manifest.xml'
+PACKAGE_FILE = 'package.xml'
+STACK_FILE = 'stack.xml'
+ROS_STACK = 'ros'
+
+
+class ResourceNotFound(Exception):
+ """
+ A ROS filesystem resource was not found.
+ """
+
+ def __init__(self, msg, ros_paths=None):
+ super(ResourceNotFound, self).__init__(msg)
+ self.ros_paths = ros_paths
+
+ def __str__(self):
+ s = self.args[0] # python 2.6
+ if self.ros_paths:
+ for i, p in enumerate(self.ros_paths):
+ s = s + '\nROS path [%s]=%s' % (i, p)
+ return s
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# Copyright (c) 2010, 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 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.
+
+"""
+Representation/model of rosdistro format.
+"""
+
+import os
+import re
+import string
+try:
+ from urllib.request import urlopen
+except ImportError:
+ from urllib2 import urlopen
+import yaml
+
+from .common import ResourceNotFound
+from .environment import get_etc_ros_dir
+
+TARBALL_URI_EVAL = 'http://svn.code.sf.net/p/ros-dry-releases/code/download/stacks/$STACK_NAME/$STACK_NAME-$STACK_VERSION/$STACK_NAME-$STACK_VERSION.tar.bz2'
+TARBALL_VERSION_EVAL = '$STACK_NAME-$STACK_VERSION'
+
+
+class InvalidDistro(Exception):
+ """
+ Distro file data does not match specification.
+ """
+ pass
+
+
+def distro_uri(distro_name):
+ """
+ Get distro URI of main ROS distribution files.
+
+ :param distro_name: name of distro, e.g. 'diamondback'
+ :returns: the SVN/HTTP URL of the specified distro. This function should only be used
+ with the main distros.
+ """
+ return "http://svn.code.sf.net/p/ros-dry-releases/code/trunk/distros/%s.rosdistro" % (distro_name)
+
+def expand_rule(rule, stack_name, stack_ver, release_name):
+ s = rule.replace('$STACK_NAME', stack_name)
+ if stack_ver:
+ s = s.replace('$STACK_VERSION', stack_ver)
+ s = s.replace('$RELEASE_NAME', release_name)
+ return s
+
+
+class DistroStack(object):
+ """Stores information about a stack release"""
+
+ def __init__(self, stack_name, stack_version, release_name, rules):
+ """
+ :param stack_name: Name of stack
+ :param stack_version: Version number of stack.
+ :param release_name: name of distribution release. Necessary for rule expansion.
+ :param rules: raw '_rules' data. Will be converted into appropriate vcs config instance.
+ """
+ self.name = stack_name
+ self.version = stack_version
+ self.release_name = release_name
+ self._rules = rules
+ self.repo = rules.get('repo', None)
+ self.vcs_config = load_vcs_config(self._rules, self._expand_rule)
+
+ def _expand_rule(self, rule):
+ """
+ Perform variable substitution on stack rule.
+ """
+ return expand_rule(rule, self.name, self.version, self.release_name)
+
+ def __eq__(self, other):
+ try:
+ return self.name == other.name and \
+ self.version == other.version and \
+ self.vcs_config == other.vcs_config
+ except AttributeError:
+ return False
+
+
+class Variant(object):
+ """
+ A variant defines a specific set of stacks ("metapackage", in Debian
+ parlance). For example, "base", "pr2". These variants can extend
+ another variant.
+ """
+
+ def __init__(self, variant_name, extends, stack_names, stack_names_implicit):
+ """
+ :param variant_name: name of variant to load from distro file, ``str``
+ :param stack_names_implicit: full list of stacks implicitly included in this variant, ``[str]``
+ :param raw_data: raw rosdistro data for this variant
+ """
+ self.name = variant_name
+ self.extends = extends
+ self._stack_names = stack_names
+ self._stack_names_implicit = stack_names_implicit
+
+ def get_stack_names(self, implicit=True):
+ if implicit:
+ return self._stack_names_implicit
+ else:
+ return self._stack_names
+
+ # stack_names includes implicit stack names. Use get_stack_names()
+ # to get explicit only
+ stack_names = property(get_stack_names)
+
+
+class Distro(object):
+ """
+ Store information in a rosdistro file.
+ """
+
+ def __init__(self, stacks, variants, release_name, version, raw_data):
+ """
+ :param stacks: dictionary mapping stack names to :class:`DistroStack` instances
+ :param variants: dictionary mapping variant names to :class:`Variant` instances
+ :param release_name: name of release, e.g. 'diamondback'
+ :param version: version number of release
+ :param raw_data: raw dictionary representation of a distro
+ """
+ self._stacks = stacks
+ self.variants = variants
+ self.release_name = release_name
+ self.version = version
+ self.raw_data = raw_data
+
+ def get_stacks(self, released=False):
+ """
+ :param released: only included released stacks
+ :returns: dictionary of stack names to :class:`DistroStack` instances in
+ this distro.
+ """
+ if released:
+ return self._get_released_stacks()
+ else:
+ return self._stacks.copy()
+
+ def _get_released_stacks(self):
+ retval = {}
+ for s, obj in self._stacks.items():
+ if obj.version:
+ retval[s] = obj
+ return retval
+
+ # gets map of all stacks
+ stacks = property(get_stacks)
+ # gets maps of released stacks
+ released_stacks = property(_get_released_stacks)
+
+
+def load_distro(source_uri):
+ """
+ :param source_uri: source URI of distro file, or path to distro
+ file. Filename has precedence in resolution.
+
+ :raises: :exc:`InvalidDistro` If distro file is invalid
+ :raises: :exc:`ResourceNotFound` If file at *source_uri* is not found
+ """
+ try:
+ # parse rosdistro yaml
+ if os.path.isfile(source_uri):
+ # load rosdistro file
+ with open(source_uri) as f:
+ raw_data = yaml.load(f.read())
+ else:
+ try:
+ request = urlopen(source_uri)
+ except Exception as e:
+ raise ResourceNotFound('%s (%s)' % (str(e), source_uri))
+ try:
+ raw_data = yaml.load(request)
+ except ValueError:
+ raise ResourceNotFound(source_uri)
+ if not type(raw_data) == dict:
+ raise InvalidDistro("Distro must be a dictionary: %s" % (source_uri))
+ except yaml.YAMLError as e:
+ raise InvalidDistro(str(e))
+
+ try:
+ version = _distro_version(raw_data.get('version', '0'))
+ release_name = raw_data['release']
+ stacks = _load_distro_stacks(raw_data, release_name)
+ variants = _load_variants(raw_data.get('variants', {}), stacks)
+ return Distro(stacks, variants, release_name, version, raw_data)
+ except KeyError as e:
+ raise InvalidDistro("distro is missing required '%s' key" % (str(e)))
+
+
+def _load_variants(raw_data, stacks):
+ if not raw_data:
+ return {}
+ all_variants_raw_data = {}
+ for v in raw_data:
+ if type(v) != dict or len(v.keys()) != 1:
+ raise InvalidDistro("invalid variant spec: %s" % v)
+ variant_name = list(v.keys())[0]
+ all_variants_raw_data[variant_name] = v[variant_name]
+ variants = {}
+ for variant_name in all_variants_raw_data.keys():
+ variants[variant_name] = _load_variant(variant_name, all_variants_raw_data)
+
+ # Disabling validation to support variants which include wet packages.
+ # validate
+ # for stack_name in variants[variant_name].get_stack_names(implicit=False):
+ # if stack_name not in stacks:
+ # raise InvalidDistro("variant [%s] refers to non-existent stack [%s]"%(variant_name, stack_name))
+ return variants
+
+
+def _load_variant(variant_name, all_variants_raw_data):
+ variant_raw_data = all_variants_raw_data[variant_name]
+ stack_names_implicit = list(variant_raw_data.get('stacks', []))
+ extends = variant_raw_data.get('extends', [])
+ if isinstance(extends, str):
+ extends = [extends]
+ for e in extends:
+ parent_variant = _load_variant(e, all_variants_raw_data)
+ stack_names_implicit = parent_variant.get_stack_names(implicit=True) + stack_names_implicit
+ return Variant(variant_name, extends, variant_raw_data.get('stacks', []), stack_names_implicit)
+
+
+def _load_distro_stacks(distro_doc, release_name):
+ """
+ :param distro_doc: dictionary form of rosdistro file, `dict`
+ :returns: dictionary of stack names to :class:`DistroStack` instances, `{str : DistroStack}`
+ :raises: :exc:`InvalidDistro` if distro_doc format is invalid
+ """
+
+ # load stacks and expand out uri rules
+ stacks = {}
+ try:
+ stack_props = distro_doc['stacks']
+ stack_props = stack_props or {}
+ stack_names = [x for x in stack_props.keys() if not x[0] == '_']
+ except KeyError:
+ raise InvalidDistro("distro is missing required 'stacks' key")
+ for stack_name in stack_names:
+ stack_version = stack_props[stack_name].get('version', None)
+ rules = _get_rules(distro_doc, stack_name)
+ if not rules:
+ raise InvalidDistro("no VCS rules for stack [%s]" % (stack_name))
+ stacks[stack_name] = DistroStack(stack_name, stack_version, release_name, rules)
+ return stacks
+
+
+def _distro_version(version_val):
+ """
+ Parse distro version value, converting SVN revision to version value if necessary
+ """
+ version_val = str(version_val)
+ # check for no keyword sub
+ if version_val == '$Revision$':
+ return 0
+ m = re.search(r'\$Revision:\s*([0-9]*)\s*\$', version_val)
+ if m is not None:
+ version_val = 'r' + m.group(1)
+
+ # Check that is a valid version string
+ valid = string.ascii_letters + string.digits + '.+~'
+ if False in (c in valid for c in version_val):
+ raise InvalidDistro("Version string %s not valid" % version_val)
+ return version_val
+
+
+def distro_to_rosinstall(distro, branch, variant_name=None, implicit=True, released_only=True, anonymous=True):
+ """
+ :param branch: branch to convert for
+ :param variant_name: if not None, only include stacks in the specified variant.
+ :param implicit: if variant_name is provided, include full (recursive) dependencies of variant, default True
+ :param released_only: only included released stacks, default True.
+ :param anonymous: create for anonymous access rules
+ :returns: rosinstall data in Python list format, ``[dict]``
+
+ :raises: :exc:`KeyError` If branch is invalid or if distro is mis-configured
+ """
+ variant = distro.variants.get(variant_name, None)
+ if variant_name:
+ stack_names = set(variant.get_stack_names(implicit=implicit))
+ else:
+ stack_names = distro.released_stacks.keys()
+ rosinstall_data = []
+ for s in stack_names:
+ if released_only and s not in distro.released_stacks:
+ continue
+ rosinstall_data.extend(distro.stacks[s].vcs_config.to_rosinstall(s, branch, anonymous))
+ return rosinstall_data
+
+################################################################################
+
+
+def _get_rules(distro_doc, stack_name):
+ """
+ Retrieve rules from distro_doc for specified stack. This operates on
+ the raw distro dictionary document.
+
+ :param distro_doc: rosdistro document, ``dict``
+ :param stack_name: name of stack to get rules for, ``str``
+ """
+ # top-level named section
+ named_rules_d = distro_doc.get('_rules', {})
+
+ # other rules to search
+ rules_d = [distro_doc.get('stacks', {}),
+ distro_doc.get('stacks', {}).get(stack_name, {})]
+ rules_d = [d for d in rules_d if '_rules' in d]
+
+ # last rules wins
+ if not rules_d:
+ return None
+ rules_d = rules_d[-1]
+
+ update_r = rules_d.get('_rules', {})
+ if type(update_r) == str:
+ try:
+ update_r = named_rules_d[update_r]
+ except KeyError:
+ raise InvalidDistro("no _rules named [%s]" % (update_r))
+ if not type(update_r) == dict:
+ raise InvalidDistro("invalid rules: %s %s" % (update_r, type(update_r)))
+ return update_r
+
+################################################################################
+
+
+class VcsConfig(object):
+ """
+ Base representation of a rosdistro VCS rules configuration.
+ """
+
+ def __init__(self, type_):
+ self.type = type_
+ self.tarball_url = self.tarball_version = None
+
+ def to_rosinstall(self, local_name, branch, anonymous):
+ uri, version_tag = self.get_branch(branch, anonymous)
+ if branch == 'release-tar':
+ type_ = 'tar'
+ else:
+ type_ = self.type
+ if version_tag:
+ return [{type_: {"uri": uri, 'local-name': local_name, 'version': version_tag}}]
+ else:
+ return [({type_: {"uri": uri, 'local-name': local_name}})]
+
+ def load(self, rules, rule_eval):
+ """
+ Initialize fields of this class based on the raw rosdistro
+ *rules* data after applying *rule_eval* function (e.g. to
+ replace variables in rules).
+
+ :param rules: raw rosdistro rules entry, ``dict``
+ :param rule_eval: function to evaluate rule values, ``fn(str) -> str``
+ """
+ self.tarball_url = rule_eval(TARBALL_URI_EVAL)
+ self.tarball_version = rule_eval(TARBALL_VERSION_EVAL)
+
+ def get_branch(self, branch, anonymous):
+ """
+ :raises: :exc:`ValueError` If branch is invalid
+ """
+ if branch == 'release-tar':
+ return self.tarball_url, self.tarball_version
+ else:
+ raise ValueError(branch)
+
+ def __eq__(self, other):
+ return self.type == other.type and \
+ self.tarball_url == other.tarball_url
+
+
+class DvcsConfig(VcsConfig):
+ """
+ Configuration information for a distributed VCS-style repository.
+
+ Configuration fields:
+
+ * ``repo_uri``: base URI of repo
+ * ``dev_branch``: git branch the code is developed
+ * ``distro_tag``: a tag of the latest released code for a specific ROS distribution
+ * ``release_tag``: a tag of the code for a specific release
+ """
+
+ def __init__(self, type_):
+ super(DvcsConfig, self).__init__(type_)
+ self.repo_uri = self.anon_repo_uri = None
+ self.dev_branch = self.distro_tag = self.release_tag = None
+
+ def load(self, rules, rule_eval):
+ super(DvcsConfig, self).load(rules, rule_eval)
+
+ self.repo_uri = rule_eval(rules['uri'])
+ if 'anon-uri' in rules:
+ self.anon_repo_uri = rule_eval(rules['anon-uri'])
+ else:
+ self.anon_repo_uri = self.repo_uri
+ self.dev_branch = rule_eval(rules['dev-branch'])
+ self.distro_tag = rule_eval(rules['distro-tag'])
+ self.release_tag = rule_eval(rules['release-tag'])
+
+ def get_branch(self, branch, anonymous):
+ """
+ :raises: :exc:`KeyError` Invalid branch parameter
+ """
+ if branch == 'release-tar':
+ return super(DvcsConfig, self).get_branch(branch, anonymous)
+ elif branch == 'devel':
+ version_tag = self.dev_branch
+ elif branch == 'distro':
+ version_tag = self.distro_tag
+ elif branch == 'release':
+ version_tag = self.release_tag
+ else:
+ raise ValueError("invalid branch spec [%s]" % (branch))
+ # occurs, for example, with unreleased stacks. Only devel is valid
+ if version_tag is None:
+ raise ValueError("branch [%s] is not available for this config" % (branch))
+ if anonymous:
+ return self.anon_repo_uri, version_tag
+ else:
+ return self.repo_uri, version_tag
+
+ def __eq__(self, other):
+ return super(DvcsConfig, self).__eq__(other) and \
+ self.repo_uri == other.repo_uri and \
+ self.anon_repo_uri == other.anon_repo_uri and \
+ self.dev_branch == other.dev_branch and \
+ self.release_tag == other.release_tag and \
+ self.distro_tag == other.distro_tag
+
+
+class GitConfig(DvcsConfig):
+ """
+ Configuration information about an GIT repository. See parent class :class:`DvcsConfig` for more API information.
+ """
+
+ def __init__(self):
+ super(GitConfig, self).__init__('git')
+
+
+class HgConfig(DvcsConfig):
+ """
+ Configuration information about a Mercurial repository. See parent class :class:`DvcsConfig` for more API information.
+ """
+
+ def __init__(self):
+ super(HgConfig, self).__init__('hg')
+
+
+class BzrConfig(DvcsConfig):
+ """
+ Configuration information about an BZR repository. See parent class :class:`DvcsConfig` for more API information.
+ """
+
+ def __init__(self):
+ super(BzrConfig, self).__init__('bzr')
+
+
+class SvnConfig(VcsConfig):
+ """
+ Configuration information about an SVN repository.
+
+ Configuration fields:
+
+ * ``dev``: where the code is developed
+ * ``distro_tag``: a tag of the code for a specific ROS distribution
+ * ``release_tag``: a tag of the code for a specific release
+ """
+
+ def __init__(self):
+ super(SvnConfig, self).__init__('svn')
+ self.dev = None
+ self.distro_tag = None
+ self.release_tag = None
+
+ # anonymously readable version of URLs above. Some repos have
+ # separate URLs for read-only vs. writable versions of repo
+ # and many tools need to be able to read repos without
+ # providing credentials.
+ self.anon_dev = None
+ self.anon_distro_tag = None
+ self.anon_release_tag = None
+
+ def load(self, rules, rule_eval):
+ super(SvnConfig, self).load(rules, rule_eval)
+ for k in ['dev', 'distro-tag', 'release-tag']:
+ if k not in rules:
+ raise KeyError("svn rules missing required %s key: %s" % (k, rules))
+ self.dev = rule_eval(rules['dev'])
+ self.distro_tag = rule_eval(rules['distro-tag'])
+ self.release_tag = rule_eval(rules['release-tag'])
+
+ # specify urls that are safe to anonymously read
+ # from. Users must supply a complete set.
+ if 'anon-dev' in rules:
+ self.anon_dev = rule_eval(rules['anon-dev'])
+ self.anon_distro_tag = rule_eval(rules['anon-distro-tag'])
+ self.anon_release_tag = rule_eval(rules['anon-release-tag'])
+ else:
+ # if no login credentials, assume that anonymous is
+ # same as normal keys.
+ self.anon_dev = self.dev
+ self.anon_distro_tag = self.distro_tag
+ self.anon_release_tag = self.release_tag
+
+ def get_branch(self, branch, anonymous):
+ """
+ :raises: :exc:`ValueError` If branch is invalid
+ """
+ if branch == 'release-tar':
+ return super(SvnConfig, self).get_branch(branch, anonymous)
+ else:
+ key_map = dict(devel='dev', distro='distro_tag', release='release_tag')
+ if branch not in key_map:
+ raise KeyError("invalid branch spec [%s]" % (branch))
+ attr_name = key_map[branch]
+ if anonymous:
+ attr_name = 'anon_' + attr_name
+ uri = getattr(self, attr_name)
+ # occurs, for example, with unreleased stacks. Only devel is valid
+ if uri is None:
+ raise ValueError("branch [%s] is not available for this config" % (branch))
+ return uri, None
+
+ def __eq__(self, other):
+ return super(SvnConfig, self).__eq__(other) and \
+ self.dev == other.dev and \
+ self.distro_tag == other.distro_tag and \
+ self.release_tag == other.release_tag and \
+ self.anon_dev == other.anon_dev and \
+ self.anon_distro_tag == other.anon_distro_tag and \
+ self.anon_release_tag == other.anon_release_tag
+
+
+_vcs_configs = {
+ 'svn': SvnConfig,
+ 'git': GitConfig,
+ 'hg': HgConfig,
+ 'bzr': BzrConfig,
+}
+
+
+def get_vcs_configs():
+ """
+ :returns: Dictionary of supported :class:`VcsConfig` instances.
+ Key is the VCS type name, e.g. 'svn'. ``{str: VcsConfig}``
+ """
+ return _vcs_configs.copy()
+
+
+def load_vcs_config(rules, rule_eval):
+ """
+ Factory for creating :class:`VcsConfig` subclass based on
+ rosdistro _rules data.
+
+ :param rules: rosdistro rules data
+ :param rules_eval: Function to apply to rule values, e.g. to
+ convert variables. ``fn(str)->str``
+ :returns: :class:`VcsConfig` subclass instance with interpreted rules data.
+ """
+ vcs_config = None
+ for k, clazz in _vcs_configs.items():
+ if k in rules:
+ vcs_config = clazz()
+ vcs_config.load(rules[k], rule_eval)
+ break
+ return vcs_config
+
+
+def _current_distro_electric_parse_roscore(roscore_file):
+ if not os.path.exists(roscore_file):
+ return None
+ import xml.dom.minidom
+ try:
+ dom = xml.dom.minidom.parse(roscore_file)
+ tags = dom.getElementsByTagName("param")
+ for t in tags:
+ if t.hasAttribute('name') and t.getAttribute('name') == 'rosdistro':
+ return t.getAttribute('value')
+ except:
+ return None
+
+
+# for < fuerte, retrieve from roscore file
+def _current_distro_electric(env=None):
+ if env is None:
+ env = os.environ
+ from . import RosPack, get_ros_paths
+ rospack = RosPack(get_ros_paths(env))
+ # there's some chance that the location of this file changes in the future
+ try:
+ roscore_file = os.path.join(rospack.get_path('roslaunch'), 'roscore.xml')
+ return _current_distro_electric_parse_roscore(roscore_file)
+ except:
+ return None
+
+
+def current_distro_codename(env=None):
+ """
+ Get the currently active ROS distribution codename, e.g. 'fuerte'
+
+ :param env: override os.environ, ``dict``
+ """
+ if env is None:
+ env = os.environ
+
+ # ROS_DISTRO is only used in ros catkin buildspace. It is not
+ # meant to be well publicized and thus is not declared in
+ # rospkg.environment.
+ if 'ROS_DISTRO' in env:
+ return env['ROS_DISTRO']
+
+ # check for /etc/ros/distro file
+ distro_name = None
+ etc_ros = get_etc_ros_dir(env=env)
+ distro_file = os.path.join(etc_ros, 'distro')
+ if os.path.isfile(distro_file):
+ with open(distro_file, 'r') as f:
+ distro_name = f.read().strip()
+
+ # fallback logic for pre-Fuerte
+ if distro_name is None:
+ distro_name = _current_distro_electric(env=env)
+
+ return distro_name
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+"""
+Access information about ROS package system environment variables.
+"""
+
+import os
+
+# Enviroment Variables
+
+# Global, usually set in setup
+ROS_ROOT = "ROS_ROOT"
+ROS_PACKAGE_PATH = "ROS_PACKAGE_PATH"
+ROS_HOME = "ROS_HOME"
+
+# override directory path to /etc/ros
+ROS_ETC_DIR = "ROS_ETC_DIR"
+
+# directory in which log files are written
+ROS_LOG_DIR = "ROS_LOG_DIR"
+# directory in which test result files are written
+ROS_TEST_RESULTS_DIR = "ROS_TEST_RESULTS_DIR"
+
+
+# Utilities
+def _resolve_path(p):
+ """
+ @param path: path string
+ @type path: str
+ Catch-all utility routine for fixing ROS environment variables that
+ are a single path (e.g. ROS_ROOT). Currently this just expands
+ tildes to home directories, but in the future it may encode other
+ behaviors.
+ """
+ if p and p[0] == '~':
+ return os.path.expanduser(p)
+ return p
+
+
+def _resolve_paths(paths):
+ """
+ Catch-all utility routine for fixing ROS environment variables
+ that are paths (e.g. ROS_PACKAGE_PATH). Currently this just
+ expands tildes to home directories, but in the future it may
+ encode other behaviors.
+
+ :param paths: path string with OS-defined separator (i.e. ':' for Linux), ``str``
+ """
+ splits = [p for p in paths.split(os.pathsep) if p]
+ return os.pathsep.join([_resolve_path(p) for p in splits])
+
+
+def get_ros_paths(env=None):
+ """
+ Get an ordered list of ROS paths to search for ROS packages,
+ stacks, and other resources. This is generally computed from
+ :envvar:`ROS_ROOT` and :envvar:`ROS_PACKAGE_PATH`.
+
+ :param env: override environment dictionary
+ """
+ if env is None:
+ env = os.environ
+ return _compute_package_paths(get_ros_root(env), get_ros_package_path(env))
+
+
+def get_ros_root(env=None):
+ """
+ Get the current :envvar:`ROS_ROOT`.
+
+ :param env: override environment dictionary, ``dict``
+ """
+ if env is None:
+ env = os.environ
+ ros_root = env.get(ROS_ROOT, None)
+ if ros_root:
+ ros_root = os.path.normpath(ros_root)
+ return ros_root
+
+
+def get_ros_package_path(env=None):
+ """
+ Get the current ROS_PACKAGE_PATH.
+ :param env: (optional) environment override, ``dict``
+ """
+ if env is None:
+ env = os.environ
+ return env.get(ROS_PACKAGE_PATH, None)
+
+
+def get_ros_home(env=None):
+ """
+ Get directory location of '.ros' directory (aka ROS home).
+ possible locations for this. The :envvar:`ROS_HOME` environment
+ variable has priority. If :envvar:`ROS_HOME` is not set,
+ ``$HOME/.ros`` is used.
+
+ :param env: override ``os.environ`` dictionary, ``dict``
+ :returns: path to use use for log file directory, ``str``
+ """
+ if env is None:
+ env = os.environ
+ if ROS_HOME in env:
+ return env[ROS_HOME]
+ else:
+ # slightly more robust than $HOME
+ return os.path.join(os.path.expanduser('~'), '.ros')
+
+
+def get_log_dir(env=None):
+ """
+ Get directory to use for writing log files. There are multiple
+ possible locations for this. The ROS_LOG_DIR environment variable
+ has priority. If that is not set, then ROS_HOME/log is used. If
+ ROS_HOME is not set, $HOME/.ros/log is used.
+
+ :param env: override os.environ dictionary, ``dict``
+ :returns: path to use use for log file directory, ``str``
+ """
+ if env is None:
+ env = os.environ
+ if ROS_LOG_DIR in env:
+ return env[ROS_LOG_DIR]
+ else:
+ return os.path.join(get_ros_home(env), 'log')
+
+
+def get_test_results_dir(env=None):
+ """
+ Get directory to use for writing test result files. There are
+ multiple possible locations for this. The
+ :envvar:`ROS_TEST_RESULTS_DIR` environment variable has
+ priority. If that is set, :envvar:`ROS_TEST_RESULTS_DIR` is
+ returned. If :envvar:`ROS_TEST_RESULTS_DIR` is not set, then
+ :envvar:`ROS_HOME`/test_results is used. If :envvar:`ROS_HOME` is
+ not set, ``$HOME/.ros/test_results`` is used.
+
+ :param env: environment dictionary (defaults to ``os.environ``), ``dict``
+ :returns: path to use use for log file directory, ``str``
+ """
+ if env is None:
+ env = os.environ
+
+ if ROS_TEST_RESULTS_DIR in env:
+ return env[ROS_TEST_RESULTS_DIR]
+ else:
+ return os.path.join(get_ros_home(env), 'test_results')
+
+
+def _compute_package_paths(ros_root, ros_package_path):
+ """
+ Get the paths to search for packages in normal precedence order (i.e. first path wins).
+
+ :param ros_root: value of ROS_ROOT parameter, ``str``
+ :param ros_package_path: value of ROS_PACKAGE_PATH parameter, ``str``
+ :returns: paths to search in reverse order of precedence, ``[str]``
+ """
+ paths = []
+ if ros_root:
+ paths.append(ros_root)
+ if ros_package_path:
+ paths.extend([x for x in ros_package_path.split(os.pathsep) if x.strip()])
+ return paths
+
+
+def on_ros_path(p, env=None):
+ """
+ Check to see if filesystem path is on paths specified in ROS
+ environment (:envvar:`ROS_ROOT`, :envvar:`ROS_PACKAGE_PATH`).
+
+ :param p: path, ``str``
+ :return: ``True`` if p is on the ROS path, ``bool``
+ """
+ if env is None:
+ env = os.environ
+
+ package = os.path.realpath(_resolve_path(p))
+ # filter out non-paths (e.g. if no ROS environment is configured)
+ paths = get_ros_paths(env)
+ paths = [os.path.realpath(_resolve_path(x)) for x in paths]
+ return bool([x for x in paths if package == x or package.startswith(x + os.sep)])
+
+
+def get_etc_ros_dir(env=None):
+ """
+ Get directory location of ``/etc/ros`` directory. The
+ :envvar:`ROS_ETC_DIR` environment variable has priority. If
+ :envvar:`ROS_ETC_DIR` is not set, ``/etc/ros`` is used.
+
+ :param env: override environment dictionary
+ :return: path to use use for log file directory
+ """
+ if env is None:
+ env = os.environ
+ if ROS_ETC_DIR in env:
+ return env[ROS_ETC_DIR]
+ else:
+ return '/etc/ros'
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# Copyright (c) 2008, 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 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.
+
+"""
+Library for processing 'manifest' files, i.e. manifest.xml and
+stack.xml.
+"""
+
+import os
+import sys
+import xml.dom.minidom as dom
+
+from .common import MANIFEST_FILE, PACKAGE_FILE, STACK_FILE
+
+# stack.xml and manifest.xml have the same internal tags right now
+REQUIRED = ['license']
+ALLOWXHTML = ['description']
+OPTIONAL = ['author', 'logo', 'url', 'brief', 'description', 'status',
+ 'notes', 'depend', 'rosdep', 'export', 'review',
+ 'versioncontrol', 'platform', 'version', 'rosbuild2',
+ 'catkin']
+VALID = REQUIRED + OPTIONAL
+
+
+class InvalidManifest(Exception):
+ pass
+
+
+def _get_nodes_by_name(n, name):
+ return [t for t in n.childNodes if t.nodeType == t.ELEMENT_NODE and t.tagName == name]
+
+
+def _check_optional(name, allowXHTML=False, merge_multiple=False):
+ """
+ Validator for optional elements.
+
+ :raise: :exc:`InvalidManifest` If validation fails
+ """
+ def check(n, filename):
+ n = _get_nodes_by_name(n, name)
+ if len(n) > 1 and not merge_multiple:
+ raise InvalidManifest("Invalid manifest file [%s]: must have a single '%s' element" % (filename, name))
+ if n:
+ values = []
+ for child in n:
+ if allowXHTML:
+ values.append(''.join([x.toxml() for x in child.childNodes]))
+ else:
+ values.append(_get_text(child.childNodes).strip())
+ return ', '.join(values)
+ return check
+
+
+def _check_required(name, allowXHTML=False, merge_multiple=False):
+ """
+ Validator for required elements.
+
+ :raise: :exc:`InvalidManifest` If validation fails
+ """
+ def check(n, filename):
+ n = _get_nodes_by_name(n, name)
+ if not n:
+ return ''
+ if len(n) != 1 and not merge_multiple:
+ raise InvalidManifest("Invalid manifest file: must have only one '%s' element" % name)
+ values = []
+ for child in n:
+ if allowXHTML:
+ values.append(''.join([x.toxml() for x in child.childNodes]))
+ else:
+ values.append(_get_text(child.childNodes).strip())
+ return ', '.join(values)
+ return check
+
+
+def _check_platform(n, filename):
+ """
+ Validator for manifest platform.
+ :raise: :exc:`InvalidManifest` If validation fails
+ """
+ platforms = _get_nodes_by_name(n, 'platform')
+ try:
+ vals = [(p.attributes['os'].value, p.attributes['version'].value, p.getAttribute('notes')) for p in platforms]
+ except KeyError as e:
+ raise InvalidManifest("<platform> tag is missing required '%s' attribute" % str(e))
+ return [Platform(*v) for v in vals]
+
+
+def _check_depends(type_, n, filename):
+ """
+ Validator for manifest depends.
+ :raise: :exc:`InvalidManifest` If validation fails
+ """
+ nodes = _get_nodes_by_name(n, 'depend')
+ # TDS 20110419: this is a hack.
+ # rosbuild2 has a <depend thirdparty="depname"/> tag,
+ # which is confusing this subroutine with
+ # KeyError: 'package'
+ # for now, explicitly don't consider thirdparty depends
+ depends = [e.attributes for e in nodes if 'thirdparty' not in e.attributes.keys()]
+ try:
+ depend_names = [d[type_].value for d in depends]
+ except KeyError:
+ raise InvalidManifest("Invalid manifest file [%s]: depends is missing '%s' attribute" % (filename, type_))
+
+ return [Depend(name, type_) for name in depend_names]
+
+
+def _check_rosdeps(n, filename):
+ """
+ Validator for stack rosdeps.
+
+ :raises: :exc:`InvalidManifest` If validation fails
+ """
+ try:
+ nodes = _get_nodes_by_name(n, 'rosdep')
+ rosdeps = [e.attributes for e in nodes]
+ names = [d['name'].value for d in rosdeps]
+ return [RosDep(n) for n in names]
+ except KeyError:
+ raise InvalidManifest("invalid rosdep tag in [%s]" % (filename))
+
+
+def _attrs(node):
+ attrs = {}
+ for k in node.attributes.keys():
+ attrs[k] = node.attributes.get(k).value
+ return attrs
+
+
+def _check_exports(n, filename):
+ ret_val = []
+ for e in _get_nodes_by_name(n, 'export'):
+ elements = [c for c in e.childNodes if c.nodeType == c.ELEMENT_NODE]
+ ret_val.extend([Export(t.tagName, _attrs(t), _get_text(t.childNodes)) for t in elements])
+ return ret_val
+
+
+def _check(name, merge_multiple=False):
+ """
+ Generic validator for text-based tags.
+ """
+ if name in REQUIRED:
+ if name in ALLOWXHTML:
+ return _check_required(name, True, merge_multiple)
+ return _check_required(name, merge_multiple=merge_multiple)
+ elif name in OPTIONAL:
+ if name in ALLOWXHTML:
+ return _check_optional(name, True, merge_multiple)
+ return _check_optional(name, merge_multiple=merge_multiple)
+
+
+class Export(object):
+ """
+ Manifest 'export' tag
+ """
+
+ def __init__(self, tag, attrs, str):
+ """
+ Create new export instance.
+ :param tag: name of the XML tag
+ @type tag: str
+ :param attrs: dictionary of XML attributes for this export tag
+ @type attrs: dict
+ :param str: string value contained by tag, if any
+ @type str: str
+ """
+ self.tag = tag
+ self.attrs = attrs
+ self.str = str
+
+ def get(self, attr):
+ """
+ :returns: value of attribute or ``None`` if attribute not set, ``str``
+ """
+ return self.attrs.get(attr, None)
+
+
+class Platform(object):
+ """
+ Manifest 'platform' tag
+ """
+ __slots__ = ['os', 'version', 'notes']
+
+ def __init__(self, os_, version, notes=None):
+ """
+ Create new depend instance.
+ :param os_: OS name. must be non-empty, ``str``
+ :param version: OS version. must be non-empty, ``str``
+ :param notes: (optional) notes about platform support, ``str``
+ """
+ if not os_:
+ raise ValueError("bad 'os' attribute")
+ if not version:
+ raise ValueError("bad 'version' attribute")
+ self.os = os_
+ self.version = version
+ self.notes = notes
+
+ def __str__(self):
+ return "%s %s" % (self.os, self.version)
+
+ def __repr__(self):
+ return "%s %s" % (self.os, self.version)
+
+ def __eq__(self, obj):
+ """
+ Override equality test. notes *are* considered in the equality test.
+ """
+ if not isinstance(obj, Platform):
+ return False
+ return self.os == obj.os and self.version == obj.version and self.notes == obj.notes
+
+ def __hash__(self):
+ """
+ :returns: an integer, which must be the same for two equal instances.
+
+ Since __eq__ is defined, Python 3 requires that this class also provide a __hash__ method.
+ """
+ return hash(self.os) ^ hash(self.version) ^ hash(self.notes)
+
+
+class Depend(object):
+ """
+ Manifest 'depend' tag
+ """
+ __slots__ = ['name', 'type']
+
+ def __init__(self, name, type_):
+ """
+ Create new depend instance.
+ :param name: dependency name (e.g. package/stack). Must be non-empty
+ @type name: str
+ :param type_: dependency type, e.g. 'package', 'stack'. Must be non-empty.
+ @type type_: str
+
+ @raise ValueError: if parameters are invalid
+ """
+ if not name:
+ raise ValueError("bad '%s' attribute" % (type_))
+ if not type_:
+ raise ValueError("type_ must be specified")
+ self.name = name
+ self.type = type_
+
+ def __str__(self):
+ return self.name
+
+ def __repr__(self):
+ return self.name
+
+ def __eq__(self, obj):
+ if not isinstance(obj, Depend):
+ return False
+ return self.name == obj.name and self.type == obj.type
+
+ def __hash__(self):
+ """
+ :returns: an integer, which must be the same for two equal instances.
+
+ Since __eq__ is defined, Python 3 requires that this class also provide a __hash__ method.
+ """
+ return hash(self.name) ^ hash(self.type)
+
+
+class RosDep(object):
+ """
+ Manifest 'rosdep' tag
+ """
+ __slots__ = ['name']
+
+ def __init__(self, name):
+ """
+ Create new rosdep instance.
+
+ :param name: dependency name. Must be non-empty. ``str``
+ """
+ if not name:
+ raise ValueError("bad 'name' attribute")
+ self.name = name
+
+
+class Manifest(object):
+ """
+ Object representation of a ROS manifest file (``manifest.xml`` and ``stack.xml``)
+ """
+ __slots__ = [
+ 'description', 'brief',
+ 'author', 'license', 'licenses', 'license_url', 'url',
+ 'depends', 'rosdeps', 'platforms',
+ 'exports', 'version',
+ 'status', 'notes',
+ 'unknown_tags', 'type', 'filename',
+ 'is_catkin']
+
+ def __init__(self, type_='package', filename=None, is_catkin=False):
+ """
+ :param type: `'package'` or `'stack'`
+ :param filename: location of manifest file. Necessary if
+ converting ``${prefix}`` in ``<export>`` values, ``str``.
+ """
+ self.description = self.brief = self.author = \
+ self.license = self.license_url = \
+ self.url = self.status = \
+ self.version = self.notes = ''
+ self.licenses = []
+ self.depends = []
+ self.rosdeps = []
+ self.exports = []
+ self.platforms = []
+ self.is_catkin = is_catkin
+
+ self.type = type_
+ self.filename = filename
+
+ # store unrecognized tags during parsing
+ self.unknown_tags = []
+
+ def get_export(self, tag, attr, convert=True):
+ """
+ :param tag: Name of XML tag to retrieve, ``str``
+ :param attr: Name of XML attribute to retrieve from tag, ``str``
+ :param convert: If ``True``, interpret variables (e.g. ``${prefix}``) export values.
+ :returns: exports that match the specified tag and attribute, e.g. 'python', 'path'. ``[str]``
+ """
+ vals = [e.get(attr) for e in self.exports if e.tag == tag if e.get(attr) is not None]
+ if convert:
+ if not self.filename:
+ raise ValueError("cannot convert export values when filename for Manifest is not set")
+ prefix = os.path.dirname(self.filename)
+ vals = [v.replace('${prefix}', prefix) for v in vals]
+ return vals
+
+
+def _get_text(nodes):
+ """
+ DOM utility routine for getting contents of text nodes
+ """
+ return "".join([n.data for n in nodes if n.nodeType == n.TEXT_NODE])
+
+
+_static_rosdep_view = None
+
+
+def parse_manifest_file(dirpath, manifest_name, rospack=None):
+ """
+ Parse manifest file (package, stack). Type will be inferred from manifest_name.
+
+ :param dirpath: directory of manifest file, ``str``
+ :param manifest_name: ``MANIFEST_FILE`` or ``STACK_FILE``, ``str``
+ :param rospack: a RosPack instance to identify local packages as ROS packages
+
+ :returns: return :class:`Manifest` instance, populated with parsed fields
+ :raises: :exc:`InvalidManifest`
+ :raises: :exc:`IOError`
+ """
+ filename = os.path.join(dirpath, manifest_name)
+ if not os.path.isfile(filename):
+ # hack for backward compatibility
+ package_filename = os.path.join(dirpath, PACKAGE_FILE)
+ if not os.path.isfile(package_filename):
+ raise IOError("Invalid/non-existent manifest file: %s" % filename)
+ manifest = Manifest(filename=filename, is_catkin=True)
+
+ # extract all information from package.xml
+ from catkin_pkg.package import parse_package
+ p = parse_package(package_filename)
+ # put these into manifest
+ manifest.description = p.description
+ manifest.author = ', '.join([('Maintainer: %s' % str(m)) for m in p.maintainers] + [str(a) for a in p.authors])
+ manifest.license = ', '.join(p.licenses)
+ manifest.licenses = p.licenses
+ if p.urls:
+ manifest.url = str(p.urls[0])
+ manifest.version = p.version
+ for export in p.exports:
+ manifest.exports.append(Export(export.tagname, export.attributes, export.content))
+
+ # split ros and system dependencies (using rosdep)
+ try:
+ from rosdep2.rospack import init_rospack_interface, is_ros_package, is_system_dependency, is_view_empty
+ global _static_rosdep_view
+ # initialize rosdep view once
+ if _static_rosdep_view is None:
+ _static_rosdep_view = init_rospack_interface()
+ if is_view_empty(_static_rosdep_view):
+ sys.stderr.write("the rosdep view is empty: call 'sudo rosdep init' and 'rosdep update'\n")
+ _static_rosdep_view = False
+ if _static_rosdep_view:
+ depends = set([])
+ rosdeps = set([])
+ for d in (p.buildtool_depends + p.build_depends + p.run_depends + p.test_depends):
+ if (rospack and d.name in rospack.list()) or is_ros_package(_static_rosdep_view, d.name):
+ depends.add(d.name)
+ if is_system_dependency(_static_rosdep_view, d.name):
+ rosdeps.add(d.name)
+ for name in depends:
+ manifest.depends.append(Depend(name, 'package'))
+ for name in rosdeps:
+ manifest.rosdeps.append(RosDep(name))
+ except ImportError:
+ pass
+
+ return manifest
+
+ with open(filename, 'rb') as f:
+ data = f.read()
+ if sys.version_info[0] >= 3:
+ data = data.decode('utf-8')
+ return parse_manifest(manifest_name, data, filename)
+
+
+def parse_manifest(manifest_name, string, filename='string'):
+ """
+ Parse manifest string contents.
+
+ :param manifest_name: ``MANIFEST_FILE`` or ``STACK_FILE``, ``str``
+ :param string: manifest.xml contents, ``str``
+ :param filename: full file path for debugging, ``str``
+ :returns: return parsed :class:`Manifest`
+ """
+ if manifest_name == MANIFEST_FILE:
+ type_ = 'package'
+ elif manifest_name == STACK_FILE:
+ type_ = 'stack'
+
+ try:
+ d = dom.parseString(string)
+ except Exception as e:
+ raise InvalidManifest("[%s] invalid XML: %s" % (filename, e))
+
+ m = Manifest(type_, filename)
+ p = _get_nodes_by_name(d, type_)
+ if len(p) != 1:
+ raise InvalidManifest("manifest [%s] must have a single '%s' element" % (filename, type_))
+ p = p[0]
+ m.description = _check('description')(p, filename)
+ m.brief = ''
+ try:
+ tag = _get_nodes_by_name(p, 'description')[0]
+ m.brief = tag.getAttribute('brief') or ''
+ except:
+ # means that 'description' tag is missing
+ pass
+
+ m.depends = _check_depends(type_, p, filename)
+ m.rosdeps = _check_rosdeps(p, filename)
+ m.platforms = _check_platform(p, filename)
+ m.exports = _check_exports(p, filename)
+ m.license = _check('license')(p, filename)
+ m.license_url = ''
+ try:
+ tag = _get_nodes_by_name(p, 'license')[0]
+ m.license_url = tag.getAttribute('url') or ''
+ except:
+ pass # manifest is missing required 'license' tag
+
+ m.status = 'unreviewed'
+ try:
+ tag = _get_nodes_by_name(p, 'review')[0]
+ m.status = tag.getAttribute('status') or ''
+ except:
+ pass # manifest is missing optional 'review status' tag
+
+ m.notes = ''
+ try:
+ tag = _get_nodes_by_name(p, 'review')[0]
+ m.notes = tag.getAttribute('notes') or ''
+ except:
+ pass # manifest is missing optional 'review notes' tag
+
+ m.author = _check('author', True)(p, filename)
+ m.url = _check('url')(p, filename)
+ m.version = _check('version')(p, filename)
+
+ # do some validation on what we just parsed
+ if type_ == 'stack':
+ if m.exports:
+ raise InvalidManifest("stack manifests are not allowed to have exports")
+ if m.rosdeps:
+ raise InvalidManifest("stack manifests are not allowed to have rosdeps")
+
+ m.is_catkin = bool(_get_nodes_by_name(p, 'catkin')) or bool(_get_nodes_by_name(p, 'name'))
+
+ # store unrecognized tags
+ m.unknown_tags = [e for e in p.childNodes if e.nodeType == e.ELEMENT_NODE and e.tagName not in VALID]
+ return m
--- /dev/null
+# Copyright (c) 2009, 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 Tully Foote/tfoote@willowgarage.com, Ken Conley/kwc@willowgarage.com
+
+"""
+Library for detecting the current OS, including detecting specific
+Linux distributions.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+import codecs
+# to be removed after Ubuntu Xenial is out of support
+import sys
+if sys.version_info >= (3, 8):
+ import distro
+else:
+ import platform as distro
+import locale
+import os
+import platform
+import subprocess
+
+
+def _read_stdout(cmd):
+ try:
+ pop = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (std_out, std_err) = pop.communicate()
+ # Python 2.6 compatibility
+ if isinstance(std_out, str):
+ return std_out.strip()
+ return std_out.decode(encoding='UTF-8').strip()
+ except:
+ return None
+
+
+def uname_get_machine():
+ """
+ Linux: wrapper around uname to determine if OS is 64-bit
+ """
+ return _read_stdout(['uname', '-m'])
+
+
+def read_issue(filename="/etc/issue"):
+ """
+ :returns: list of strings in issue file, or None if issue file cannot be read/split
+ """
+ if os.path.exists(filename):
+ with codecs.open(filename, 'r', encoding=locale.getpreferredencoding()) as f:
+ return f.read().split()
+ return None
+
+
+def read_os_release(filename=None):
+ """
+ :returns: Dictionary of key value pairs from /etc/os-release or fallback to
+ /usr/lib/os-release, with quotes stripped from values
+ """
+ if filename is None:
+ filename = '/etc/os-release'
+ if not os.path.exists(filename):
+ filename = '/usr/lib/os-release'
+
+ if not os.path.exists(filename):
+ return None
+
+ release_info = {}
+ with codecs.open(filename, 'r', encoding=locale.getpreferredencoding()) as f:
+ for line in f:
+ key, val = line.rstrip('\n').partition('=')[::2]
+ release_info[key] = val.strip('"')
+ return release_info
+
+
+class OsNotDetected(Exception):
+ """
+ Exception to indicate failure to detect operating system.
+ """
+ pass
+
+
+class OsDetector(object):
+ """
+ Generic API for detecting a specific OS.
+ """
+ def is_os(self):
+ """
+ :returns: if the specific OS which this class is designed to
+ detect is present. Only one version of this class should
+ return for any version.
+ """
+ raise NotImplementedError("is_os unimplemented")
+
+ def get_version(self):
+ """
+ :returns: standardized version for this OS. (aka Ubuntu Hardy Heron = "8.04")
+ :raises: :exc:`OsNotDetected` if called on incorrect OS.
+ """
+ raise NotImplementedError("get_version unimplemented")
+
+ def get_codename(self):
+ """
+ :returns: codename for this OS. (aka Ubuntu Hardy Heron = "hardy"). If codenames are not available for this OS, return empty string.
+ :raises: :exc:`OsNotDetected` if called on incorrect OS.
+ """
+ raise NotImplementedError("get_codename unimplemented")
+
+
+class LsbDetect(OsDetector):
+ """
+ Generic detector for Debian, Ubuntu, Mint, and Pop! OS
+ """
+ def __init__(self, lsb_name, get_version_fn=None):
+ self.lsb_name = lsb_name
+ if hasattr(distro, "linux_distribution"):
+ self.lsb_info = distro.linux_distribution(full_distribution_name=0)
+ elif hasattr(distro, "dist"):
+ self.lsb_info = distro.dist()
+ else:
+ self.lsb_info = None
+
+ def is_os(self):
+ if self.lsb_info is None:
+ return False
+ # Work around platform returning 'Ubuntu' and distro returning 'ubuntu'
+ return self.lsb_info[0].lower() == self.lsb_name.lower()
+
+ def get_version(self):
+ if self.is_os():
+ return self.lsb_info[1]
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ if self.is_os():
+ return self.lsb_info[2]
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Debian(LsbDetect):
+
+ def __init__(self, get_version_fn=None):
+ super(Debian, self).__init__('debian', get_version_fn)
+
+ def get_codename(self):
+ if self.is_os():
+ v = self.get_version()
+ if v.startswith('7.'):
+ return 'wheezy'
+ if v.startswith('8.'):
+ return 'jessie'
+ if v.startswith('9.'):
+ return 'stretch'
+ if v.startswith('10.'):
+ return 'buster'
+ return ''
+
+
+class FdoDetect(OsDetector):
+ """
+ Generic detector for operating systems implementing /etc/os-release, as defined by the os-release spec hosted at Freedesktop.org (Fdo):
+ http://www.freedesktop.org/software/systemd/man/os-release.html
+ Requires that the "ID", and "VERSION_ID" keys are set in the os-release file.
+
+ Codename is parsed from the VERSION key if available: either using the format "foo, CODENAME" or "foo (CODENAME)."
+ If the VERSION key is not present, the VERSION_ID is value is used as the codename.
+ """
+ def __init__(self, fdo_id):
+ release_info = read_os_release()
+ if release_info is not None and "ID" in release_info and release_info["ID"] == fdo_id:
+ self.release_info = release_info
+ else:
+ self.release_info = None
+
+ def is_os(self):
+ return self.release_info is not None and "VERSION_ID" in self.release_info
+
+ def get_version(self):
+ if self.is_os():
+ return self.release_info["VERSION_ID"]
+ raise OsNotDetected("called in incorrect OS")
+
+ def get_codename(self):
+ if self.is_os():
+ if "VERSION" in self.release_info:
+ version = self.release_info["VERSION"]
+ # FDO style: works with Fedora, Debian, Suse.
+ if '(' in version:
+ codename = version[version.find("(") + 1:version.find(")")]
+ # Ubuntu style
+ elif '"' in version:
+ codename = version[version.find(",") + 1:].lstrip(' ').split()[0]
+ # Indeterminate style
+ else:
+ codename = version
+ return codename.lower()
+ else:
+ return self.get_version()
+ raise OsNotDetected("called in incorrect OS")
+
+
+class OpenEmbedded(OsDetector):
+ """
+ Detect OpenEmbedded.
+ """
+ def is_os(self):
+ return "ROS_OS_OVERRIDE" in os.environ and os.environ["ROS_OS_OVERRIDE"] == "openembedded"
+
+ def get_version(self):
+ if self.is_os():
+ return ""
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ if self.is_os():
+ return ""
+ raise OsNotDetected('called in incorrect OS')
+
+
+class OpenSuse(OsDetector):
+ """
+ Detect OpenSuse OS.
+ """
+ def __init__(self, brand_file="/etc/SuSE-brand", release_file="/etc/SuSE-release"):
+ self._brand_file = brand_file
+ self._release_file = release_file
+
+ def is_os(self):
+ os_list = read_issue(self._brand_file)
+ return os_list and os_list[0] == "openSUSE"
+
+ def get_version(self):
+ if self.is_os() and os.path.exists(self._brand_file):
+ with open(self._brand_file, 'r') as fh:
+ os_list = fh.read().strip().split('\n')
+ if len(os_list) == 2:
+ os_list = os_list[1].split(' = ')
+ if os_list[0] == "VERSION":
+ return os_list[1]
+ raise OsNotDetected('cannot get version on this OS')
+
+ def get_codename(self):
+ # /etc/SuSE-release is deprecated since 13.1
+ if self._release_file is None:
+ return ""
+ if self.is_os() and os.path.exists(self._release_file):
+ with open(self._release_file, 'r') as fh:
+ os_list = fh.read().strip().split('\n')
+ for line in os_list:
+ kv = line.split(' = ')
+ if kv[0] == "CODENAME":
+ return kv[1]
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Fedora(OsDetector):
+ """
+ Detect Fedora OS.
+ """
+ def __init__(self, release_file="/etc/redhat-release", issue_file="/etc/issue"):
+ self._release_file = release_file
+ self._issue_file = issue_file
+
+ def is_os(self):
+ os_list = read_issue(self._release_file)
+ return os_list and os_list[0] == "Fedora"
+
+ def get_version(self):
+ if self.is_os():
+ os_list = read_issue(self._issue_file)
+ idx = os_list.index('release')
+ if idx > 0:
+ return os_list[idx + 1]
+ raise OsNotDetected('cannot get version on this OS')
+
+ def get_codename(self):
+ if self.is_os():
+ os_list = read_issue(self._release_file)
+ idx = os_list.index('release')
+ matches = [x for x in os_list if x[0] == '(']
+ codename = matches[0][1:]
+ if codename[-1] == ')':
+ codename = codename[:-1]
+ return codename.lower()
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Rhel(Fedora):
+ """
+ Detect Redhat OS.
+ """
+ def __init__(self, release_file="/etc/redhat-release"):
+ self._release_file = release_file
+
+ def is_os(self):
+ os_list = read_issue(self._release_file)
+ return os_list and os_list[:3] == ['Red', 'Hat', 'Enterprise']
+
+ def get_version(self):
+ if self.is_os():
+ os_list = read_issue(self._release_file)
+ idx = os_list.index('release')
+ return os_list[idx + 1]
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ # taroon, nahant, tikanga, santiago, pensacola
+ if self.is_os():
+ os_list = read_issue(self._release_file)
+ idx = os_list.index('release')
+ matches = [x for x in os_list if x[0] == '(']
+ codename = matches[0][1:]
+ if codename[-1] == ')':
+ codename = codename[:-1]
+ return codename.lower()
+ raise OsNotDetected('called in incorrect OS')
+
+
+# Source: https://en.wikipedia.org/wiki/MacOS#Versions
+_osx_codename_map = {
+ 4: 'tiger',
+ 5: 'leopard',
+ 6: 'snow',
+ 7: 'lion',
+ 8: 'mountain lion',
+ 9: 'mavericks',
+ 10: 'yosemite',
+ 11: 'el capitan',
+ 12: 'sierra',
+ 13: 'high sierra',
+ 14: 'mojave',
+ 15: 'catalina',
+}
+
+
+def _osx_codename(major, minor):
+ if major != 10 or minor not in _osx_codename_map:
+ raise OsNotDetected("unrecognized version: %s.%s" % (major, minor))
+ return _osx_codename_map[minor]
+
+
+class OSX(OsDetector):
+ """
+ Detect OS X
+ """
+ def __init__(self, sw_vers_file="/usr/bin/sw_vers"):
+ self._sw_vers_file = sw_vers_file
+
+ def is_os(self):
+ return os.path.exists(self._sw_vers_file)
+
+ def get_codename(self):
+ if self.is_os():
+ version = self.get_version()
+ import distutils.version # To parse version numbers
+ try:
+ ver = distutils.version.StrictVersion(version).version
+ except ValueError:
+ raise OsNotDetected("invalid version string: %s" % (version))
+ return _osx_codename(*ver[0:2])
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_version(self):
+ if self.is_os():
+ return _read_stdout([self._sw_vers_file, '-productVersion'])
+ raise OsNotDetected('called in incorrect OS')
+
+
+class QNX(OsDetector):
+ '''
+ Detect QNX realtime OS.
+ @author: Isaac Saito
+ '''
+ def __init__(self, uname_file='/bin/uname'):
+ '''
+ @param uname_file: An executable that can be used for detecting
+ OS name and version.
+ '''
+ self._os_name_qnx = 'QNX'
+ self._uname_file = uname_file
+
+ def is_os(self):
+ if os.path.exists(self._uname_file):
+ std_out = _read_stdout([self._uname_file])
+ return std_out.strip() == self._os_name_qnx
+ else:
+ return False
+
+ def get_codename(self):
+ if self.is_os():
+ return ''
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_version(self):
+ if self.is_os() and os.path.exists(self._uname_file):
+ return _read_stdout([self._uname_file, "-r"])
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Arch(OsDetector):
+ """
+ Detect Arch Linux.
+ """
+ def __init__(self, release_file='/etc/arch-release'):
+ self._release_file = release_file
+
+ def is_os(self):
+ return os.path.exists(self._release_file)
+
+ def get_version(self):
+ if self.is_os():
+ return ""
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ if self.is_os():
+ return ""
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Manjaro(Arch):
+ """
+ Detect Manjaro.
+ """
+ def __init__(self, release_file='/etc/manjaro-release'):
+ super(Manjaro, self).__init__(release_file)
+
+
+class Centos(OsDetector):
+ """
+ Detect CentOS.
+ """
+ def __init__(self, release_file='/etc/redhat-release'):
+ self._release_file = release_file
+
+ def is_os(self):
+ os_list = read_issue(self._release_file)
+ return os_list and os_list[0] == 'CentOS'
+
+ def get_version(self):
+ if self.is_os():
+ os_list = read_issue(self._release_file)
+ idx = os_list.index('release')
+ return os_list[idx + 1]
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ if self.is_os():
+ os_list = read_issue(self._release_file)
+ idx = os_list.index('release')
+ matches = [x for x in os_list if x[0] == '(']
+ codename = matches[0][1:]
+ if codename[-1] == ')':
+ codename = codename[:-1]
+ return codename.lower()
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Cygwin(OsDetector):
+ """
+ Detect Cygwin presence on Windows OS.
+ """
+ def is_os(self):
+ return os.path.exists("/usr/bin/cygwin1.dll")
+
+ def get_version(self):
+ if self.is_os():
+ return _read_stdout(['uname', '-r'])
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ if self.is_os():
+ return ''
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Gentoo(OsDetector):
+ """
+ Detect Gentoo OS.
+ """
+ def __init__(self, release_file="/etc/gentoo-release"):
+ self._release_file = release_file
+
+ def is_os(self):
+ os_list = read_issue(self._release_file)
+ return os_list and os_list[0] == "Gentoo" and os_list[1] == "Base"
+
+ def get_version(self):
+ if self.is_os():
+ os_list = read_issue(self._release_file)
+ return os_list[4]
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ if self.is_os():
+ return ''
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Funtoo(Gentoo):
+ """
+ Detect Funtoo OS, a Gentoo Variant.
+ """
+ def __init__(self, release_file="/etc/gentoo-release"):
+ Gentoo.__init__(self, release_file)
+
+ def is_os(self):
+ os_list = read_issue(self._release_file)
+ return os_list and os_list[0] == "Funtoo" and os_list[1] == "Linux"
+
+
+class FreeBSD(OsDetector):
+ """
+ Detect FreeBSD OS.
+ """
+ def __init__(self, uname_file="/usr/bin/uname"):
+ self._uname_file = uname_file
+
+ def is_os(self):
+ if os.path.exists(self._uname_file):
+ std_out = _read_stdout([self._uname_file])
+ return std_out.strip() == "FreeBSD"
+ else:
+ return False
+
+ def get_version(self):
+ if self.is_os() and os.path.exists(self._uname_file):
+ return _read_stdout([self._uname_file, "-r"])
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ if self.is_os():
+ return ''
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Slackware(OsDetector):
+ """
+ Detect SlackWare Linux.
+ """
+ def __init__(self, release_file='/etc/slackware-version'):
+ self._release_file = release_file
+
+ def is_os(self):
+ return os.path.exists(self._release_file)
+
+ def get_version(self):
+ if self.is_os():
+ os_list = read_issue(self._release_file)
+ return os_list[1]
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ if self.is_os():
+ return ''
+ raise OsNotDetected('called in incorrect OS')
+
+
+class Windows(OsDetector):
+ """
+ Detect Windows OS.
+ """
+ def is_os(self):
+ return platform.system() == "Windows"
+
+ def get_version(self):
+ if self.is_os():
+ return platform.version()
+ raise OsNotDetected('called in incorrect OS')
+
+ def get_codename(self):
+ if self.is_os():
+ return platform.release()
+ raise OsNotDetected('called in incorrect OS')
+
+
+class OsDetect:
+ """
+ This class will iterate over registered classes to lookup the
+ active OS and version
+ """
+
+ default_os_list = []
+
+ def __init__(self, os_list=None):
+ if os_list is None:
+ os_list = OsDetect.default_os_list
+ self._os_list = os_list
+ self._os_name = None
+ self._os_version = None
+ self._os_codename = None
+ self._os_detector = None
+ self._override = False
+
+ @staticmethod
+ def register_default(os_name, os_detector):
+ """
+ Register detector to be used with all future instances of
+ :class:`OsDetect`. The new detector will have precedence over
+ any previously registered detectors associated with *os_name*.
+
+ :param os_name: OS key associated with OS detector
+ :param os_detector: :class:`OsDetector` instance
+ """
+ OsDetect.default_os_list.insert(0, (os_name, os_detector))
+
+ def detect_os(self, env=None):
+ """
+ Detect operating system. Return value can be overridden by
+ the :env:`ROS_OS_OVERRIDE` environment variable.
+
+ :param env: override ``os.environ``
+ :returns: (os_name, os_version, os_codename), ``(str, str, str)``
+ :raises: :exc:`OsNotDetected` if OS could not be detected
+ """
+ if env is None:
+ env = os.environ
+ if 'ROS_OS_OVERRIDE' in env:
+ splits = env["ROS_OS_OVERRIDE"].split(':')
+ self._os_name = splits[0]
+ if len(splits) > 1:
+ self._os_version = splits[1]
+ if len(splits) > 2:
+ self._os_codename = splits[2]
+ else:
+ self._os_codename = ''
+ else:
+ self._os_version = self._os_codename = ''
+ self._override = True
+ else:
+ for os_name, os_detector in self._os_list:
+ if os_detector.is_os():
+ self._os_name = os_name
+ self._os_version = os_detector.get_version()
+ self._os_codename = os_detector.get_codename()
+ self._os_detector = os_detector
+ break
+
+ if self._os_name:
+ return self._os_name, self._os_version, self._os_codename
+ else: # No solution found
+ attempted = [x[0] for x in self._os_list]
+ raise OsNotDetected("Could not detect OS, tried %s" % attempted)
+
+ def get_detector(self, name=None):
+ """
+ Get detector used for specified OS name, or the detector for this OS if name is ``None``.
+
+ :raises: :exc:`KeyError`
+ """
+ if name is None:
+ if not self._os_detector:
+ self.detect_os()
+ return self._os_detector
+ else:
+ try:
+ return [d for d_name, d in self._os_list if d_name == name][0]
+ except IndexError:
+ raise KeyError(name)
+
+ def add_detector(self, name, detector):
+ """
+ Add detector to list of detectors used by this instance. *detector* will override any previous
+ detectors associated with *name*.
+
+ :param name: OS name that detector matches
+ :param detector: :class:`OsDetector` instance
+ """
+ self._os_list.insert(0, (name, detector))
+
+ def get_name(self):
+ if not self._os_name:
+ self.detect_os()
+ return self._os_name
+
+ def get_version(self):
+ if not self._os_version:
+ self.detect_os()
+ return self._os_version
+
+ def get_codename(self):
+ if not self._os_codename:
+ self.detect_os()
+ return self._os_codename
+
+
+OS_ALPINE = 'alpine'
+OS_ARCH = 'arch'
+OS_MANJARO = 'manjaro'
+OS_CENTOS = 'centos'
+OS_CYGWIN = 'cygwin'
+OS_DEBIAN = 'debian'
+OS_ELEMENTARY = 'elementary'
+OS_ELEMENTARY_OLD = 'elementary'
+OS_FEDORA = 'fedora'
+OS_FREEBSD = 'freebsd'
+OS_FUNTOO = 'funtoo'
+OS_GENTOO = 'gentoo'
+OS_LINARO = 'linaro'
+OS_MINT = 'mint'
+OS_MX = 'mx'
+OS_NEON = 'neon'
+OS_OPENEMBEDDED = 'openembedded'
+OS_OPENSUSE = 'opensuse'
+OS_OPENSUSE13 = 'opensuse'
+OS_TIZEN = 'tizen'
+OS_SAILFISHOS = 'sailfishos'
+OS_OSX = 'osx'
+OS_POP = 'pop'
+OS_QNX = 'qnx'
+OS_RHEL = 'rhel'
+OS_SLACKWARE = 'slackware'
+OS_UBUNTU = 'ubuntu'
+OS_CLEARLINUX = 'clearlinux'
+OS_NIXOS = 'nixos'
+OS_WINDOWS = 'windows'
+OS_ZORIN = 'zorin'
+
+OsDetect.register_default(OS_ALPINE, FdoDetect("alpine"))
+OsDetect.register_default(OS_ARCH, Arch())
+OsDetect.register_default(OS_MANJARO, Manjaro())
+OsDetect.register_default(OS_CENTOS, Centos())
+OsDetect.register_default(OS_CYGWIN, Cygwin())
+OsDetect.register_default(OS_DEBIAN, Debian())
+OsDetect.register_default(OS_ELEMENTARY, LsbDetect("elementary"))
+OsDetect.register_default(OS_ELEMENTARY_OLD, LsbDetect("elementary OS"))
+OsDetect.register_default(OS_FEDORA, FdoDetect("fedora"))
+OsDetect.register_default(OS_FREEBSD, FreeBSD())
+OsDetect.register_default(OS_FUNTOO, Funtoo())
+OsDetect.register_default(OS_GENTOO, Gentoo())
+OsDetect.register_default(OS_LINARO, LsbDetect("Linaro"))
+OsDetect.register_default(OS_MINT, LsbDetect("LinuxMint"))
+OsDetect.register_default(OS_MX, LsbDetect("MX"))
+OsDetect.register_default(OS_NEON, LsbDetect("neon"))
+OsDetect.register_default(OS_OPENEMBEDDED, OpenEmbedded())
+OsDetect.register_default(OS_OPENSUSE, OpenSuse())
+OsDetect.register_default(OS_OPENSUSE13, OpenSuse(brand_file='/etc/SUSE-brand', release_file=None))
+OsDetect.register_default(OS_OPENSUSE, FdoDetect("opensuse-tumbleweed"))
+OsDetect.register_default(OS_OPENSUSE, FdoDetect("opensuse"))
+OsDetect.register_default(OS_TIZEN, FdoDetect("tizen"))
+OsDetect.register_default(OS_SAILFISHOS, FdoDetect("sailfishos"))
+OsDetect.register_default(OS_OSX, OSX())
+OsDetect.register_default(OS_POP, LsbDetect("Pop"))
+OsDetect.register_default(OS_QNX, QNX())
+OsDetect.register_default(OS_RHEL, Rhel())
+OsDetect.register_default(OS_SLACKWARE, Slackware())
+OsDetect.register_default(OS_UBUNTU, LsbDetect("Ubuntu"))
+OsDetect.register_default(OS_CLEARLINUX, FdoDetect("clear-linux-os"))
+OsDetect.register_default(OS_NIXOS, FdoDetect("nixos"))
+OsDetect.register_default(OS_WINDOWS, Windows())
+OsDetect.register_default(OS_ZORIN, LsbDetect("Zorin"))
+
+
+if __name__ == '__main__':
+ detect = OsDetect()
+ print("OS Name: %s" % detect.get_name())
+ print("OS Version: %s" % detect.get_version())
+ print("OS Codename: %s" % detect.get_codename())
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+import os
+from threading import Lock
+
+try:
+ from xml.etree.cElementTree import ElementTree
+except ImportError:
+ from xml.etree.ElementTree import ElementTree
+
+from .common import MANIFEST_FILE, PACKAGE_FILE, ResourceNotFound, STACK_FILE
+from .environment import get_ros_paths
+from .manifest import InvalidManifest, parse_manifest_file
+from .stack import InvalidStack, parse_stack_file
+
+_cache_lock = Lock()
+
+
+def list_by_path(manifest_name, path, cache):
+ """
+ List ROS stacks or packages within the specified path.
+
+ The cache will be updated with the resource->path
+ mappings. list_by_path() does NOT returned cached results
+ -- it only updates the cache.
+
+ :param manifest_name: MANIFEST_FILE or STACK_FILE, ``str``
+ :param path: path to list resources in, ``str``
+ :param cache: path cache to update. Maps resource name to directory path, ``{str: str}``
+ :returns: complete list of resources in ROS environment, ``[str]``
+ """
+ resources = []
+ path = os.path.abspath(path)
+ basename = os.path.basename
+ for d, dirs, files in os.walk(path, topdown=True, followlinks=True):
+ if 'CATKIN_IGNORE' in files:
+ del dirs[:]
+ continue # leaf
+ if PACKAGE_FILE in files:
+ # parse package.xml and decide if it matches the search criteria
+ root = ElementTree(None, os.path.join(d, PACKAGE_FILE))
+ is_metapackage = root.find('./export/metapackage') is not None
+ if (
+ (manifest_name == STACK_FILE and is_metapackage) or
+ (manifest_name == MANIFEST_FILE and not is_metapackage) or
+ manifest_name == PACKAGE_FILE
+ ):
+ resource_name = root.findtext('name').strip(' \n\r\t')
+ if resource_name not in resources:
+ resources.append(resource_name)
+ if cache is not None:
+ cache[resource_name] = d
+ del dirs[:]
+ continue # leaf
+ if manifest_name in files:
+ resource_name = basename(d)
+ if resource_name not in resources:
+ resources.append(resource_name)
+ if cache is not None:
+ cache[resource_name] = d
+ del dirs[:]
+ continue # leaf
+ elif MANIFEST_FILE in files or PACKAGE_FILE in files:
+ # noop if manifest_name==MANIFEST_FILE, but a good
+ # optimization for stacks.
+ del dirs[:]
+ continue # leaf
+ elif 'rospack_nosubdirs' in files:
+ del dirs[:]
+ continue # leaf
+ # remove hidden dirs (esp. .svn/.git)
+ [dirs.remove(di) for di in dirs if di[0] == '.']
+ return resources
+
+
+class ManifestManager(object):
+ """
+ Base class implementation for :class:`RosPack` and
+ :class:`RosStack`. This class indexes resources on paths with
+ where manifests denote the precense of the resource. NOTE: for
+ performance reasons, instances cache information and will not
+ reflect changes made on disk or to environment configuration.
+ """
+
+ def __init__(self, manifest_name, ros_paths=None):
+ """
+ ctor. subclasses are expected to use *manifest_name*
+ to customize behavior of ManifestManager.
+
+ :param manifest_name: MANIFEST_FILE or STACK_FILE
+ :param ros_paths: Ordered list of paths to search for
+ resources. If `None` (default), use environment ROS path.
+ """
+ self._manifest_name = manifest_name
+
+ if ros_paths is None:
+ self._ros_paths = get_ros_paths()
+ else:
+ self._ros_paths = ros_paths
+
+ self._manifests = {}
+ self._depends_cache = {}
+ self._rosdeps_cache = {}
+ self._location_cache = None
+ self._custom_cache = {}
+
+ @classmethod
+ def get_instance(cls, ros_paths=None):
+ """
+ Reuse an existing instance for the specified ros_paths instead of creating a new one.
+ Only works for subclasses, as the ManifestManager itself expects two args for the ctor.
+
+ :param ros_paths: Ordered list of paths to search for
+ resources. If `None` (default), use environment ROS path.
+ """
+ if not hasattr(cls, '_instances'):
+ # add class variable _instances to cls
+ cls._instances = {}
+
+ # generate instance_key from ros_paths variable
+ if ros_paths is None:
+ ros_paths = get_ros_paths()
+ instance_key = str(tuple(ros_paths))
+
+ if instance_key not in cls._instances:
+ # create and cache new instance
+ cls._instances[instance_key] = cls(ros_paths)
+ return cls._instances[instance_key]
+
+ def get_ros_paths(self):
+ return self._ros_paths[:]
+ ros_paths = property(get_ros_paths, doc="Get ROS paths of this instance")
+
+ def get_manifest(self, name):
+ """
+ :raises: :exc:`InvalidManifest`
+ """
+ if name in self._manifests:
+ return self._manifests[name]
+ else:
+ return self._load_manifest(name)
+
+ def _update_location_cache(self):
+ global _cache_lock
+ # ensure self._location_cache is not checked while it is being updated
+ # (i.e. while it is not None, but also not completely populated)
+ with _cache_lock:
+ if self._location_cache is not None:
+ return
+ # initialize cache
+ cache = self._location_cache = {}
+ # nothing to search, #3680
+ if not self._ros_paths:
+ return
+ # crawl paths using our own logic, in reverse order to get
+ # correct precedence
+ for path in reversed(self._ros_paths):
+ list_by_path(self._manifest_name, path, cache)
+
+ def list(self):
+ """
+ List resources.
+
+ :returns: complete list of package names in ROS environment, ``[str]``
+ """
+ self._update_location_cache()
+ return self._location_cache.keys()
+
+ def get_path(self, name):
+ """
+ :param name: package name, ``str``
+ :returns: filesystem path of package
+ :raises: :exc:`ResourceNotFound`
+ """
+ self._update_location_cache()
+ if name not in self._location_cache:
+ raise ResourceNotFound(name, ros_paths=self._ros_paths)
+ else:
+ return self._location_cache[name]
+
+ def _load_manifest(self, name):
+ """
+ :raises: :exc:`ResourceNotFound`
+ """
+ retval = self._manifests[name] = parse_manifest_file(self.get_path(name), self._manifest_name, rospack=self)
+ return retval
+
+ def get_depends(self, name, implicit=True):
+ """
+ Get dependencies of a resource. If implicit is ``True``, this
+ includes implicit (recursive) dependencies.
+
+ :param name: resource name, ``str``
+ :param implicit: include implicit (recursive) dependencies, ``bool``
+
+ :returns: list of names of dependencies, ``[str]``
+ :raises: :exc:`InvalidManifest` If resource or any of its
+ dependencies have an invalid manifest.
+ """
+ if not implicit:
+ m = self.get_manifest(name)
+ return [d.name for d in m.depends]
+ else:
+ if name in self._depends_cache:
+ return self._depends_cache[name]
+
+ # take the union of all dependencies
+ names = [p.name for p in self.get_manifest(name).depends]
+
+ # assign key before recursive call to prevent infinite case
+ self._depends_cache[name] = s = set()
+
+ for p in names:
+ s.update(self.get_depends(p, implicit))
+ # add in our own deps
+ s.update(names)
+ # cache the return value as a list
+ s = list(s)
+ self._depends_cache[name] = s
+ return s
+
+ def get_depends_on(self, name, implicit=True):
+ """
+ Get resources that depend on a resource. If implicit is ``True``, this
+ includes implicit (recursive) dependency relationships.
+
+ NOTE: this does *not* raise :exc:`rospkg.InvalidManifest` if
+ there are invalid manifests found.
+
+ :param name: resource name, ``str``
+ :param implicit: include implicit (recursive) dependencies, ``bool``
+
+ :returns: list of names of dependencies, ``[str]``
+ """
+ depends_on = []
+ if not implicit:
+ # have to examine all dependencies
+ for r in self.list():
+ if r == name:
+ continue
+ try:
+ m = self.get_manifest(r)
+ if any(d for d in m.depends if d.name == name):
+ depends_on.append(r)
+ except InvalidManifest:
+ # robust to bad packages
+ pass
+ except ResourceNotFound:
+ # robust to bad packages
+ pass
+ else:
+ # Computing implicit dependencies requires examining the
+ # dependencies of all packages. As we already implement
+ # this logic in get_depends(), we simply reuse it here for
+ # the reverse calculation. This enables us to use the
+ # same dependency cache that get_depends() uses. The
+ # efficiency is roughly the same due to the caching.
+ for r in self.list():
+ if r == name:
+ continue
+ try:
+ depends = self.get_depends(r, implicit=True)
+ if name in depends:
+ depends_on.append(r)
+ except InvalidManifest:
+ # robust to bad packages
+ pass
+ except ResourceNotFound:
+ # robust to bad packages
+ pass
+ return depends_on
+
+ def get_custom_cache(self, key, default=None):
+ return self._custom_cache.get(key, default)
+
+ def set_custom_cache(self, key, value):
+ self._custom_cache[key] = value
+
+
+class RosPack(ManifestManager):
+ """
+ Utility class for querying properties about ROS packages. This
+ should be used when querying properties about multiple
+ packages.
+
+ NOTE 1: for performance reasons, RosPack caches information about
+ packages.
+
+ NOTE 2: RosPack is not thread-safe.
+
+ Example::
+ from rospkg import RosPack
+ rp = RosPack()
+ packages = rp.list()
+ path = rp.get_path('rospy')
+ depends = rp.get_depends('roscpp')
+ direct_depends = rp.get_depends('roscpp', implicit=False)
+ """
+
+ def __init__(self, ros_paths=None):
+ """
+ :param ros_paths: Ordered list of paths to search for
+ resources. If `None` (default), use environment ROS path.
+ """
+ super(RosPack, self).__init__(MANIFEST_FILE,
+ ros_paths)
+ self._rosdeps_cache = {}
+
+ def get_rosdeps(self, package, implicit=True):
+ """
+ Collect rosdeps of specified package into a dictionary.
+
+ :param package: package name, ``str``
+ :param implicit: include implicit (recursive) rosdeps, ``bool``
+
+ :returns: list of rosdep names, ``[str]``
+ """
+ if implicit:
+ return self._implicit_rosdeps(package)
+ else:
+ m = self.get_manifest(package)
+ return [d.name for d in m.rosdeps]
+
+ def _implicit_rosdeps(self, package):
+ """
+ Compute recursive rosdeps of a single package and cache the
+ result in self._rosdeps_cache.
+
+ :param package: package name, ``str``
+ :returns: list of rosdeps, ``[str]``
+ """
+ if package in self._rosdeps_cache:
+ return self._rosdeps_cache[package]
+
+ # set the key before recursive call to prevent infinite case
+ self._rosdeps_cache[package] = s = set()
+
+ # take the union of all dependencies
+ packages = self.get_depends(package, implicit=True)
+ for p in packages:
+ s.update(self.get_rosdeps(p, implicit=False))
+ # add in our own deps
+ m = self.get_manifest(package)
+ s.update([d.name for d in m.rosdeps])
+ # cache the return value as a list
+ s = list(s)
+ self._rosdeps_cache[package] = s
+ return s
+
+ def stack_of(self, package):
+ """
+ :param package: package name, ``str``
+ :returns: name of stack that package is in, or None if package is not part of a stack, ``str``
+ :raises: :exc:`ResourceNotFound` If package cannot be located
+ """
+ d = self.get_path(package)
+ while d and os.path.dirname(d) != d:
+ stack_file = os.path.join(d, STACK_FILE)
+ if os.path.exists(stack_file):
+ return os.path.basename(d)
+ else:
+ d = os.path.dirname(d)
+
+
+class RosStack(ManifestManager):
+ """
+ Utility class for querying properties about ROS stacks. This
+ should be used when querying properties about multiple
+ stacks.
+
+ NOTE 1: for performance reasons, RosStack caches information about
+ stacks.
+
+ NOTE 2: RosStack is not thread-safe.
+ """
+
+ def __init__(self, ros_paths=None):
+ """
+ :param ros_paths: Ordered list of paths to search for
+ resources. If `None` (default), use environment ROS path.
+ """
+ super(RosStack, self).__init__(STACK_FILE, ros_paths)
+
+ def packages_of(self, stack):
+ """
+ :returns: name of packages that are part of stack, ``[str]``
+ :raises: :exc:`ResourceNotFound` If stack cannot be located
+ """
+ return list_by_path(MANIFEST_FILE, self.get_path(stack), {})
+
+ def get_stack_version(self, stack):
+ """
+ :param env: override environment variables, ``{str: str}``
+ :returns: version number of stack, or None if stack is unversioned, ``str``
+ """
+ return get_stack_version_by_dir(self.get_path(stack))
+
+
+# #2022
+def expand_to_packages(names, rospack, rosstack):
+ """
+ Expand names into a list of packages. Names can either be of packages or stacks.
+
+ :param names: names of stacks or packages, ``[str]``
+ :returns: ([packages], [not_found]). Returns two lists. The first
+ is of packages names. The second is a list of names for which no
+ matching stack or package was found. Lists may have
+ duplicates. ``([str], [str])``
+ """
+ if type(names) not in (tuple, list):
+ raise ValueError("names must be a list of strings")
+
+ # do full package list first. This forces an entire tree
+ # crawl. This is less efficient for a small list of names, but
+ # much more efficient for many names.
+ package_list = rospack.list()
+ valid = []
+ invalid = []
+ for n in names:
+ if n not in package_list:
+ try:
+ valid.extend(rosstack.packages_of(n))
+ except ResourceNotFound:
+ invalid.append(n)
+ else:
+ valid.append(n)
+ return valid, invalid
+
+
+def get_stack_version_by_dir(stack_dir):
+ """
+ Get stack version where stack_dir points to root directory of stack.
+
+ :param env: override environment variables, ``{str: str}``
+
+ :returns: version number of stack, or None if stack is unversioned, ``str``
+ :raises: :exc:`IOError`
+ :raises: :exc:`InvalidStack`
+ """
+ catkin_stack_filename = os.path.join(stack_dir, 'stack.xml')
+ if os.path.isfile(catkin_stack_filename):
+ try:
+ stack = parse_stack_file(catkin_stack_filename)
+ return stack.version
+ except InvalidStack:
+ pass
+
+ cmake_filename = os.path.join(stack_dir, 'CMakeLists.txt')
+ if os.path.isfile(cmake_filename):
+ with open(cmake_filename) as f:
+ try:
+ return _get_cmake_version(f.read())
+ except ValueError:
+ return None
+ else:
+ return None
+
+
+def _get_cmake_version(text):
+ """
+ :raises :exc:`ValueError` If version number in CMakeLists.txt cannot be parsed correctly
+ """
+ import re
+ for l in text.split('\n'):
+ if l.strip().startswith('rosbuild_make_distribution'):
+ x_re = re.compile(r'[()]')
+ lsplit = x_re.split(l.strip())
+ if len(lsplit) < 2:
+ raise ValueError("couldn't find version number in CMakeLists.txt:\n\n%s" % l)
+ version = lsplit[1]
+ if version:
+ return version
+ else:
+ raise ValueError("cannot parse version number in CMakeLists.txt:\n\n%s" % l)
+
+
+def get_package_name(path):
+ """
+ Get the name of the ROS package that contains *path*. This is
+ determined by finding the nearest parent ``manifest.xml`` file.
+ This routine may not traverse package setups that rely on internal
+ symlinks within the package itself.
+
+ :param path: filesystem path
+ :return: Package name or ``None`` if package cannot be found, ``str``
+ """
+ # NOTE: the realpath is going to create issues with symlinks, most
+ # likely.
+ parent = os.path.dirname(os.path.realpath(path))
+ # walk up until we hit ros root or ros/pkg
+ while not os.path.exists(os.path.join(path, MANIFEST_FILE)) and not os.path.exists(os.path.join(path, PACKAGE_FILE)) and parent != path:
+ path = parent
+ parent = os.path.dirname(path)
+ # check termination condition
+ if os.path.exists(os.path.join(path, MANIFEST_FILE)):
+ return os.path.basename(os.path.abspath(path))
+ elif os.path.exists(os.path.join(path, PACKAGE_FILE)):
+ root = ElementTree(None, os.path.join(path, PACKAGE_FILE))
+ return root.findtext('name')
+ else:
+ return None
--- /dev/null
+# Copyright (c) 2008, 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.
+
+from __future__ import print_function
+
+import argparse
+import os
+import sys
+import traceback
+
+from .common import PACKAGE_FILE
+from .rospack import ManifestManager, RosPack, RosStack, ResourceNotFound
+
+
+# for < fuerte, retrieve from roscore file
+def get_distro_name_from_roscore():
+ '''
+ This function only works for ROS Electric and older.
+ For any newer ROS distro the information is provided
+ in the ROS_DISTRO environment variable.
+ '''
+ rospack = RosPack()
+ # there's some chance that the location of this file changes in the future
+ try:
+ roslaunch_dir = rospack.get_path('roslaunch')
+ roscore_file = os.path.join(roslaunch_dir, 'roscore.xml')
+ if not os.path.exists(roscore_file):
+ return None
+ except:
+ return None
+
+ import xml.dom.minidom
+ try:
+ dom = xml.dom.minidom.parse(roscore_file)
+ tags = dom.getElementsByTagName("param")
+ for t in tags:
+ if t.hasAttribute('name') and t.getAttribute('name') == 'rosdistro':
+ return t.getAttribute('value')
+ except:
+ traceback.print_exc()
+
+
+def print_without_newline(argtext):
+ """Print with no new line."""
+ print(argtext, end='')
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='rosversion -d: Output the version of the given package\n'
+ 'rosversion package: Output the ROS distribution name',
+ formatter_class=argparse.RawTextHelpFormatter)
+ parser.add_argument(
+ '-s', '--skip-newline', action='store_true',
+ help='Skip trailing newline')
+ group = parser.add_mutually_exclusive_group(required=True)
+ group.add_argument(
+ 'package', nargs='?',
+ help="The ROS package name (e.g. 'roscpp')")
+ group.add_argument(
+ '-d', '--distro', action='store_true',
+ help='Output the ROS distribution name')
+
+ args = parser.parse_args()
+
+ printer = print_without_newline if args.skip_newline else print
+
+ if args.distro:
+ if 'ROS_DISTRO' in os.environ:
+ distro_name = os.environ['ROS_DISTRO']
+ else:
+ distro_name = get_distro_name_from_roscore()
+ if not distro_name:
+ distro_name = '<unknown>'
+ printer(distro_name)
+ sys.exit(0)
+
+ rosstack = RosStack()
+ try:
+ version = rosstack.get_stack_version(args.package)
+ except ResourceNotFound as e:
+ try:
+ # hack to make it work with wet packages
+ mm = ManifestManager(PACKAGE_FILE)
+ path = mm.get_path(args.package)
+ package_manifest = os.path.join(path, 'package.xml')
+ if os.path.exists(package_manifest):
+ from xml.etree.ElementTree import ElementTree
+ try:
+ root = ElementTree(None, package_manifest)
+ version = root.findtext('version')
+ except Exception:
+ pass
+ except ResourceNotFound as e:
+ print('Cannot locate [%s]' % args.package)
+ sys.exit(1)
+
+ if version is None:
+ version = '<unversioned>'
+ printer(version)
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# Copyright (c) 2012, 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 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.
+
+"""
+Library for processing stack.xml created post-catkin
+"""
+
+import collections
+import os
+import xml.dom.minidom as dom
+
+# as defined on http://ros.org/doc/fuerte/api/catkin/html/stack_xml.html
+REQUIRED = ['name', 'version', 'description', 'author', 'maintainer', 'license', 'copyright']
+ALLOWXHTML = ['description']
+OPTIONAL = ['description_brief', 'version_abi', 'url', 'review_notes', 'review_status', 'build_depends', 'depends', 'build_type', 'message_generator', 'review']
+
+LISTED_ATTRIBUTES = {'Author': ['name', 'email'], 'Maintainer': ['name', 'email'], 'Depend': ['name', 'version']}
+
+VALID = REQUIRED + OPTIONAL
+
+
+class InvalidStack(Exception):
+ pass
+
+
+def _get_nodes_by_name(n, name):
+ return [t for t in n.childNodes if t.nodeType == t.ELEMENT_NODE and t.tagName == name]
+
+
+def _check_optional(name, allowXHTML=False):
+ """
+ Validator for optional elements.
+
+ :raise: :exc:`InvalidStack` If validation fails
+ """
+ def check(n, filename):
+ n = _get_nodes_by_name(n, name)
+ if len(n) > 1:
+ raise InvalidStack("Invalid stack.xml file [%s]: must have at most one '%s' element" % (filename, name))
+ if n:
+ if allowXHTML:
+ return ''.join([x.toxml() for x in n[0].childNodes])
+ return _get_text(n[0].childNodes).strip()
+ return check
+
+
+def _check_required(name, allowXHTML=False):
+ """
+ Validator for required elements.
+
+ :raise: :exc:`InvalidStack` If validation fails
+ """
+ def check(n, filename):
+ n = _get_nodes_by_name(n, name)
+ if len(n) != 1:
+ raise InvalidStack("Invalid stack.xml file [%s]: must have exactly one '%s' element" % (filename, name))
+ if allowXHTML:
+ return ''.join([x.toxml() for x in n[0].childNodes])
+ return _get_text(n[0].childNodes).strip()
+ return check
+
+
+def _check_depends(n, key, filename):
+ """
+ Validator for stack.xml depends.
+ :raise: :exc:`InvalidStack` If validation fails
+ """
+ nodes = _get_nodes_by_name(n, key)
+ return set([_get_text(n.childNodes).strip() for n in nodes])
+
+
+def _build_listed_attributes(n, key, object_type):
+ """
+ Validator for stack.xml depends.
+ :raise: :exc:`InvalidStack` If validation fails
+ """
+ members = set()
+ for node in _get_nodes_by_name(n, key):
+ # The first field is always supposed to be the value
+ attribute_dict = {}
+ for field in object_type._fields:
+ try:
+ attribute_dict[field] = node.getAttribute(field)
+ except:
+ pass
+ attribute_dict[object_type._fields[0]] = _get_text(node.childNodes).strip()
+ members.add(object_type(**attribute_dict))
+ return members
+
+
+def _attrs(node):
+ attrs = {}
+ for k in node.attributes.keys():
+ attrs[k] = node.attributes.get(k).value
+ return attrs
+
+
+def _check(name):
+ """
+ Generic validator for text-based tags.
+ """
+ if name in REQUIRED:
+ return _check_required(name, name in ALLOWXHTML)
+ elif name in OPTIONAL:
+ return _check_optional(name, name in ALLOWXHTML)
+
+
+class Stack(object):
+ """
+ Object representation of a ROS ``stack.xml`` file
+ """
+ __slots__ = [
+ 'name', 'version', 'description', 'authors', 'maintainers', 'license', 'copyright',
+ 'description_brief', 'version_abi', 'url', 'review_notes', 'review_status',
+ 'build_depends', 'depends', 'build_type', 'build_type_file', 'message_generator',
+ 'unknown_tags']
+
+ def __init__(self, filename=None):
+ """
+ :param filename: location of stack.xml. Necessary if
+ converting ``${prefix}`` in ``<export>`` values, ``str``.
+ """
+ self.description = self.description_brief = self.name = \
+ self.version = self.version_abi = \
+ self.license = self.copyright = ''
+ self.url = ''
+ self.authors = []
+ self.maintainers = []
+ self.depends = []
+ self.build_depends = []
+ self.review_notes = self.review_status = ''
+ self.build_type = 'cmake'
+ self.build_type_file = ''
+ self.message_generator = ''
+
+ # store unrecognized tags during parsing
+ self.unknown_tags = []
+
+
+def _get_text(nodes):
+ """
+ DOM utility routine for getting contents of text nodes
+ """
+ return "".join([n.data for n in nodes if n.nodeType == n.TEXT_NODE])
+
+
+def parse_stack_file(stack_path):
+ """
+ Parse stack file.
+
+ :param stack_path: The path of the stack.xml file
+
+ :returns: return :class:`Stack` instance, populated with parsed fields
+ :raises: :exc:`InvalidStack`
+ :raises: :exc:`IOError`
+ """
+ if not os.path.isfile(stack_path):
+ raise IOError("Invalid/non-existent stack.xml file: %s" % (stack_path))
+
+ with open(stack_path, 'r') as f:
+ return parse_stack(f.read(), stack_path)
+
+
+def parse_stack(string, filename):
+ """
+ Parse stack.xml string contents.
+
+ :param string: stack.xml contents, ``str``
+ :param filename: full file path for debugging, ``str``
+ :returns: return parsed :class:`Stack`
+ """
+ # Create some classes to hold some members
+ new_tuples = {}
+ for key, members in LISTED_ATTRIBUTES.items():
+ new_tuples[key] = collections.namedtuple(key, members)
+
+ try:
+ d = dom.parseString(string)
+ except Exception as e:
+ raise InvalidStack("[%s] invalid XML: %s" % (filename, e))
+
+ s = Stack()
+ p = _get_nodes_by_name(d, 'stack')
+ if len(p) != 1:
+ raise InvalidStack("stack.xml [%s] must have a single 'stack' element" % (filename))
+ p = p[0]
+ for attr in [
+ 'name', 'version', 'description',
+ 'license', 'copyright', 'url', 'build_type', 'message_generator'
+ ]:
+ val = _check(attr)(p, filename)
+ if val:
+ setattr(s, attr, val)
+
+ try:
+ tag = _get_nodes_by_name(p, 'description')[0]
+ s.description_brief = tag.getAttribute('brief') or ''
+ except:
+ # means that 'description' tag is missing
+ pass
+
+ s.authors = _build_listed_attributes(p, 'author', new_tuples['Author'])
+ s.maintainers = _build_listed_attributes(p, 'maintainer', new_tuples['Maintainer'])
+ s.depends = _build_listed_attributes(p, 'depends', new_tuples['Depend'])
+ s.build_depends = _build_listed_attributes(p, 'build_depends', new_tuples['Depend'])
+
+ try:
+ tag = _get_nodes_by_name(p, 'review')[0]
+ s.review_status = tag.getAttribute('status') or ''
+ except:
+ pass # stack.xml is missing optional 'review status' tag
+
+ try:
+ tag = _get_nodes_by_name(p, 'review')[0]
+ s.review_notes = tag.getAttribute('notes') or ''
+ except:
+ pass # stack.xml is missing optional 'review notes' tag
+
+ try:
+ tag = _get_nodes_by_name(p, 'build_type')[0]
+ s.build_type_file = tag.getAttribute('file') or ''
+ except:
+ pass # stack.xml is missing optional 'build_type file' tag
+
+ # store unrecognized tags
+ s.unknown_tags = [e.nodeName for e in p.childNodes if e.nodeType == e.ELEMENT_NODE and e.tagName not in VALID]
+ if s.unknown_tags:
+ raise InvalidStack("stack.xml [%s] must be cleaned up from %s" % (filename, str(s.unknown_tags)))
+ return s
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+from __future__ import print_function
+
+import os
+
+import rospkg
+
+search_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'catkin_package_tests'))
+
+
+def test_find_packages():
+ manager = rospkg.rospack.ManifestManager(rospkg.common.MANIFEST_FILE, ros_paths=[search_path])
+ # for backward compatibility a wet package which is not a metapackage is found when searching for MANIFEST_FILE
+ assert(len(manager.list()) == 1)
+ manager = rospkg.rospack.ManifestManager(rospkg.common.STACK_FILE, ros_paths=[search_path])
+ assert(len(manager.list()) == 0)
+ manager = rospkg.rospack.ManifestManager(rospkg.common.PACKAGE_FILE, ros_paths=[search_path])
+
+ for pkg_name in manager.list():
+ assert(pkg_name == 'foo')
+ path = manager.get_path(pkg_name)
+ assert(path == os.path.join(search_path, 'p1', 'foo'))
+
+
+def test_get_manifest():
+ manager = rospkg.rospack.ManifestManager(rospkg.common.MANIFEST_FILE, ros_paths=[search_path])
+ manif = manager.get_manifest("foo")
+ assert(manif.type == "package")
+
+
+def test_licenses():
+ rospack = rospkg.rospack.RosPack(ros_paths=[search_path])
+ licenses_list = ["BSD", "LGPL"]
+ manif = rospack.get_manifest("foo")
+ assert(manif.license == ", ".join(licenses_list))
+ assert(len(manif.licenses) == 2)
+ for l in manif.licenses:
+ assert(l in licenses_list)
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+from __future__ import print_function
+
+
+def test_ResourceNotFound():
+ from rospkg import ResourceNotFound
+ r = ResourceNotFound('blah')
+ assert 'blah' == str(r)
+ assert r.ros_paths is None
+ s = str(r)
+ assert 'None' not in s
+ assert 'blah' in s
+
+ r = ResourceNotFound('blah', ['ros_root', 'ros_package_path'])
+ assert 'blah' == str(r.args[0])
+ assert ['ros_root', 'ros_package_path'] == r.ros_paths
+ s = str(r)
+ print(s)
+ assert 'blah' in s
+ assert 'ros_root' in s
+ assert 'ros_package_path' in s
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# Copyright (c) 2009, 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 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.
+
+import os
+
+import yaml
+
+
+def get_test_path():
+ return os.path.abspath(os.path.join(os.path.dirname(__file__), 'rosdistro'))
+
+
+def get_etc_path():
+ return os.path.abspath(os.path.join(os.path.dirname(__file__), 'fakeetcros'))
+
+
+def test_distro_uri():
+ from rospkg.distro import distro_uri
+ assert distro_uri('groovy') == "http://svn.code.sf.net/p/ros-dry-releases/code/trunk/distros/groovy.rosdistro"
+
+
+def test_current_distro_codename():
+ import rospkg.environment
+ from rospkg.distro import current_distro_codename
+ assert 'awesome' == current_distro_codename(env={'ROS_DISTRO': 'awesome'})
+ env = {rospkg.environment.ROS_ETC_DIR: get_etc_path()}
+ val = current_distro_codename(env=env)
+ assert 'rosawesome' == current_distro_codename(env=env), val
+
+
+def test__current_distro_electric():
+ from rospkg.distro import _current_distro_electric
+ # tripwire, not allowed to throw
+ _current_distro_electric()
+
+
+def test__current_distro_electric_parse_roscore():
+ from rospkg.distro import _current_distro_electric_parse_roscore
+ roscore_file = os.path.join(get_test_path(), 'roscore-electric.xml')
+ assert os.path.exists(roscore_file), roscore_file
+ val = _current_distro_electric_parse_roscore(roscore_file)
+ assert 'electric' == val, val
+
+ bad_roscore_file = os.path.join(get_test_path(), 'roscore-bad.xml')
+ assert _current_distro_electric_parse_roscore(bad_roscore_file) is None
+
+ no_roscore_file = os.path.join(get_test_path(), 'non-existent.xml')
+ assert _current_distro_electric_parse_roscore(no_roscore_file) is None
+
+
+def xtest_Distro_dback(self):
+ # TODO: better unit tests. For now this is mostly a tripwire
+ from rospkg.distro import DistroStack
+ distros = load_Distros_dback()
+ dback = distros['diamondback']
+ r = 'diamondback'
+ v = 'r8596'
+
+ self.assertEquals(r, dback.release_name)
+ self.assertEquals(v, dback.version)
+
+ # make sure ros got assigned and is correct
+ ros = DistroStack('ros', dback_ros_rules, dback_versions['ros'], r, v)
+ self.assertEquals(ros, dback.ros)
+ self.assertEquals(ros, dback.stacks['ros'])
+
+ # make sure the variants are configured
+ self.assert_('base' not in dback.variants)
+
+ ros_base = dback.variants['ros-base']
+ self.assertEquals([], ros_base.extends)
+ ros_base_stacks = ['ros', 'ros_comm']
+ self.assertEquals(ros_base_stacks, ros_base.stack_names)
+
+ robot = dback.variants['robot'] # extends ros-base
+ self.assertEquals(set(['ros-base']), set(robot.extends))
+ robot_stacks = ['common_msgs', 'common', 'diagnostics', 'driver_common', 'geometry', 'robot_model', 'executive_smach']
+ self.assertEquals(set(ros_base_stacks + robot_stacks), set(robot.stack_names))
+ self.assertEquals(set(robot_stacks), set(robot.stack_names_explicit))
+
+ mobile = dback.variants['mobile'] # extends robot
+ mobile_stacks = ['navigation', 'slam_gmapping']
+ self.assertEquals(set(ros_base_stacks + robot_stacks + mobile_stacks), set(mobile.stack_names))
+ self.assertEquals(set(mobile_stacks), set(mobile.stack_names_explicit))
+
+ viz = dback.variants['viz']
+ self.assertEquals([], viz.extends)
+ viz_stacks = ['visualization_common', 'visualization']
+ self.assertEquals(set(viz_stacks), set(viz.stack_names))
+ self.assertEquals(set(viz_stacks), set(viz.stack_names_explicit))
+
+ desktop = dback.variants['desktop'] # robot, rviz
+ self.assertEquals(set(['robot', 'viz']), set(desktop.extends))
+ desktop_stacks = ['ros_tutorials', 'common_tutorials', 'geometry_tutorials', 'visualization_tutorials']
+ self.assertEquals(set(ros_base_stacks + robot_stacks + viz_stacks + desktop_stacks), set(desktop.stack_names))
+ self.assertEquals(set(desktop_stacks), set(desktop.stack_names_explicit))
+
+ simulator_stacks = ['simulator_stage', 'simulator_gazebo', 'physics_ode']
+ perception_stacks = ['image_common', 'image_transport_plugins', 'image_pipeline', 'laser_pipeline', 'perception_pcl', 'vision_opencv']
+ desktop_full = dback.variants['desktop-full']
+ self.assertEquals(set(['desktop', 'mobile', 'perception', 'simulators']), set(desktop_full.extends))
+ self.assertEquals(set(ros_base_stacks + robot_stacks + mobile_stacks + perception_stacks + simulator_stacks + desktop_stacks + viz_stacks), set(desktop_full.stack_names))
+ self.assertEquals([], desktop_full.stack_names_explicit)
+
+ # make sure we loaded the stacks
+ stack_names = ['common', 'common_msgs', 'navigation']
+ for s in stack_names:
+ val = DistroStack(s, dback_rospkg_rules, dback_versions[s], r, v)
+ self.assertEquals(val, dback.stacks[s])
+
+ # test an hg rule
+ dback_geometry_rules = {'hg':
+ {'dev-branch': 'tf_rework',
+ 'distro-tag': '$RELEASE_NAME',
+ 'release-tag': '$STACK_NAME-$STACK_VERSION',
+ 'uri': 'https://ros-geometry.googlecode.com/hg/'},
+ 'repo': 'ros-pkg',
+ }
+ s = 'geometry'
+ val = DistroStack(s, dback_geometry_rules, dback_versions[s], r, v)
+ self.assertEquals(val, dback.stacks[s])
+
+
+def test_expand_rule():
+ from rospkg.distro import expand_rule
+ assert 'foo' == expand_rule('$STACK_NAME', 'foo', 'version', 'release')
+ assert 'version' == expand_rule('$STACK_VERSION', 'foo', 'version', 'release')
+ assert 'release' == expand_rule('$RELEASE_NAME', 'foo', 'version', 'release')
+ assert 'foo-version-release' == expand_rule('$STACK_NAME-$STACK_VERSION-$RELEASE_NAME', 'foo', 'version', 'release')
+
+
+default_rules = {}
+default_rules['git'] = {'git': {'anon-uri': 'https://github.com/ipa320/$STACK_NAME.git',
+ 'dev-branch': 'release_electric',
+ 'distro-tag': '$RELEASE_NAME',
+ 'release-tag': '$STACK_NAME-$STACK_VERSION',
+ 'uri': 'git@github.com:ipa320/$STACK_NAME.git'}}
+rule = default_rules['git']
+
+
+def test_DistroStack():
+ from rospkg.distro import DistroStack
+ s = DistroStack('stack', 'version', 'electric', rule)
+ assert 'stack' == s.name
+ assert 'version' == s.version
+ assert rule == s._rules
+ assert 'git' == s.vcs_config.type
+ assert s.vcs_config.get_branch('devel', False) == ('git@github.com:ipa320/stack.git', 'release_electric')
+ assert s.vcs_config.get_branch('devel', True) == ('https://github.com/ipa320/stack.git', 'release_electric')
+ assert s.vcs_config.get_branch('distro', False) == ('git@github.com:ipa320/stack.git', 'electric'), s.vcs_config.get_branch('release', False)
+ assert s.vcs_config.get_branch('distro', True) == ('https://github.com/ipa320/stack.git', 'electric')
+ assert s.vcs_config.get_branch('release', False) == ('git@github.com:ipa320/stack.git', 'stack-version'), s.vcs_config.get_branch('release', False)
+ assert s.vcs_config.get_branch('release', True) == ('https://github.com/ipa320/stack.git', 'stack-version')
+
+ assert s == s
+ assert s == DistroStack('stack', 'version', 'electric', rule)
+ assert s != 'stack'
+ assert s != DistroStack('stack2', 'version', 'electric', rule)
+ assert s != DistroStack('stack', 'version2', 'electric', rule)
+ assert s != DistroStack('stack', 'version', 'dback', rule)
+ rule2 = rule.copy()
+ rule2['git']['uri'] == 'foo'
+ assert s != DistroStack('stack', 'version', 'dback', rule2)
+
+
+def test_Variant():
+ from rospkg.distro import Variant
+ v = Variant("foo", [], [], [])
+ assert 'foo' == v.name
+ assert [] == v.extends
+ assert [] == v.get_stack_names(True)
+ assert [] == v.get_stack_names(False)
+
+ raw_data = {'extends': ['robot', 'viz'],
+ 'stacks': ['arm_navigation', 'octomap_mapping', 'physics_ode', 'perception_pcl', 'pr2_controllers',
+ 'control', 'pr2_mechanism', 'pr2_common']}
+ stack_names_implicit = raw_data['stacks'] + ['a', 'b', 'c', 'd']
+ v = Variant('bar', raw_data['extends'], raw_data['stacks'], stack_names_implicit)
+ assert set(v.extends) == set(['robot', 'viz']), v.extends
+ assert set(v.get_stack_names(True)) == set(['arm_navigation', 'octomap_mapping', 'physics_ode', 'perception_pcl', 'pr2_controllers',
+ 'control', 'pr2_mechanism', 'pr2_common', 'a', 'b', 'c', 'd'])
+ assert set(v.get_stack_names(False)) == set(['arm_navigation', 'octomap_mapping', 'physics_ode', 'perception_pcl', 'pr2_controllers',
+ 'control', 'pr2_mechanism', 'pr2_common'])
+
+
+def test_Distro():
+ from rospkg.distro import Distro, Variant, DistroStack
+
+ raw_data = {'extends': ['robot', 'viz'],
+ 'stacks': ['arm_navigation', 'octomap_mapping', 'physics_ode', 'perception_pcl', 'pr2_controllers',
+ 'control', 'pr2_mechanism', 'pr2_common']}
+ stack_names_implicit = raw_data['stacks'] + ['a', 'b', 'c', 'd']
+ v = Variant('bar', raw_data['extends'], raw_data['stacks'], stack_names_implicit)
+ s = DistroStack('stack', 'version', 'electric', rule)
+ s_unreleased = DistroStack('unreleased', None, 'electric', rule)
+
+ variants = {'bar': v}
+ stacks = {'stack': s, 'unreleased': s_unreleased}
+ d = Distro(stacks, variants, 'electric', '1', {})
+ assert d._stacks == stacks
+ assert d.variants == variants
+ assert d.release_name == 'electric'
+ assert d.version == '1'
+ assert {} == d.raw_data
+ assert stacks == d.get_stacks(released=False)
+ assert {'stack': s} == d.get_stacks(released=True)
+ assert stacks == d.stacks
+ assert {'stack': s} == d.released_stacks
+
+
+dback_ros_rules = {'svn': {'dev': 'https://code.ros.org/svn/ros/stacks/$STACK_NAME/trunk',
+ 'distro-tag': 'https://code.ros.org/svn/ros/stacks/$STACK_NAME/tags/$RELEASE_NAME',
+ 'release-tag': 'https://code.ros.org/svn/ros/stacks/$STACK_NAME/tags/$STACK_NAME-$STACK_VERSION'},
+ 'repo': 'ros'}
+dback_rospkg_rules = {'svn': {'dev': 'https://code.ros.org/svn/ros-pkg/stacks/$STACK_NAME/trunk',
+ 'distro-tag': 'https://code.ros.org/svn/ros-pkg/stacks/$STACK_NAME/tags/$RELEASE_NAME',
+ 'release-tag': 'https://code.ros.org/svn/ros-pkg/stacks/$STACK_NAME/tags/$STACK_NAME-$STACK_VERSION'},
+ 'repo': 'ros-pkg'}
+
+dback_versions = {
+ 'common': '1.3.3',
+ 'common_msgs': '1.3.5',
+ 'geometry': '1.3.1',
+ 'navigation': '1.3.1',
+ 'ros': '1.4.0',
+}
+
+
+def test_load_distro_bad_data():
+ from rospkg import ResourceNotFound
+ from rospkg.distro import load_distro, InvalidDistro
+ try:
+ load_distro('bad')
+ assert False
+ except ResourceNotFound:
+ pass
+ for i in range(1, 10):
+ if i == 4:
+ # currently non-existent stacks in variants are not determinable
+ continue
+ filename = 'bad%s.rosdistro' % (i)
+ try:
+ d = get_test_path()
+ p = os.path.join(d, filename)
+ load_distro(p)
+ assert False, "should have raised: %s" % (filename)
+ except InvalidDistro:
+ pass
+
+
+def test_load_distro_variants():
+ # test with no and empty variants (issue found in fuerte bringup)
+ from rospkg.distro import load_distro
+ d = get_test_path()
+ for name in ['no_variants.rosdistro', 'empty_variants.rosdistro']:
+ p = os.path.join(d, name)
+ distro = load_distro(p)
+ assert distro.release_name == 'simple', distro.release_name
+ assert set(distro.variants.keys()) == set([])
+
+
+def test_distro_to_rosinstall():
+ from rospkg.distro import distro_to_rosinstall, load_distro
+ d = get_test_path()
+ distro = load_distro(os.path.join(d, 'simple.rosdistro'))
+ data = distro_to_rosinstall(distro, 'devel', variant_name='base', implicit=False, released_only=True, anonymous=True)
+ # should only have a single stack
+ assert len(data) == 1
+ url = 'https://simple.com/svn/trunk/stack1'
+ assert data[0] == dict(svn={'uri': url, 'local-name': 'stack1'})
+ data = distro_to_rosinstall(distro, 'devel', variant_name=None, implicit=False, released_only=True, anonymous=True)
+ assert len(data) == 1
+ assert data[0] == dict(svn={'uri': url, 'local-name': 'stack1'})
+ data = distro_to_rosinstall(distro, 'devel', variant_name=None, implicit=False, released_only=False, anonymous=True)
+ assert len(data) == 1
+ assert data[0] == dict(svn={'uri': url, 'local-name': 'stack1'})
+ # TODO: need more complete tests with more complicated files
+
+
+def test_load_distro_simple():
+ from rospkg.distro import load_distro, Distro
+ d = get_test_path()
+ p = os.path.join(d, 'simple.rosdistro')
+ distro = load_distro(p)
+ assert isinstance(distro, Distro)
+
+ assert distro.release_name == 'simple', distro.release_name
+ assert distro.version == '1', distro.version
+ assert yaml.load(open(p)) == distro.raw_data, distro.raw_data
+ assert set(distro.variants.keys()) == set(['base'])
+ assert set(distro.stacks.keys()) == set(['stack1'])
+
+ stack1 = distro.stacks['stack1']
+ assert stack1.vcs_config.get_branch('devel', False) == ('https://simple.com/svn/trunk/stack1', None)
+ assert stack1.vcs_config.get_branch('distro', False) == ('https://simple.com/svn/tags/distros/simple/stacks/stack1', None)
+ assert stack1.vcs_config.get_branch('release', False) == ('https://simple.com/svn/tags/stacks/stack1/stack1-0.3.0', None)
+
+
+def test_load_distro_diamondback():
+ from rospkg.distro import load_distro, Distro
+ d = get_test_path()
+ p = os.path.join(d, 'diamondback.rosdistro')
+ distro = load_distro(p)
+ assert isinstance(distro, Distro)
+
+ assert distro.release_name == 'diamondback', distro.release_name
+ assert distro.version == 'r8596', distro.version
+ assert yaml.load(open(p)) == distro.raw_data, distro.raw_data
+ assert set(distro.variants.keys()) == set(diamondback_variants)
+ assert set(distro.stacks.keys()) == set(diamondback_stacks), set(distro.stacks.keys()) ^ set(diamondback_stacks)
+
+ assert distro.variants['ros-base'].extends == []
+ retval = distro.variants['ros-base'].get_stack_names(True)
+ assert retval == ['ros', 'ros_comm'], retval
+ assert distro.variants['ros-base'].get_stack_names(False) == ['ros', 'ros_comm']
+ assert set(distro.variants['ros-full'].get_stack_names(True)) == set(['ros', 'ros_comm', 'rx', 'documentation'])
+
+ assert distro.stacks['common'].version == '1.3.3'
+ assert distro.stacks['common'].vcs_config.get_branch('devel', True) == ('https://code.ros.org/svn/ros-pkg/stacks/common/trunk', None)
+
+
+def test__load_variants():
+ from rospkg.distro import _load_variants
+ raw_data = yaml.load("""variants:
+- ros-base:
+ stacks: [ros, ros_comm]
+- ros-full:
+ extends: ros-base
+ stacks: [rx, documentation]
+- viz:
+ stacks: [visualization_common, visualization]
+- robot:
+ extends: [ros-base]
+ stacks: [common_msgs, common, diagnostics]
+- desktop:
+ extends: [robot, viz, ros-full]
+ stacks: [ros_tutorials, common_tutorials]
+""")
+ raw_data = raw_data['variants']
+ # mock data so variants validate
+ stacks = dict(ros=1, ros_comm=2, rx=3, documentation=4, visualization_common=5,
+ visualization=6, common_msgs=7, common=8, ros_tutorials=9, common_tutorials=10, diagnostics=11)
+ variants = _load_variants(raw_data, stacks)
+ assert set(variants.keys()) == set(['ros-base', 'ros-full', 'viz', 'robot', 'desktop']), variants.keys()
+ assert variants['ros-base'].extends == []
+ assert variants['ros-full'].extends == ['ros-base']
+ assert variants['desktop'].extends == ['robot', 'viz', 'ros-full']
+
+ assert set(variants['ros-base'].get_stack_names(True)) == set(['ros', 'ros_comm'])
+ assert set(variants['ros-base'].get_stack_names(False)) == set(['ros', 'ros_comm'])
+
+ assert set(variants['ros-full'].get_stack_names(True)) == set(['rx', 'documentation', 'ros', 'ros_comm'])
+ assert set(variants['ros-full'].get_stack_names(False)) == set(['rx', 'documentation'])
+
+ assert set(variants['desktop'].get_stack_names(True)) == set(stacks.keys())
+ assert set(variants['desktop'].get_stack_names(False)) == set(['ros_tutorials', 'common_tutorials'])
+
+
+diamondback_stacks = [
+ 'pr2_web_apps', 'octomap_mapping', 'motion_planning_environment', 'robot_calibration',
+ 'sound_drivers', 'joystick_drivers', 'ros',
+ 'pano', 'knowrob', 'perception_pcl', 'image_pipeline', 'kinect',
+ 'bosch_skin', 'pr2_common_actions', 'pr2_arm_navigation_apps', 'ocr', 'articulation',
+ 'nxt_robots', 'visualization_common', 'physics_ode', 'arm_navigation', 'collision_environment',
+ 'executive_smach', 'ethzasl_aseba', 'cart_pushing', 'velodyne', 'pr2_arm_navigation_tests',
+ 'art_vehicle', 'common', 'motion_planning_visualization', 'geometry_tutorials', 'people',
+ 'pr2_power_drivers', 'joystick_drivers_tutorials', 'cob_common', 'vslam', 'pr2_arm_navigation',
+ 'ias_common', 'pr2_navigation_apps', 'geometry_experimental', 'rx', 'motion_planners',
+ 'pr2_gui', 'simulator_stage', 'linux_networking', 'pr2_calibration', 'image_common',
+ 'visualization', 'mpi', 'cob_extern', 'camera_drivers', 'laser_drivers',
+ 'orocos_toolchain_ros', 'driver_common', 'common_msgs', 'pr2_controllers', 'robot_model',
+ 'motion_planning_common', 'simulator_gazebo', 'cram_pl', 'multimaster_experimental', 'navigation',
+ 'pr2_robot', 'geometry', 'freiburg_tools', 'nxt_apps', 'wifi_drivers',
+ 'slam_gmapping', 'web_interface', 'vision_opencv', 'kinematics', 'pr2_simulator',
+ 'roshpit', 'pr2_cockpit', 'pr2_kinematics', 'sql_database',
+ 'navigation_experimental', 'pr2_object_manipulation', 'erratic_robot', 'object_manipulation', 'tabletop_object_perception',
+ 'pr2_tabletop_manipulation_apps', 'bosch_drivers', 'image_transport_plugins', 'perception_pcl_addons', 'slam_karto',
+ 'wg_hardware_test', 'ros_release', 'pr2_navigation', 'exploration', 'continuous_ops',
+ 'control', 'ros_tutorials', 'pr2_ethercat_drivers', 'ethzasl_message_transport', 'client_rosjava',
+ 'ros_realtime', 'pr2_mechanism', 'point_cloud_perception', 'wg_pr2_apps', 'graph_mapping',
+ 'cob_driver', 'cob_simulation', 'pr2_common', 'wg_robots_gazebo', 'pr2_common_alpha',
+ 'trajectory_filters', 'topological_navigation', 'imu_drivers', 'ros_applications', 'pr2_exploration',
+ 'common_tutorials', 'ros_comm', 'mapping', 'pr2_plugs', 'roslisp_common',
+ 'wg_common', 'roslisp_support', 'cob_apps', 'nxt', 'pr2_apps', 'visualization_tutorials',
+ 'laser_pipeline', 'pr2_kinematics_with_constraints', 'documentation', 'pr2_self_test', 'diagnostics', 'pr2_doors']
+
+diamondback_variants = [
+ 'ros-base', 'ros-full', 'viz', 'robot', 'simulators', 'mobile', 'perception', 'desktop',
+ 'desktop-full', 'move-arm', 'pr2-base', 'pr2', 'pr2-desktop', 'pr2-applications',
+ 'wg-pr2', 'care-o-bot', 'bosch', 'nxtall', 'alufr', 'utexas-art', 'tum']
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+default_rules = {}
+rosinstalls = {}
+default_rules['git'] = {'git': {'anon-uri': 'https://github.com/ipa320/$STACK_NAME.git',
+ 'dev-branch': 'release_electric',
+ 'distro-tag': '$RELEASE_NAME',
+ 'release-tag': '$STACK_NAME-$STACK_VERSION',
+ 'uri': 'git@github.com:ipa320/$STACK_NAME.git'}}
+rosinstalls['git'] = {}
+rosinstalls['git']['release-tar'] = [{'tar': {'local-name': 'local_name',
+ 'version': '$STACK_NAME-$STACK_VERSIONevaled',
+ 'uri': 'http://svn.code.sf.net/p/ros-dry-releases/code/download/stacks/$STACK_NAME/$STACK_NAME-$STACK_VERSION/$STACK_NAME-$STACK_VERSION.tar.bz2evaled'}}]
+rosinstalls['git']['devel'] = [{'git': {'local-name': 'local_name',
+ 'version': 'release_electricevaled',
+ 'uri': 'https://github.com/ipa320/$STACK_NAME.gitevaled'}}]
+rosinstalls['git']['release'] = [{'git': {'local-name': 'local_name',
+ 'version': '$STACK_NAME-$STACK_VERSIONevaled',
+ 'uri': 'https://github.com/ipa320/$STACK_NAME.gitevaled'}}]
+rosinstalls['git']['distro'] = [{'git': {'local-name': 'local_name',
+ 'version': '$RELEASE_NAMEevaled',
+ 'uri': 'https://github.com/ipa320/$STACK_NAME.gitevaled'}}]
+default_rules['svn'] = {'svn': {'dev': 'https://alufr-ros-pkg.googlecode.com/svn/trunk/$STACK_NAME',
+ 'distro-tag': 'https://alufr-ros-pkg.googlecode.com/svn/tags/distros/$RELEASE_NAME/stacks/$STACK_NAME',
+ 'release-tag': 'https://alufr-ros-pkg.googlecode.com/svn/tags/stacks/$STACK_NAME/$STACK_NAME-$STACK_VERSION'}}
+rosinstalls['svn'] = {}
+rosinstalls['svn']['release-tar'] = rosinstalls['git']['release-tar']
+rosinstalls['svn']['devel'] = [{'svn': {'local-name': 'local_name',
+ 'uri': 'https://alufr-ros-pkg.googlecode.com/svn/trunk/$STACK_NAMEevaled',
+ }}]
+rosinstalls['svn']['release'] = [{'svn': {'local-name': 'local_name',
+ 'uri': 'https://alufr-ros-pkg.googlecode.com/svn/tags/stacks/$STACK_NAME/$STACK_NAME-$STACK_VERSIONevaled',
+ }}]
+rosinstalls['svn']['distro'] = [{'svn': {'local-name': 'local_name',
+ 'uri': 'https://alufr-ros-pkg.googlecode.com/svn/tags/distros/$RELEASE_NAME/stacks/$STACK_NAMEevaled',
+ }}]
+default_rules['hg'] = {'hg': {'dev-branch': 'default',
+ 'distro-tag': '$RELEASE_NAME',
+ 'release-tag': '$STACK_NAME-$STACK_VERSION',
+ 'uri': 'https://kforge.ros.org/navigation/navigation'}}
+default_rules['bzr'] = {'bzr': {'anon-uri': 'lp:sr-ros-interface',
+ 'dev-branch': 'stable',
+ 'distro-tag': '$RELEASE_NAME',
+ 'release-tag': '$STACK_NAME-$STACK_VERSION',
+ 'uri': 'bzr+ssh://bazaar.launchpad.net/~shadowrobot/sr-ros-interface'}}
+
+
+def test_to_rosinstall():
+ from rospkg.distro import load_vcs_config
+
+ def rule_eval(x):
+ return x + 'evaled'
+ anonymous = True
+ # TODO: for branch in ['devel', 'release', 'distro']:
+ for vcs in ['git', 'svn']:
+ vcs_config = load_vcs_config(default_rules[vcs], rule_eval)
+ for branch in ['release', 'distro', 'release-tar', 'devel']:
+ retval = vcs_config.to_rosinstall('local_name', branch, anonymous)
+ assert retval == rosinstalls[vcs][branch], "%s %s:\n%s\nvs.\n%s" % (vcs, branch, retval, rosinstalls[vcs][branch])
+
+
+def test_VcsConfig():
+ from rospkg.distro import VcsConfig
+ vcs_config = VcsConfig('fake')
+ vcs_config.tarball_url = 'http://foo'
+ assert 'fake' == vcs_config.type
+ for b in ['devel', 'release', 'distro']:
+ try:
+ vcs_config.get_branch(b, False)
+ assert False, "should have raised" + b
+ except ValueError:
+ pass
+ for anon in [True, False]:
+ assert ('http://foo', None) == vcs_config.get_branch('release-tar', anon)
+
+
+def test_BZRConfig():
+ from rospkg.distro import BzrConfig
+ anon_rules = default_rules['bzr']['bzr']
+ rules = anon_rules.copy()
+ rules['uri'] = rules['anon-uri']
+ del rules['anon-uri']
+
+ config = BzrConfig()
+ anon_config = BzrConfig()
+
+ required = ['dev-branch', 'distro-tag', 'release-tag', 'uri']
+ for r in required:
+ bad_copy = rules.copy()
+ del bad_copy[r]
+ try:
+ config.load(bad_copy, lambda x: x)
+ assert False, "should have raised"
+ except KeyError:
+ pass
+
+ config.load(rules, lambda x: x + 'evaled')
+ anon_config.load(anon_rules, lambda x: x + 'evaled')
+
+ repo_uri = anon_rules['uri'] + 'evaled'
+ anon_repo_uri = anon_rules['anon-uri'] + 'evaled'
+ assert config.repo_uri == anon_repo_uri, config.repo_uri
+ assert config.anon_repo_uri == anon_repo_uri, config.anon_repo_uri
+ assert anon_config.repo_uri == repo_uri, anon_config.repo_uri
+ for c in [config, anon_config]:
+ assert c.dev_branch == 'stableevaled'
+ assert c.distro_tag == '$RELEASE_NAMEevaled'
+ assert c.release_tag == '$STACK_NAME-$STACK_VERSIONevaled'
+ assert c.anon_repo_uri == anon_repo_uri
+
+ c = anon_config
+ assert c.get_branch('devel', False) == (repo_uri, 'stableevaled')
+ assert c.get_branch('devel', True) == (anon_repo_uri, 'stableevaled')
+ assert c.get_branch('distro', False) == (repo_uri, '$RELEASE_NAMEevaled')
+ assert c.get_branch('distro', True) == (anon_repo_uri, '$RELEASE_NAMEevaled')
+ assert c.get_branch('release', False) == (repo_uri, '$STACK_NAME-$STACK_VERSIONevaled')
+ assert c.get_branch('release', True) == (anon_repo_uri, '$STACK_NAME-$STACK_VERSIONevaled')
+ try:
+ c.get_branch('foo', True)
+ assert False
+ except ValueError:
+ pass
+ # setup for coverage -- invalidate release branch
+ rel_tag = c.release_tag
+ c.release_tag = None
+ try:
+ assert c.get_branch('release', False)
+ assert False
+ except ValueError:
+ pass
+ c.release_tag = rel_tag
+
+ # test equals
+ config2 = BzrConfig()
+ config2.load(rules, lambda x: x + 'evaled')
+ assert config == config2
+ anon_config2 = BzrConfig()
+ anon_config2.load(anon_rules, lambda x: x + 'evaled')
+ assert anon_config == anon_config2
+
+ # test eq
+ config_check = BzrConfig()
+ config_check_eq = BzrConfig()
+ config_check_neq = BzrConfig()
+ config_check.load(rules, lambda x: x + 'evaled')
+ config_check_eq.load(rules, lambda x: x + 'evaled')
+ config_check_neq.load(anon_rules, lambda x: x + 'evaled')
+ assert config_check == config_check_eq
+ assert config_check != config_check_neq
+
+
+def test_HgConfig():
+ from rospkg.distro import HgConfig
+ anon_rules = {
+ 'dev-branch': 'default',
+ 'distro-tag': '$RELEASE_NAME',
+ 'release-tag': '$STACK_NAME-$STACK_VERSION',
+ 'anon-uri': 'https://kforge.ros.org/navigation/navigation',
+ 'uri': 'ssh://user@kforge.ros.org/navigation/navigation'
+ }
+ rules = default_rules['hg']['hg']
+
+ config = HgConfig()
+ anon_config = HgConfig()
+
+ required = ['dev-branch', 'distro-tag', 'release-tag', 'uri']
+ for r in required:
+ bad_copy = rules.copy()
+ del bad_copy[r]
+ try:
+ config.load(bad_copy, lambda x: x)
+ assert False, "should have raised"
+ except KeyError:
+ pass
+
+ config.load(rules, lambda x: x + 'evaled')
+ anon_config.load(anon_rules, lambda x: x + 'evaled')
+
+ repo_uri = 'ssh://user@kforge.ros.org/navigation/navigationevaled'
+ anon_repo_uri = 'https://kforge.ros.org/navigation/navigationevaled'
+ assert config.repo_uri == anon_repo_uri, config.repo_uri
+ assert config.anon_repo_uri == anon_repo_uri, config.anon_repo_uri
+ assert anon_config.repo_uri == repo_uri, anon_config.repo_uri
+ for c in [config, anon_config]:
+ assert c.dev_branch == 'defaultevaled'
+ assert c.distro_tag == '$RELEASE_NAMEevaled'
+ assert c.release_tag == '$STACK_NAME-$STACK_VERSIONevaled'
+ assert c.anon_repo_uri == anon_repo_uri
+
+ c = anon_config
+ assert c.get_branch('devel', False) == (repo_uri, 'defaultevaled')
+ assert c.get_branch('devel', True) == (anon_repo_uri, 'defaultevaled')
+ assert c.get_branch('distro', False) == (repo_uri, '$RELEASE_NAMEevaled')
+ assert c.get_branch('distro', True) == (anon_repo_uri, '$RELEASE_NAMEevaled')
+ assert c.get_branch('release', False) == (repo_uri, '$STACK_NAME-$STACK_VERSIONevaled')
+ assert c.get_branch('release', True) == (anon_repo_uri, '$STACK_NAME-$STACK_VERSIONevaled')
+
+ # test equals
+ config2 = HgConfig()
+ config2.load(rules, lambda x: x + 'evaled')
+ assert config == config2
+ anon_config2 = HgConfig()
+ anon_config2.load(anon_rules, lambda x: x + 'evaled')
+ assert anon_config == anon_config2
+
+ # test eq
+ config_check = HgConfig()
+ config_check_eq = HgConfig()
+ config_check_neq = HgConfig()
+ config_check.load(rules, lambda x: x + 'evaled')
+ config_check_eq.load(rules, lambda x: x + 'evaled')
+ config_check_neq.load(anon_rules, lambda x: x + 'evaled')
+ assert config_check == config_check_eq
+ assert config_check != config_check_neq
+
+
+def test_GitConfig():
+ from rospkg.distro import GitConfig
+ anon_rules = default_rules['git']['git']
+ rules = anon_rules.copy()
+ del rules['anon-uri']
+
+ config = GitConfig()
+ anon_config = GitConfig()
+
+ required = ['dev-branch', 'distro-tag', 'release-tag', 'uri']
+ for r in required:
+ bad_copy = rules.copy()
+ del bad_copy[r]
+ try:
+ config.load(bad_copy, lambda x: x)
+ assert False, "should have raised"
+ except KeyError:
+ pass
+
+ config.load(rules, lambda x: x + 'evaled')
+ anon_config.load(anon_rules, lambda x: x + 'evaled')
+ repo_uri = 'git@github.com:ipa320/$STACK_NAME.gitevaled'
+ anon_repo_uri = 'https://github.com/ipa320/$STACK_NAME.gitevaled'
+
+ assert config.repo_uri == repo_uri
+ assert anon_config.anon_repo_uri == anon_repo_uri
+ for c in [config, anon_config]:
+ dev_branch = 'release_electricevaled'
+ assert c.dev_branch == dev_branch
+ assert c.distro_tag == '$RELEASE_NAMEevaled'
+ assert c.release_tag == '$STACK_NAME-$STACK_VERSIONevaled'
+ assert c.repo_uri == repo_uri
+
+ c = anon_config
+ assert c.get_branch('devel', False) == (repo_uri, dev_branch), c.get_branch('devel', False)
+ assert c.get_branch('devel', True) == (anon_repo_uri, dev_branch)
+ assert c.get_branch('distro', False) == (repo_uri, '$RELEASE_NAMEevaled')
+ assert c.get_branch('distro', True) == (anon_repo_uri, '$RELEASE_NAMEevaled')
+ assert c.get_branch('release', False) == (repo_uri, '$STACK_NAME-$STACK_VERSIONevaled')
+ assert c.get_branch('release', True) == (anon_repo_uri, '$STACK_NAME-$STACK_VERSIONevaled')
+
+ # test equals
+ config2 = GitConfig()
+ config2.load(rules, lambda x: x + 'evaled')
+ assert config == config2
+ anon_config2 = GitConfig()
+ anon_config2.load(anon_rules, lambda x: x + 'evaled')
+ assert anon_config == anon_config2
+
+
+def test_SvnConfig():
+ from rospkg.distro import SvnConfig
+ config = SvnConfig()
+ required = ['dev', 'distro-tag', 'release-tag']
+ rules = default_rules['svn']['svn']
+
+ anon_rules = {
+ 'anon-dev': 'http://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/trunk',
+ 'anon-distro-tag': 'http://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$RELEASE_NAME',
+ 'anon-release-tag': 'http://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$STACK_NAME-$STACK_VERSION',
+ 'dev': 'https://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/trunk',
+ 'distro-tag': 'https://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$RELEASE_NAME',
+ 'release-tag': 'https://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$STACK_NAME-$STACK_VERSION'}
+
+ # make sure it validates
+ for k in required:
+ bad_copy = rules.copy()
+ del bad_copy[k]
+ try:
+ config.load(bad_copy, lambda x: x)
+ assert False, "should have raised"
+ except KeyError:
+ pass
+
+ # load w/o anon rules
+ config.load(rules, lambda x: x + 'evaled')
+ assert config.dev == 'https://alufr-ros-pkg.googlecode.com/svn/trunk/$STACK_NAMEevaled'
+ assert config.distro_tag == 'https://alufr-ros-pkg.googlecode.com/svn/tags/distros/$RELEASE_NAME/stacks/$STACK_NAMEevaled'
+ assert config.release_tag == 'https://alufr-ros-pkg.googlecode.com/svn/tags/stacks/$STACK_NAME/$STACK_NAME-$STACK_VERSIONevaled'
+
+ assert config.anon_dev == 'https://alufr-ros-pkg.googlecode.com/svn/trunk/$STACK_NAMEevaled'
+ assert config.anon_distro_tag == 'https://alufr-ros-pkg.googlecode.com/svn/tags/distros/$RELEASE_NAME/stacks/$STACK_NAMEevaled'
+ assert config.anon_release_tag == 'https://alufr-ros-pkg.googlecode.com/svn/tags/stacks/$STACK_NAME/$STACK_NAME-$STACK_VERSIONevaled'
+
+ # test eq
+ config_check = SvnConfig()
+ config_check_eq = SvnConfig()
+ config_check_neq = SvnConfig()
+ config_check.load(rules, lambda x: x + 'evaled')
+ config_check_eq.load(rules, lambda x: x + 'evaled')
+ config_check_neq.load(anon_rules, lambda x: x + 'evaled')
+ assert config_check == config_check_eq
+ assert config_check != config_check_neq
+
+ # load w anon rules
+ config2 = SvnConfig()
+ config.load(anon_rules, lambda x: x + 'evaled')
+ config2.load(anon_rules, lambda x: x + 'evaled')
+ for c in [config, config2]:
+ assert c.anon_dev == 'http://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/trunkevaled'
+ assert c.anon_distro_tag == 'http://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$RELEASE_NAMEevaled'
+ assert c.anon_release_tag == 'http://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$STACK_NAME-$STACK_VERSIONevaled'
+ assert c.dev == 'https://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/trunkevaled'
+ assert c.distro_tag == 'https://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$RELEASE_NAMEevaled'
+ assert c.release_tag == 'https://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$STACK_NAME-$STACK_VERSIONevaled'
+
+ # test get_branch
+ assert c.get_branch('devel', True) == ('http://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/trunkevaled', None)
+ assert c.get_branch('distro', True) == ('http://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$RELEASE_NAMEevaled', None)
+ assert c.get_branch('release', True) == ('http://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$STACK_NAME-$STACK_VERSIONevaled', None)
+ assert c.get_branch('devel', False) == ('https://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/trunkevaled', None)
+ assert c.get_branch('distro', False) == ('https://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$RELEASE_NAMEevaled', None)
+ assert c.get_branch('release', False) == ('https://svn.mech.kuleuven.be/repos/orocos/trunk/kul-ros-pkg/stacks/$STACK_NAME/tags/$STACK_NAME-$STACK_VERSIONevaled', None)
+
+ # get full coverage on get_branch()
+ try:
+ c.get_branch('fake', False)
+ assert False
+ except KeyError:
+ pass
+ # setup for coverage -- invalidate release branch
+ rel_tag = c.release_tag
+ c.release_tag = None
+ try:
+ assert c.get_branch('release', False)
+ assert False
+ except ValueError:
+ pass
+ c.release_tag = rel_tag
+
+
+def test_load_vcs_config():
+ from rospkg.distro import load_vcs_config, get_vcs_configs
+ for t in ['svn', 'git', 'hg', 'bzr']:
+ assert t in get_vcs_configs()
+ config = load_vcs_config(default_rules[t], lambda x: x + 'evaled')
+ assert config.type == t, t
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+import os
+import tempfile
+
+
+def test_get_ros_root():
+ from rospkg import get_ros_root
+ assert get_ros_root(env={}) is None
+
+ env = {'ROS_ROOT': '/fake/path'}
+ assert '/fake/path' == get_ros_root(env=env)
+
+ real_ros_root = get_ros_root()
+
+ if real_ros_root is not None:
+ # make sure that ros root is a directory
+ p = os.path.join(real_ros_root, 'Makefile')
+ env = {'ROS_ROOT': p}
+ assert p == get_ros_root(env=env)
+
+
+def test_get_ros_package_path():
+ from rospkg import get_ros_package_path
+ assert get_ros_package_path(env={}) is None
+ env = {'ROS_PACKAGE_PATH': ':'}
+ assert ':' == get_ros_package_path(env=env)
+
+ # trip-wire tests. Cannot guarantee that ROS_PACKAGE_PATH is set
+ # to valid value on test machine, just make sure logic doesn't crash
+ assert os.environ.get('ROS_PACKAGE_PATH', None) == get_ros_package_path()
+
+
+def test_get_log_dir():
+ from rospkg import get_log_dir, get_ros_root
+ base = tempfile.gettempdir()
+ ros_log_dir = os.path.join(base, 'ros_log_dir')
+ ros_home_dir = os.path.join(base, 'ros_home_dir')
+ home_dir = os.path.expanduser('~')
+
+ # ROS_LOG_DIR has precedence
+ env = {'ROS_ROOT': get_ros_root(), 'ROS_LOG_DIR': ros_log_dir, 'ROS_HOME': ros_home_dir}
+ assert ros_log_dir == get_log_dir(env=env)
+
+ env = {'ROS_ROOT': get_ros_root(), 'ROS_HOME': ros_home_dir}
+ assert os.path.join(ros_home_dir, 'log') == get_log_dir(env=env)
+
+ env = {'ROS_ROOT': get_ros_root()}
+ assert os.path.join(home_dir, '.ros', 'log') == get_log_dir(env=env)
+
+ # test default assignment of env. Don't both checking return value as we would duplicate get_log_dir
+ assert get_log_dir() is not None
+
+
+def test_get_test_results_dir():
+ from rospkg import get_ros_root, get_test_results_dir
+ base = tempfile.gettempdir()
+ ros_test_results_dir = os.path.join(base, 'ros_test_results_dir')
+ ros_home_dir = os.path.join(base, 'ros_home_dir')
+ home_dir = os.path.expanduser('~')
+
+ # ROS_TEST_RESULTS_DIR has precedence
+ env = {'ROS_ROOT': get_ros_root(), 'ROS_TEST_RESULTS_DIR': ros_test_results_dir, 'ROS_HOME': ros_home_dir}
+ assert ros_test_results_dir == get_test_results_dir(env=env)
+
+ env = {'ROS_ROOT': get_ros_root(), 'ROS_HOME': ros_home_dir}
+ assert os.path.join(ros_home_dir, 'test_results') == get_test_results_dir(env=env)
+
+ env = {'ROS_ROOT': get_ros_root()}
+ assert os.path.join(home_dir, '.ros', 'test_results') == get_test_results_dir(env=env)
+
+ # test default assignment of env. Don't both checking return value as we would duplicate get_test_results_dir
+ assert get_test_results_dir() is not None
+
+
+def test_get_ros_home():
+ from rospkg import get_ros_root, get_ros_home
+ base = tempfile.gettempdir()
+ ros_home_dir = os.path.join(base, 'ros_home_dir')
+ home_dir = os.path.expanduser('~')
+
+ # ROS_HOME has precedence
+ env = {'ROS_ROOT': get_ros_root(), 'ROS_HOME': ros_home_dir}
+ assert ros_home_dir == get_ros_home(env=env)
+
+ env = {'ROS_ROOT': get_ros_root()}
+ assert os.path.join(home_dir, '.ros') == get_ros_home(env=env)
+
+ # test default assignment of env. Don't both checking return value
+ assert get_ros_home() is not None
+
+
+def test_on_ros_path():
+ from rospkg import on_ros_path, get_ros_root, get_ros_package_path
+ from rospkg.environment import _resolve_paths
+
+ assert not on_ros_path(tempfile.gettempdir())
+
+ if get_ros_root() is not None:
+ assert on_ros_path(get_ros_root())
+
+ if get_ros_package_path() is not None:
+ paths = _resolve_paths(get_ros_package_path()).split(os.pathsep)
+ for p in paths:
+ assert on_ros_path(p), "failed: %s, [%s]" % (p, paths)
+
+
+def test_compute_package_paths():
+ from rospkg.environment import _compute_package_paths as compute_package_paths
+ assert compute_package_paths(None, None) == []
+ assert compute_package_paths('foo', None) == ['foo']
+ assert compute_package_paths(None, 'bar') == ['bar'], compute_package_paths(None, 'bar')
+ assert compute_package_paths('foo', '') == ['foo']
+ assert compute_package_paths('foo', 'bar') == ['foo', 'bar']
+ assert compute_package_paths('foo', 'bar:bz') == ['foo', 'bar', 'bz']
+ assert compute_package_paths('foo', 'bar:bz::blah') == ['foo', 'bar', 'bz', 'blah']
+
+
+def test_resolve_path():
+ # mainly for coverage
+ from rospkg.environment import _resolve_path
+ assert os.path.expanduser('~') == _resolve_path('~')
+
+
+def test_get_etc_ros_dir():
+ from rospkg import get_etc_ros_dir
+ from rospkg.environment import ROS_ETC_DIR
+ base = tempfile.gettempdir()
+ etc_ros_dir = os.path.join(base, 'etc_ros_dir')
+
+ assert '/etc/ros' == get_etc_ros_dir(env={})
+
+ # ROS_ETC_DIR has precedence
+ env = {ROS_ETC_DIR: etc_ros_dir}
+ assert etc_ros_dir == get_etc_ros_dir(env=env), get_etc_ros_dir(env=env)
+
+ # test default assignment of env. Don't both checking return value as we would duplicate get_etc_ros_dir
+ assert get_etc_ros_dir() is not None
--- /dev/null
+# -*- coding: utf-8 -*-
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+from __future__ import print_function
+
+import os
+
+
+def test_InvalidManifest():
+ from rospkg import InvalidManifest
+ assert isinstance(InvalidManifest(), Exception)
+
+
+def test_Platform():
+ from rospkg.manifest import Platform
+ for bad in [None, '']:
+ try:
+ Platform(bad, '1')
+ assert False, "should have failed on [%s]" % bad
+ except ValueError:
+ pass
+ try:
+ Platform('ubuntu', bad)
+ assert False, "should have failed on [%s]" % bad
+ except ValueError:
+ pass
+
+ p = Platform('ubuntu', '8.04')
+ assert 'ubuntu 8.04' == str(p)
+ assert 'ubuntu 8.04' == repr(p)
+
+ for v in [Platform('ubuntu', '8.04'), Platform('ubuntu', '8.04', notes=None)]:
+ assert p == p
+ for v in [Platform('ubuntu', '8.04', 'some notes'), 'foo', 1]:
+ assert p != v
+
+ # note: probably actually "osx"
+ p = Platform('OS X', '10.6', 'macports')
+ assert 'OS X 10.6' == str(p)
+ assert 'OS X 10.6' == repr(p)
+
+ for v in [p, Platform('OS X', '10.6', 'macports')]:
+ assert p == p
+ for v in [Platform('OS X', '10.6'), 'foo', 1]:
+ assert p != v
+
+
+def test_Depend():
+ from rospkg.manifest import Depend
+ for bad in [None, '']:
+ try:
+ Depend(bad, 'package')
+ assert False, "should have failed on [%s]" % bad
+ except ValueError:
+ pass
+ try:
+ Depend('foo', bad)
+ assert False, "should have failed on [%s]" % bad
+ except ValueError:
+ pass
+
+ d = Depend('roslib', 'package')
+ assert 'roslib' == str(d)
+ assert 'roslib' == repr(d)
+
+ assert d == Depend('roslib', 'package')
+ for v in [Depend('roslib', 'stack'), Depend('roslib2', 'package'), 1]:
+ assert d != v
+
+
+def _subtest_parse_example1(m):
+ from rospkg.manifest import Manifest
+ assert isinstance(m, Manifest)
+ assert 'package' == m.type
+ assert "a brief description" == m.brief
+ assert "Line 1\nLine 2" == m.description.strip()
+ assert u"The authors go here.\nutf-8 test: ÄÖÜ" == m.author.strip()
+ assert "Public Domain\nwith other stuff" == m.license.strip()
+ assert "http://pr.willowgarage.com/package/" == m.url
+ for d in m.depends:
+ assert 'package' == d.type
+ dpkgs = [d.name for d in m.depends]
+ assert set(['pkgname', 'common']) == set(dpkgs)
+ rdpkgs = [d.name for d in m.rosdeps]
+ assert set(['python', 'bar', 'baz']) == set(rdpkgs)
+ for p in m.platforms:
+ if p.os == 'ubuntu':
+ assert "8.04" == p.version
+ assert '' == p.notes
+ elif p.os == 'OS X':
+ assert "10.6" == p.version
+ assert "macports" == p.notes
+ else:
+ assert False, "unknown platform " + str(p)
+
+
+def _subtest_parse_stack_example1(m):
+ from rospkg.manifest import Manifest
+ assert isinstance(m, Manifest)
+ assert 'stack' == m.type
+ assert "a brief description" == m.brief
+ assert "Line 1\nLine 2" == m.description.strip()
+ assert "The authors\ngo here" == m.author.strip()
+ assert "Public Domain\nwith other stuff" == m.license.strip()
+ assert "http://ros.org/stack/" == m.url
+ for d in m.depends:
+ assert 'stack' == d.type
+ dpkgs = [d.name for d in m.depends]
+ assert set(['stackname', 'common']) == set(dpkgs)
+ assert [] == m.rosdeps
+ assert [] == m.exports
+
+
+def _subtest_parse_stack_version(m):
+ assert "1.2.3" == m.version
+
+
+def get_test_dir():
+ return os.path.abspath(os.path.join(os.path.dirname(__file__), 'manifest'))
+
+
+def test_is_catkin():
+ from rospkg.manifest import MANIFEST_FILE, parse_manifest_file
+ d = get_test_dir()
+ m = parse_manifest_file(os.path.join(d, 'catkin'), MANIFEST_FILE)
+ assert m.is_catkin
+ m = parse_manifest_file(os.path.join(d, 'example1'), MANIFEST_FILE)
+ assert not m.is_catkin
+
+
+def test_parse_manifest_file():
+ from rospkg.manifest import parse_manifest_file, MANIFEST_FILE, STACK_FILE
+
+ d = get_test_dir()
+ m = parse_manifest_file(os.path.join(d, 'example1'), MANIFEST_FILE)
+ _subtest_parse_example1(m)
+
+ m = parse_manifest_file(os.path.join(d, 'stack_example1'), STACK_FILE)
+ _subtest_parse_stack_example1(m)
+
+ m = parse_manifest_file(os.path.join(d, 'stack_version'), STACK_FILE)
+ _subtest_parse_stack_version(m)
+
+
+def test_parse_manifest():
+ # test_parse_manifest_file is more thorough; just want to make sure we have one call to lower-level API
+ from rospkg.manifest import MANIFEST_FILE, parse_manifest
+ d = get_test_dir()
+ p = os.path.join(d, 'example1', MANIFEST_FILE)
+ with open(p, 'r') as f:
+ contents = f.read()
+ _subtest_parse_example1(parse_manifest(MANIFEST_FILE, contents, p))
+
+
+def test__Manifest():
+ from rospkg.manifest import Manifest
+ m = Manifest()
+ # check defaults
+ assert 'package' == m.type
+
+ m = Manifest('package')
+ assert 'package' == m.type
+ m = Manifest('stack')
+ assert 'stack' == m.type
+
+ # tripwire, no defined value
+ str(m)
+ repr(m)
+
+
+# bad file examples should be more like the roslaunch tests where there is just 1 thing wrong
+def test_parse_bad_file():
+ from rospkg.manifest import parse_manifest, InvalidManifest, MANIFEST_FILE
+ base_p = get_test_dir()
+ for b in ['bad1.xml', 'bad2.xml', 'bad3.xml']:
+ p = os.path.join(base_p, b)
+ with open(p, 'r') as f:
+ contents = f.read()
+ try:
+ parse_manifest(MANIFEST_FILE, contents, filename=p)
+ assert False, "parse should have failed on bad manifest"
+ except InvalidManifest as e:
+ print(str(e))
+ assert p in str(e), "file name [%s] should be in error message [%s]" % (p, str(e))
+
+
+EXAMPLE1 = u"""<package>
+ <description brief="a brief description">Line 1
+Line 2
+ </description>
+ <author>The authors go here.
+utf-8 test: ÄÖÜ</author>
+ <license>Public Domain
+with other stuff</license>
+ <url>http://pr.willowgarage.com/package/</url>
+ <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
+ <depend package="pkgname" />
+ <depend package="common"/>
+ <export>
+ <cpp cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lros"/>
+ <cpp os="osx" cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lrosthread -framework CoreServices"/>
+ </export>
+ <rosdep name="python" />
+ <rosdep name="bar" />
+ <rosdep name="baz" />
+ <platform os="ubuntu" version="8.04" />
+ <platform os="OS X" version="10.6" notes="macports" />
+ <rosbuild2>
+ <depend thirdparty="thisshouldbeokay"/>
+ </rosbuild2>
+</package>"""
+
+STACK_EXAMPLE1 = """<stack>
+ <description brief="a brief description">Line 1
+Line 2
+ </description>
+ <author>The authors
+go here</author>
+ <license>Public Domain
+with other stuff</license>
+ <url>http://ros.org/stack/</url>
+ <logo>http://www.willowgarage.com/files/willowgarage/robot10.jpg</logo>
+ <depend stack="stackname" />
+ <depend stack="common"/>
+</stack>"""
+
+STACK_INVALID1 = """<stack>
+ <description brief="a brief description">Line 1</description>
+ <author>The authors</author>
+ <license>Public Domain</license>
+ <rosdep name="python" />
+</stack>"""
+
+STACK_INVALID2 = """<stack>
+ <description brief="a brief description">Line 1</description>
+ <author>The authors</author>
+ <license>Public Domain</license>
+ <export>
+ <cpp cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lros"/>
+ <cpp os="osx" cflags="-I${prefix}/include" lflags="-L${prefix}/lib -lrosthread -framework CoreServices"/>
+ </export>
+</stack>"""
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# Copyright (c) 2009, 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 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.
+
+from __future__ import absolute_import
+
+import os
+
+import mock
+from mock import patch
+
+
+class TrueOs():
+ def is_os(self):
+ return True
+
+ def get_version(self):
+ return "os_version"
+
+ def get_codename(self):
+ return "os_codename"
+
+
+class TrueOs2():
+ def is_os(self):
+ return True
+
+ def get_version(self):
+ return "os_version"
+
+ def get_codename(self):
+ return "os_codename"
+
+
+class FalseOs(object):
+ def is_os(self):
+ return False
+
+ def get_version(self):
+ return "os_version2"
+
+ def get_codename(self):
+ return "os_codename"
+
+
+def test__read_stdout():
+ from rospkg.os_detect import _read_stdout
+ assert 'hello' == _read_stdout(['echo', 'hello'])
+ assert _read_stdout(['bad-command-input-for-rospkg-os-detect']) is None
+
+
+def test_tripwire_ubuntu():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('ubuntu')
+
+
+def test_LsbDetect():
+ from rospkg.os_detect import LsbDetect, OsNotDetected
+
+ # test non-match
+ detect = LsbDetect('bad')
+ assert not detect.is_os()
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+ # test match
+ # to be removed after Ubuntu Xenial is out of support
+ import sys
+ if sys.version_info >= (3, 8):
+ import distro
+ else:
+ import platform as distro
+
+ distro.linux_distribution = mock.Mock()
+ distro.linux_distribution.return_value = ('Ubuntu', '10.04', 'lucid')
+ distro.dist = mock.Mock()
+ distro.dist.return_value = ('Ubuntu', '10.04', 'lucid')
+
+ detect = LsbDetect('Ubuntu')
+ assert detect.is_os(), "should be Ubuntu"
+ assert detect.get_codename() == 'lucid', detect.get_codename()
+
+ # test freely
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def test_ubuntu():
+ from rospkg.os_detect import OsDetect, OsNotDetected
+
+ os_detector = OsDetect()
+ detect = os_detector.get_detector('ubuntu')
+ detect.lsb_info = ('Ubuntu', '10.04', 'lucid')
+
+ assert detect.get_version() == '10.04', detect.get_version()
+ assert detect.get_codename() == 'lucid', detect.get_codename()
+
+ # test freely
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def test_tripwire_debian():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('debian')
+
+
+def test_tripwire_osx():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('osx')
+
+
+def test_osx():
+ if 'posix' != os.name:
+ from unittest.case import SkipTest
+ raise SkipTest('Test requires POSIX platform, not "{}"'.format(os.name))
+
+ from rospkg.os_detect import OSX, _osx_codename, OsNotDetected
+ test_dir = os.path.join(get_test_dir(), 'osx')
+ detect = OSX(os.path.join(test_dir, "sw_vers"))
+ assert detect.is_os()
+ assert detect.get_codename() == 'snow'
+ assert detect.get_version() == '10.6.5', detect.get_version()
+
+ # trigger bad version number detect
+ detect = OSX(os.path.join(test_dir, "sw_vers_bad"))
+ assert detect.is_os()
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+ # regression test codename mapping
+ assert 'lion' == _osx_codename(10, 7)
+ try:
+ _osx_codename(9, 7)
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def test_osx_patched():
+ from rospkg.os_detect import OSX, OsNotDetected
+
+ @patch.object(OSX, 'is_os')
+ def test(mock):
+ mock.return_value = False
+ detect = OSX()
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ test()
+
+
+def test_tripwire_arch():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('arch')
+
+
+def test_arch():
+ from rospkg.os_detect import Arch, OsNotDetected
+ test_dir = os.path.join(get_test_dir(), 'arch')
+ detect = Arch(os.path.join(test_dir, "arch-release"))
+ assert detect.is_os()
+ assert detect.get_version() == ''
+
+ detect = Arch()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+ @patch.object(Arch, 'is_os')
+ def test(mock):
+ mock.is_os.return_value = True
+ detect = Arch()
+ assert detect.get_version() == ''
+ assert detect.get_codename() == ''
+ test()
+
+
+def test_tripwire_manjaro():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('manjaro')
+
+
+def test_manjaro():
+ from rospkg.os_detect import Manjaro, OsNotDetected
+ test_dir = os.path.join(get_test_dir(), 'manjaro')
+ detect = Manjaro(os.path.join(test_dir, "manjaro-release"))
+ assert detect.is_os()
+ assert detect.get_version() == ''
+
+ detect = Manjaro()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+ @patch.object(Manjaro, 'is_os')
+ def test(mock):
+ mock.is_os.return_value = True
+ detect = Manjaro()
+ assert detect.get_version() == ''
+ assert detect.get_codename() == ''
+ test()
+
+
+def test_tripwire_opensuse():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('opensuse')
+
+
+def test_opensuse():
+ from rospkg.os_detect import OpenSuse, OsNotDetected
+ test_dir = os.path.join(get_test_dir(), 'opensuse')
+ detect = OpenSuse(os.path.join(test_dir, "SuSE-brand"))
+ assert detect.is_os()
+ assert detect.get_version() == '11.2'
+
+ detect = OpenSuse()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def test_opensuse13():
+ from rospkg.os_detect import OpenSuse, OsNotDetected
+ test_dir = os.path.join(get_test_dir(), 'opensuse13')
+ detect = OpenSuse(os.path.join(test_dir, "SUSE-brand"))
+ assert detect.is_os()
+ assert detect.get_version() == '13.1'
+
+ detect = OpenSuse()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def test_tripwire_gentoo():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('gentoo')
+
+
+def test_gentoo():
+ from rospkg.os_detect import Gentoo, OsNotDetected
+ test_dir = os.path.join(get_test_dir(), 'gentoo')
+ detect = Gentoo(os.path.join(test_dir, "gentoo-release"))
+ assert detect.is_os()
+ assert detect.get_version() == '2.0.1'
+ assert detect.get_codename() == ''
+
+ # test freely
+ detect = Gentoo()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def test_tripwire_fedora():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('fedora')
+
+
+def get_test_dir():
+ return os.path.abspath(os.path.join(os.path.dirname(__file__),
+ 'os_detect'))
+
+
+def test_fedora():
+ from rospkg.os_detect import Fedora, OsNotDetected
+ test_dir = os.path.join(get_test_dir(), 'fedora')
+ release_file, issue_file = [os.path.join(test_dir, x) for
+ x in ["redhat-release", "issue"]]
+ detect = Fedora(release_file, issue_file)
+ assert detect.is_os()
+ assert detect.get_version() == '1'
+ assert detect.get_codename() == 'bordeaux', detect.get_codename()
+
+ detect = Fedora()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def test_read_issue():
+ from rospkg.os_detect import read_issue
+ assert read_issue('/fake/file') is None
+ test_dir = os.path.join(get_test_dir(), 'rhel')
+ assert read_issue(os.path.join(test_dir, 'issue')) == \
+ ['Red', 'Hat', 'Enterprise', 'Linux', 'AS', 'release', '3', '(Taroon)']
+
+
+def test_OsDetector():
+ from rospkg.os_detect import OsDetector
+ d = OsDetector()
+ try:
+ d.is_os()
+ assert False
+ except NotImplementedError:
+ pass
+ try:
+ d.get_version()
+ assert False
+ except NotImplementedError:
+ pass
+ try:
+ d.get_codename()
+ assert False
+ except NotImplementedError:
+ pass
+
+
+def test_tripwire_uname_get_machine():
+ from rospkg.os_detect import uname_get_machine
+ retval = uname_get_machine()
+ assert retval in [None, 'aarch64', 'armv7l', 'i386', 'i686', 'ppc', 'ppc64', 'ppc64le', 's390', 's390x', 'x86_64']
+
+
+def test_tripwire_rhel():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('rhel')
+
+
+def test_redhat():
+ from rospkg.os_detect import Rhel, OsNotDetected
+ test_dir = os.path.join(get_test_dir(), 'rhel')
+
+ # go through several test files
+ detect = Rhel(os.path.join(test_dir, "redhat-release"))
+ assert detect.is_os()
+ assert detect.get_version() == '3'
+ assert detect.get_codename() == 'taroon'
+
+ detect = Rhel(os.path.join(test_dir, "redhat-release-tikanga"))
+ assert detect.is_os()
+ assert detect.get_version() == '5'
+ assert detect.get_codename() == 'tikanga'
+
+ detect = Rhel(os.path.join(test_dir, "redhat-release-nahant"))
+ assert detect.is_os()
+ assert detect.get_version() == '4'
+ assert detect.get_codename() == 'nahant'
+
+ # test freely
+ detect = Rhel()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def test_tripwire_slackware():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('slackware')
+
+
+def test_slackware():
+ from rospkg.os_detect import Slackware, OsNotDetected
+ test_dir = os.path.join(get_test_dir(), 'slackware')
+ detect = Slackware(os.path.join(test_dir, "slackware-version"))
+ assert detect.is_os()
+ assert detect.get_version() == '14.2'
+ assert detect.get_codename() == ''
+
+ # test freely
+ detect = Slackware()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def test_tripwire_freebsd():
+ from rospkg.os_detect import OsDetect
+ os_detect = OsDetect()
+ os_detect.get_detector('freebsd')
+
+
+def test_freebsd():
+ from rospkg.os_detect import FreeBSD, OsNotDetected
+ # TODO
+ if 0:
+ test_dir = os.path.join(get_test_dir(), 'freebsd')
+ release_file, issue_file = [os.path.join(test_dir, x) for
+ x in ["redhat-release", "issue"]]
+ detect = FreeBSD(release_file, issue_file)
+ assert detect.is_os()
+ assert detect.get_version() == '3'
+
+ # test freely
+ detect = FreeBSD()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+
+ # assure failure
+ detect = FreeBSD("/fake/uname/file")
+ assert not detect.is_os()
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+ @patch.object(FreeBSD, 'is_os')
+ def test(mock):
+ mock.is_os.return_value = True
+ detect = FreeBSD()
+ assert detect.get_codename() == ''
+ test()
+
+
+def test_cygwin():
+ from rospkg.os_detect import Cygwin, OsNotDetected
+ # TODO
+ detect = Cygwin()
+ if not detect.is_os():
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+
+ try:
+ detect.get_codename()
+ assert False
+ except OsNotDetected:
+ pass
+
+ @patch.object(Cygwin, 'is_os')
+ def test(mock):
+ mock.is_os.return_value = True
+ detect = Cygwin()
+ assert detect.get_codename() == ''
+ test()
+
+
+def test_OsDetect():
+ from rospkg.os_detect import OsDetect
+ detect = OsDetect()
+ try:
+ detect.get_detector('fake')
+ assert False, "should raise"
+ except KeyError:
+ pass
+
+
+def test_OsDetect_ROS_OVERRIDE():
+ from rospkg.os_detect import OsDetect
+ detect = OsDetect([('TrueOs', TrueOs())])
+ env = {'ROS_OS_OVERRIDE': 'arch'}
+ assert detect.detect_os(env=env) == ('arch', '', ''), \
+ detect.detect_os(env=env)
+ env = {'ROS_OS_OVERRIDE': 'fubuntu:04.10'}
+ assert detect.detect_os(env=env) == ('fubuntu', '04.10', '')
+ env = {'ROS_OS_OVERRIDE': 'fubuntu:04.10:opaque'}
+ assert detect.detect_os(env=env) == ('fubuntu', '04.10', 'opaque')
+
+
+def test_OsDetect_single():
+ # test each method twice with new instance b/c of caching
+ from rospkg.os_detect import OsDetect
+ detect = OsDetect([('TrueOs', TrueOs())])
+ assert "TrueOs" == detect.get_name()
+ assert "TrueOs" == detect.get_name()
+ detect = OsDetect([('TrueOs', TrueOs())])
+ assert "os_version" == detect.get_version()
+ assert "os_version" == detect.get_version()
+ detect = OsDetect([('TrueOs', TrueOs())])
+ assert "os_codename" == detect.get_codename()
+ assert "os_codename" == detect.get_codename()
+
+ detect = OsDetect([('TrueOs', TrueOs())])
+ assert isinstance(detect.get_detector(), TrueOs)
+ assert isinstance(detect.get_detector('TrueOs'), TrueOs)
+
+
+def test_OsDetect_register_default_add_detector():
+ # test behavior of register_default and add_detector. Both take
+ # precedence over previous detectors, but at different scopes.
+ from rospkg.os_detect import OsDetect
+ o1 = TrueOs()
+ o2 = TrueOs2()
+ key = 'TrueOs'
+ detect = OsDetect([(key, o1)])
+
+ assert detect.get_detector(key) == o1
+ detect.register_default(key, o2)
+ assert detect.get_detector(key) == o1
+ detect.add_detector(key, o2)
+ assert detect.get_detector(key) == o2
+
+ detect = OsDetect()
+ assert detect.get_detector(key) == o2
+ detect.add_detector(key, o1)
+ assert detect.get_detector(key) == o1
+
+ # restore precendence of o1 in default list
+ detect.register_default(key, o1)
+ detect = OsDetect()
+ assert detect.get_detector(key) == o1
+
+
+def test_OsDetect_nomatch():
+ from rospkg.os_detect import OsDetect, OsNotDetected
+ detect = OsDetect([('Dummy', FalseOs())])
+ assert isinstance(detect.get_detector('Dummy'), FalseOs)
+ try:
+ detect.get_name()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_version()
+ assert False
+ except OsNotDetected:
+ pass
+ try:
+ detect.get_detector()
+ assert False
+ except OsNotDetected:
+ pass
+
+
+def xTrueOsDetect_first_of_two():
+ osa = roslib.os_detect.OSDetect([TrueOs(), FalseOs()])
+ assert "os_name" == osa.get_name()
+ assert "os_version" == osa.get_version()
+ os_class = osa.get_os()
+ assert "os_name" == os_class.get_name()
+ assert "os_version" == os_class.get_version()
+
+
+def xTrueOsDetect_second_of_two():
+ osa = roslib.os_detect.OSDetect([FalseOs(), TrueOs()])
+ assert "os_name", osa.get_name()
+ assert "os_version", osa.get_version()
+ os_class = osa.get_os()
+ assert "os_name" == os_class.get_name()
+ assert "os_version" == os_class.get_version()
+
+
+def xTrueOsDetect_first_of_many():
+ osa = roslib.os_detect.OSDetect([TrueOs(), FalseOs(), FalseOs(), FalseOs(), FalseOs()])
+ assert "os_name" == osa.get_name()
+ assert "os_version" == osa.get_version()
+ os_class = osa.get_os()
+ assert "os_name" == os_class.get_name()
+ assert "os_version" == os_class.get_version()
+
+
+def xTrueOsDetect_second_of_many():
+ osa = roslib.os_detect.OSDetect([FalseOs(), TrueOs(), FalseOs(), FalseOs(), FalseOs()])
+ assert "os_name" == osa.get_name()
+ assert "os_version" == osa.get_version()
+ os_class = osa.get_os()
+ assert "os_name" == os_class.get_name()
+ assert "os_version" == os_class.get_version()
+
+
+def xTrueOsDetect_last_of_many():
+ osa = roslib.os_detect.OSDetect([FalseOs(), FalseOs(), FalseOs(), FalseOs(), TrueOs()])
+ assert "os_name", osa.get_name()
+ assert "os_version", osa.get_version()
+ os_class = osa.get_os()
+ assert "os_name" == os_class.get_name()
+ assert "os_version" == os_class.get_version()
+
+
+def xtest_ubuntu_in_OSA():
+ ubuntu = roslib.os_detect.Ubuntu()
+
+ def return_true():
+ return True
+ ubuntu.check_presence = return_true
+ osa = roslib.os_detect.OSDetect([ubuntu])
+ assert "ubuntu" == ubuntu.get_name()
+ os_class = osa.get_os()
+ assert "ubuntu" == os_class.get_name()
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+from __future__ import print_function
+
+import os
+import subprocess
+import tempfile
+
+
+def get_package_test_path():
+ return os.path.abspath(os.path.join(os.path.dirname(__file__), 'package_tests'))
+
+
+def test_ManifestManager_constructor():
+ from rospkg import RosPack, RosStack, get_ros_paths
+
+ r = RosPack()
+ assert r._manifest_name == 'manifest.xml'
+ r = RosStack()
+ assert r._manifest_name == 'stack.xml'
+ for c in [RosPack, RosStack]:
+ r = c()
+ assert r.ros_paths == get_ros_paths()
+
+ tmp = tempfile.gettempdir()
+
+ r = c(ros_paths=[tmp])
+ assert r.ros_paths == [tmp]
+ # make sure we can't accidentally mutate the actual data
+ r.ros_paths.append('foo')
+ assert r.ros_paths == [tmp]
+
+
+def test_ManifestManager_get_instance():
+ from rospkg import RosPack, RosStack, get_ros_paths
+
+ for c in [RosPack, RosStack]:
+ # make sure we get the same instance for defaults ros_paths
+ r1 = c.get_instance()
+ assert r1.ros_paths == get_ros_paths()
+ r2 = c.get_instance()
+ assert r1 is r2
+
+ # make sure we get the same instance for identical custom ros_paths
+ tmp = tempfile.gettempdir()
+ r3 = c.get_instance(ros_paths=[tmp])
+ assert r3.ros_paths == [tmp]
+ r4 = c.get_instance(ros_paths=[tmp])
+ assert r3 is r4
+
+ # make sure for different ros_paths we got different instances
+ assert r1 is not r3
+
+
+def rospackexec(args):
+ rospack_bin = 'rospack'
+ val = (subprocess.Popen([rospack_bin] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] or '').strip()
+ if val.startswith('rospack:'): # rospack error message
+ raise Exception(val)
+ return val
+
+
+# for comparing against 'ground truth'
+def rospack_list():
+ return [s.strip() for s in rospackexec(['list-names']).split('\n') if s.strip()]
+
+
+def rospack_find(package):
+ return rospackexec(['find', package]).strip()
+
+
+def rospack_depends(package):
+ return unicode(rospackexec(['depends', package])).split()
+
+
+def rospack_depends1(package):
+ return unicode(rospackexec(['depends1', package])).split()
+
+
+def rospack_depends_on(package):
+ return unicode(rospackexec(['depends-on', package])).split()
+
+
+def rospack_depends_on1(package):
+ return unicode(rospackexec(['depends-on1', package])).split()
+
+
+def delete_cache():
+ from rospkg import get_ros_home
+ p = os.path.join(get_ros_home(), 'rospack_cache')
+ if os.path.exists(p):
+ os.remove(p)
+
+
+def rospack_is_available():
+ try:
+ rospackexec(['-h'])
+ return True
+ except:
+ return False
+
+
+def test_RosPack_list():
+ from rospkg import RosPack, get_ros_root
+ if get_ros_root() is not None and rospack_is_available():
+ r = RosPack()
+
+ pkgs = rospack_list()
+ retval = r.list()
+ assert set(pkgs) == set(retval), "%s vs %s" % (pkgs, retval)
+
+ # test twice for caching
+ retval = r.list()
+ assert set(pkgs) == set(retval), "%s vs %s" % (pkgs, retval)
+
+ # make sure stress test works with rospack_cache invalidated
+ delete_cache()
+ r = RosPack()
+ retval = r.list()
+ assert set(pkgs) == set(retval), "%s vs %s" % (pkgs, retval)
+
+
+def test_RosPack_no_env():
+ # regression test for #3680
+ from rospkg import RosPack, ResourceNotFound
+ try:
+ environ_copy = os.environ.copy()
+ if 'ROS_ROOT' in os.environ:
+ del os.environ['ROS_ROOT']
+ if 'ROS_PACKAGE_PATH' in os.environ:
+ del os.environ['ROS_PACKAGE_PATH']
+ r = RosPack()
+ try:
+ r.get_depends('roscpp')
+ assert False, "should have raised"
+ except ResourceNotFound:
+ pass
+ finally:
+ os.environ.clear()
+ os.environ.update(environ_copy)
+
+
+def test_RosPack_get_path():
+ from rospkg import RosPack, ResourceNotFound, get_ros_root
+
+ path = get_package_test_path()
+ foo_path = os.path.join(path, 'p1', 'foo')
+ foo_path_alt = os.path.join(path, 'p2', 'foo')
+ bar_path = os.path.join(path, 'p1', 'bar')
+ baz_path = os.path.join(path, 'p2', 'baz')
+
+ # point ROS_ROOT at top, should spider entire tree
+ print("ROS path: %s" % (path))
+ r = RosPack(ros_paths=[path])
+ # precedence in this case is undefined as there are two 'foo's in the same path
+ assert r.get_path('foo') in [foo_path, foo_path_alt]
+ assert bar_path == r.get_path('bar')
+ assert baz_path == r.get_path('baz')
+ try:
+ r.get_path('fake')
+ assert False
+ except ResourceNotFound:
+ pass
+
+ # divide tree in half to test precedence
+ print("ROS_PATH 1: %s" % (os.path.join(path, 'p1')))
+ print("ROS_PATH 2: %s" % (os.path.join(path, 'p2')))
+ r = RosPack(ros_paths=[os.path.join(path, 'p1'), os.path.join(path, 'p2')])
+ assert foo_path == r.get_path('foo'), "%s vs. %s" % (foo_path, r.get_path('foo'))
+ assert bar_path == r.get_path('bar')
+ assert baz_path == r.get_path('baz')
+
+ if get_ros_root() and rospack_is_available():
+ # stresstest against rospack
+ r = RosPack()
+ for p in rospack_list():
+ retval = r.get_path(p)
+ rospackval = rospack_find(p)
+ assert retval == rospackval, "[%s]: %s vs. %s" % (p, retval, rospackval)
+
+
+def test_RosPackage_get_depends():
+ from rospkg import RosPack, ResourceNotFound, get_ros_root
+ path = get_package_test_path()
+ r = RosPack(ros_paths=[path])
+
+ # test on multiple calls to bad package -- there was an ordering
+ # issue in the logic that caused get_depends() to return an empty
+ # set on the second call.
+ for i in range(1, 4):
+ try:
+ r.get_depends('bad', implicit=True)
+ assert False, "should have raised"
+ except ResourceNotFound:
+ pass
+
+ # TODO: need one more step
+ assert set(r.get_depends('baz')) == set(['foo', 'bar'])
+ assert r.get_depends('bar') == ['foo']
+ assert r.get_depends('foo') == []
+
+ if get_ros_root() and rospack_is_available():
+ # stress test: test default environment against rospack
+ r = RosPack()
+ for p in rospack_list():
+ retval = set(r.get_depends(p))
+ rospackval = set(rospack_depends(p))
+ assert retval == rospackval, "[%s]: %s vs. %s" % (p, retval, rospackval)
+
+
+def get_stack_test_path():
+ return os.path.abspath(os.path.join(os.path.dirname(__file__), 'stack_tests'))
+
+
+def test_stack_of():
+ from rospkg import RosPack, ResourceNotFound
+ path = os.path.join(get_stack_test_path(), 's1')
+ r = RosPack(ros_paths=[path])
+
+ # test with actual stacks
+ assert r.stack_of('foo_pkg') == 'foo'
+ assert r.stack_of('foo_pkg_2') == 'foo'
+ assert r.stack_of('bar_pkg') == 'bar'
+
+ try:
+ r.stack_of('fake')
+ assert False, "should have raised ResourceNotFound"
+ except ResourceNotFound:
+ pass
+
+ path = os.path.join(get_package_test_path(), 'p1')
+ r = RosPack(ros_paths=[path])
+
+ # test with actual not stacked-packages
+ assert r.stack_of('foo') is None
+
+
+def test_RosPackage_get_depends_explicit():
+ from rospkg import RosPack, get_ros_root
+ path = get_package_test_path()
+ r = RosPack(ros_paths=[path])
+
+ implicit = False
+ assert set(r.get_depends('baz', implicit)) == set(['bar', 'foo'])
+ assert r.get_depends('bar', implicit) == ['foo']
+ assert r.get_depends('foo', implicit) == []
+
+ if get_ros_root() and rospack_is_available():
+ # stress test: test default environment against rospack
+ r = RosPack()
+ for p in rospack_list():
+ retval = set(r.get_depends(p, implicit))
+ rospackval = set(rospack_depends1(p))
+ assert retval == rospackval, "[%s]: %s vs. %s" % (p, retval, rospackval)
+
+
+def test_RosPack_get_rosdeps():
+ from rospkg import RosPack
+
+ path = get_package_test_path()
+ r = RosPack(ros_paths=[os.path.join(path, 'p1'), os.path.join(path, 'p2')])
+
+ # repeat tests due to caching
+ assert set(['foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3']) == set(r.get_rosdeps('foo', implicit=True)), r.get_rosdeps('foo', implicit=True)
+ assert set(['foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3']) == set(r.get_rosdeps('foo', implicit=True))
+ assert set(['foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3']) == set(r.get_rosdeps('foo', implicit=False))
+
+ assert set(['bar_rosdep1', 'bar_rosdep2']) == set(r.get_rosdeps('bar', implicit=False))
+ assert set(['foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3', 'bar_rosdep1', 'bar_rosdep2']) == set(r.get_rosdeps('bar', implicit=True))
+ assert set(['foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3', 'bar_rosdep1', 'bar_rosdep2']) == set(r.get_rosdeps('bar', implicit=True))
+ assert set(['foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3', 'bar_rosdep1', 'bar_rosdep2']) == set(r.get_rosdeps('bar'))
+
+ assert ['baz_rosdep1'] == r.get_rosdeps('baz', implicit=False)
+ assert set(['baz_rosdep1', 'foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3', 'bar_rosdep1', 'bar_rosdep2']) == set(r.get_rosdeps('baz'))
+ assert set(['baz_rosdep1', 'foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3', 'bar_rosdep1', 'bar_rosdep2']) == set(r.get_rosdeps('baz'))
+
+ # create a brand new instance to test with brand new cache
+ r = RosPack(ros_paths=[os.path.join(path, 'p1'), os.path.join(path, 'p2')])
+ assert set(['baz_rosdep1', 'foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3', 'bar_rosdep1', 'bar_rosdep2']) == set(r.get_rosdeps('baz'))
+ assert set(['baz_rosdep1', 'foo_rosdep1', 'foo_rosdep2', 'foo_rosdep3', 'bar_rosdep1', 'bar_rosdep2']) == set(r.get_rosdeps('baz'))
+
+
+def test_get_package_name():
+ from rospkg import __version__
+ from rospkg import get_package_name
+
+ # test dir is a subdirectory of this package
+ test_dir = get_package_test_path()
+ assert get_package_name(test_dir) in ['rospkg', 'rospkg-%s' % __version__], get_package_name(test_dir)
+
+ test_dir_foo = os.path.join(test_dir, 'p1', 'foo')
+ assert 'foo' == get_package_name(test_dir_foo)
+
+ # test with path outside of our hierarchy
+ assert get_package_name(tempfile.tempdir) is None
+
+
+def test_get_depends_on():
+ from rospkg import RosPack, get_ros_root
+ test_dir = get_package_test_path()
+ rp = RosPack(ros_paths=[test_dir])
+ # test direct depends
+ val = rp.get_depends_on('foo', implicit=False)
+ assert set(['bar', 'baz']) == set(val), val
+ val = rp.get_depends_on('bar', implicit=False)
+ assert ['baz'] == val, val
+ val = rp.get_depends_on('baz', implicit=False)
+ assert [] == val, val
+
+ # test implicit depends
+ val = rp.get_depends_on('foo', implicit=True)
+ assert set(['bar', 'baz']) == set(val), val
+ val = rp.get_depends_on('bar', implicit=True)
+ assert ['baz'] == val, val
+ val = rp.get_depends_on('baz', implicit=True)
+ assert [] == val, val
+
+ if get_ros_root() and rospack_is_available():
+ # stress test: test default environment against rospack
+ r = RosPack()
+ for p in rospack_list():
+ retval = set(r.get_depends_on(p, False))
+ rospackval = set(rospack_depends_on1(p))
+ assert retval == rospackval, "[%s]: %s vs. %s" % (p, retval, rospackval)
+ for p in rospack_list():
+ retval = set(r.get_depends_on(p, True))
+ rospackval = set(rospack_depends_on(p))
+ assert retval == rospackval, "[%s]: %s vs. %s" % (p, retval, rospackval)
--- /dev/null
+# Software License Agreement (BSD License)
+#
+# 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 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.
+
+from __future__ import print_function
+
+import os
+
+import subprocess
+
+
+def get_stack_test_path():
+ return os.path.abspath(os.path.join(os.path.dirname(__file__), 'stack_tests'))
+
+
+def get_unary_test_path():
+ return os.path.abspath(os.path.join(os.path.dirname(__file__), 'unary_tests'))
+
+
+def rosstackexec(args):
+ rosstack_bin = 'rosstack'
+ val = subprocess.Popen([rosstack_bin] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=os.environ).communicate()
+ val = val[0].strip()
+ if val.startswith('rosstack:'): # rosstack error message
+ raise Exception(val)
+ return val
+
+
+def rosstack_is_available():
+ try:
+ rosstackexec(['-h'])
+ return True
+ except:
+ return False
+
+
+# for comparing against 'ground truth'
+def rosstack_list():
+ return [s.strip() for s in rosstackexec(['list-names']).split('\n') if s.strip()]
+
+
+def rosstack_find(package):
+ return rosstackexec(['find', package]).strip()
+
+
+def rosstack_depends(package):
+ return unicode(rosstackexec(['depends', package])).split()
+
+
+def rosstack_depends1(package):
+ return unicode(rosstackexec(['depends1', package])).split()
+
+
+def delete_cache():
+ from rospkg import get_ros_home
+ p = os.path.join(get_ros_home(), 'rosstack_cache')
+ if os.path.exists(p):
+ os.remove(p)
+
+
+def test_RosStack_list():
+ from rospkg import get_ros_paths, RosStack
+
+ print("ROS paths", get_ros_paths())
+ if get_ros_paths() is not None and rosstack_is_available():
+ r = RosStack()
+
+ l = rosstack_list()
+ retval = r.list()
+ assert set(l) == set(retval), "%s vs %s" % (l, retval)
+
+ # test twice for caching
+ retval = r.list()
+ assert set(l) == set(retval), "%s vs %s" % (l, retval)
+
+ # make sure stress test works with rospack_cache invalidated
+ delete_cache()
+ r = RosStack()
+ retval = r.list()
+ assert set(l) == set(retval), "%s vs %s" % (l, retval)
+
+
+def test_RosStack_get_path():
+ from rospkg import RosStack, ResourceNotFound, get_ros_paths
+
+ path = get_stack_test_path()
+ bar_path = os.path.join(path, 's1', 'bar')
+ baz_path = os.path.join(path, 's2', 'baz')
+
+ # point ROS_ROOT at top, should spider entire tree
+ print("ROS_PATHS: %s" % str([path]))
+ print("ROS_PACKAGE_PATH: ")
+ r = RosStack(ros_paths=[path])
+ assert bar_path == r.get_path('bar'), "%s vs. %s" % (bar_path, r.get_path('bar'))
+ try:
+ r.get_path('fake')
+ assert False
+ except ResourceNotFound:
+ pass
+
+ # divide tree in half to test precedence
+ print("ROS PATH 1: %s" % (os.path.join(path, 'p1')))
+ print("ROS PATH 2: %s" % (os.path.join(path, 'p2')))
+ foo_path = os.path.join(path, 's1', 'foo')
+ r = RosStack(ros_paths=[os.path.join(path, 's1'), os.path.join(path, 's2')])
+ assert foo_path == r.get_path('foo'), "%s vs. %s" % (foo_path, r.get_path('foo'))
+ assert bar_path == r.get_path('bar')
+ assert baz_path == r.get_path('baz')
+
+ # divide tree in half again and test precedence of ROS_PACKAGE_PATH (foo should switch)
+ print("ROS_ROOT: %s" % (os.path.join(path, 'p1')))
+ print("ROS_PACKAGE_PATH: %s" % (os.path.join(path, 'p2')))
+ foo_path = os.path.join(path, 's2', 'foo')
+ ros_paths = [os.path.join(path, 'notapath'), os.path.join(path, 's2'), os.path.join(path, 's1')]
+ r = RosStack(ros_paths=ros_paths)
+ assert foo_path == r.get_path('foo'), "%s vs. %s" % (foo_path, r.get_path('foo'))
+
+ if get_ros_paths() and rosstack_is_available():
+ # stresstest against rospack
+ r = RosStack()
+ listval = rosstack_list()
+ for p in listval:
+ retval = r.get_path(p)
+ rospackval = rosstack_find(p)
+ assert retval == rospackval, "[%s]: %s vs. %s" % (p, retval, rospackval)
+
+ # stresstest with cache invalidated
+ delete_cache()
+ r = RosStack()
+ for p in listval:
+ retval = r.get_path(p)
+ rospackval = rosstack_find(p)
+ assert retval == rospackval, "[%s]: %s vs. %s" % (p, retval, rospackval)
+
+
+def test_RosStack_get_depends():
+ from rospkg import get_ros_paths, RosStack
+ path = get_stack_test_path()
+ s1 = os.path.join(path, 's1')
+ s3 = os.path.join(path, 's3')
+ r = RosStack(ros_paths=[s1, s3])
+
+ # TODO: need one more step
+ assert set(r.get_depends('baz')) == set(['foo', 'bar'])
+ assert r.get_depends('bar') == ['foo']
+ assert r.get_depends('foo') == []
+
+ if get_ros_paths() and rosstack_is_available():
+ # stress test: test default environment against rosstack
+ r = RosStack()
+ for p in rosstack_list():
+ retval = set(r.get_depends(p))
+ rospackval = set(rosstack_depends(p))
+ assert retval == rospackval, "[%s]: %s vs. %s" % (p, retval, rospackval)
+
+
+def test_RosStack_get_depends_explicit():
+ from rospkg import get_ros_paths, RosStack
+ path = get_stack_test_path()
+ s1 = os.path.join(path, 's1')
+ s3 = os.path.join(path, 's3')
+ r = RosStack(ros_paths=[s1, s3])
+
+ implicit = False
+ assert set(r.get_depends('baz', implicit)) == set(['bar', 'foo'])
+ assert r.get_depends('bar', implicit) == ['foo']
+ assert r.get_depends('foo', implicit) == []
+
+ # stress test: test default environment against rospack
+ if get_ros_paths() and rosstack_is_available():
+ r = RosStack()
+ for p in rosstack_list():
+ retval = set(r.get_depends(p, implicit))
+ rospackval = set(rosstack_depends1(p))
+ assert retval == rospackval, "[%s]: %s vs. %s" % (p, retval, rospackval)
+
+
+def test_expand_to_packages():
+ from rospkg import expand_to_packages, RosPack, RosStack
+ path = os.path.join(get_stack_test_path(), 's1')
+ rospack = RosPack(ros_paths=[path])
+ rosstack = RosStack(ros_paths=[path])
+
+ try:
+ expand_to_packages('foo', rospack, rosstack)
+ assert False, "should have raised ValueError"
+ except ValueError:
+ pass
+
+ valid, invalid = expand_to_packages(['foo'], rospack, rosstack)
+ assert set(valid) == set(['foo_pkg', 'foo_pkg_2'])
+ assert not invalid
+
+ valid, invalid = expand_to_packages(['foo_pkg'], rospack, rosstack)
+ assert set(valid) == set(['foo_pkg'])
+ assert not invalid
+
+ valid, invalid = expand_to_packages(['foo', 'foo_pkg'], rospack, rosstack)
+ assert set(valid) == set(['foo_pkg', 'foo_pkg_2'])
+ assert not invalid
+
+ valid, invalid = expand_to_packages(['foo', 'bar'], rospack, rosstack)
+ assert set(valid) == set(['foo_pkg', 'foo_pkg_2', 'bar_pkg'])
+ assert not invalid
+
+ valid, invalid = expand_to_packages(['foo', 'bar_pkg'], rospack, rosstack)
+ assert set(valid) == set(['foo_pkg', 'foo_pkg_2', 'bar_pkg'])
+ assert not invalid
+
+ valid, invalid = expand_to_packages(['foo', 'bar_pkg', 'bar'], rospack, rosstack)
+ assert set(valid) == set(['foo_pkg', 'foo_pkg_2', 'bar_pkg'])
+ assert not invalid
+
+ valid, invalid = expand_to_packages(['foo', 'fake1', 'bar_pkg', 'bar', 'fake2'], rospack, rosstack)
+ assert set(valid) == set(['foo_pkg', 'foo_pkg_2', 'bar_pkg'])
+ assert set(invalid) == set(['fake1', 'fake2'])
+
+
+def test_get_stack_version():
+ from rospkg import get_stack_version_by_dir, RosStack
+ path = os.path.join(get_stack_test_path(), 's1')
+ r = RosStack(ros_paths=[path])
+
+ # test by dir option directly
+ foo_dir = r.get_path('foo')
+ assert get_stack_version_by_dir(foo_dir) == '1.6.0-manifest'
+ bar_dir = r.get_path('bar')
+ assert get_stack_version_by_dir(bar_dir) == '1.5.0-cmake'
+
+ # test via rosstack
+ assert r.get_stack_version('foo') == '1.6.0-manifest'
+ assert r.get_stack_version('bar') == '1.5.0-cmake'
+
+ path = os.path.join(get_stack_test_path(), 's2')
+ r = RosStack(ros_paths=[path])
+ foo_dir = r.get_path('foo')
+ assert get_stack_version_by_dir(foo_dir) is None, get_stack_version_by_dir(foo_dir)
+
+ # test reading from stack.yaml
+ baz_dir = r.get_path('baz')
+ assert get_stack_version_by_dir(baz_dir) == '1-manifest', get_stack_version_by_dir(baz_dir)
+ assert r.get_stack_version('baz') == '1-manifest'
+
+
+def test_get_cmake_version():
+ from rospkg.rospack import _get_cmake_version
+
+ assert '1.6.0' == _get_cmake_version("rosbuild_make_distribution(1.6.0)")
+ try:
+ _get_cmake_version("rosbuild_make_distribution")
+ assert False, "should have raised ValueError"
+ except ValueError:
+ pass
+
+
+def test_unary():
+ from rospkg import RosStack, RosPack
+ path = get_unary_test_path()
+ rospack = RosPack(ros_paths=[path])
+ rosstack = RosStack(ros_paths=[path])
+ assert rospack.get_path('unary') == rosstack.get_path('unary')
+
+ assert rosstack.packages_of('unary') == ['unary']
+ assert rospack.stack_of('unary') == 'unary'