Imported Upstream version 0.10.33

parent 9daaca33
......@@ -11,3 +11,4 @@ build
deb_dist
dist
src/rosdep.egg-info
nose*
......@@ -2,6 +2,7 @@ language: python
python:
- "2.6"
- "2.7"
- "3.2"
- "3.3"
# command to install dependencies
install:
......
__version__ = '0.10.27'
__version__ = '0.10.33'
......@@ -68,6 +68,12 @@ class RosdepInternalError(Exception):
def __str__(self):
return self.message
class CachePermissionError(Exception):
"""Failure when writing the cache."""
pass
class DownloadFailure(Exception):
"""
......
......@@ -36,11 +36,6 @@ from rospkg.os_detect import OsDetect
from .core import rd_debug, RosdepInternalError, InstallFailed, print_bold, InvalidData
# use OsDetect.get_version() for OS version key
TYPE_VERSION = 'version'
# use OsDetect.get_codename() for OS version key
TYPE_CODENAME = 'codename'
# kwc: InstallerContext is basically just a bunch of dictionaries with
# defined lookup methods. It really encompasses two facets of a
# rosdep configuration: the pluggable nature of installers and
......@@ -95,11 +90,11 @@ class InstallerContext(object):
self.os_override = os_name, os_version
def get_os_version_type(self, os_name):
return self.os_version_type.get(os_name, TYPE_VERSION)
return self.os_version_type.get(os_name, OsDetect.get_version)
def set_os_version_type(self, os_name, version_type):
if version_type not in (TYPE_VERSION, TYPE_CODENAME):
raise ValueError("version type not TYPE_VERSION or TYPE_CODENAME")
if not hasattr(version_type, '__call__'):
raise ValueError("version type should be a method")
self.os_version_type[os_name] = version_type
def get_os_name_and_version(self):
......@@ -115,10 +110,8 @@ class InstallerContext(object):
return self.os_override
else:
os_name = self.os_detect.get_name()
if self.get_os_version_type(os_name) == TYPE_CODENAME:
os_version = self.os_detect.get_codename()
else:
os_version = self.os_detect.get_version()
os_key = self.get_os_version_type(os_name)
os_version = os_key(self.os_detect)
return os_name, os_version
def get_os_detect(self):
......@@ -260,14 +253,14 @@ class Installer(object):
"""
raise NotImplementedError("is_installed", resolved_item)
def get_install_command(self, resolved, interactive=True, reinstall=False):
def get_install_command(self, resolved, interactive=True, reinstall=False, quiet=False):
"""
:param resolved: list of resolved installation items, ``[opaque]``
:param interactive: If `False`, disable interactive prompts,
e.g. Pass through ``-y`` or equivalant to package manager.
:param reinstall: If `True`, install everything even if already installed
"""
raise NotImplementedError("get_package_install_command", resolved, interactive, reinstall)
raise NotImplementedError("get_package_install_command", resolved, interactive, reinstall, quiet)
def get_depends(self, rosdep_args):
"""
......@@ -320,6 +313,17 @@ class PackageManagerInstaller(Installer):
"""
self.detect_fn = detect_fn
self.supports_depends = supports_depends
self.as_root = True
self.sudo_command = 'sudo'
def elevate_priv(self, cmd):
"""
Prepend *self.sudo_command* to the command if *self.as_root* is ``True``.
:param list cmd: list of strings comprising the command
:returns: a list of commands
"""
return (self.sudo_command.split() if self.as_root else []) + cmd
def resolve(self, rosdep_args):
"""
......@@ -358,8 +362,8 @@ class PackageManagerInstaller(Installer):
def is_installed(self, resolved_item):
return not self.get_packages_to_install([resolved_item])
def get_install_command(self, resolved, interactive=True, reinstall=False):
raise NotImplementedError('subclasses must implement', resolved, interactive, reinstall)
def get_install_command(self, resolved, interactive=True, reinstall=False, quiet=False):
raise NotImplementedError('subclasses must implement', resolved, interactive, reinstall, quiet)
def get_depends(self, rosdep_args):
"""
......@@ -426,7 +430,7 @@ class RosdepInstaller(object):
return uninstalled, errors
def install(self, uninstalled, interactive=True, simulate=False,
continue_on_error=False, reinstall=False, verbose=False):
continue_on_error=False, reinstall=False, verbose=False, quiet=False):
"""
Install the uninstalled rosdeps. This API is for the bulk
workflow of rosdep (see example below). For a more targeted
......@@ -474,7 +478,7 @@ class RosdepInstaller(object):
try:
self.install_resolved(installer_key, resolved, simulate=simulate,
interactive=interactive, reinstall=reinstall, continue_on_error=continue_on_error,
verbose=verbose)
verbose=verbose, quiet=quiet)
except InstallFailed as e:
if not continue_on_error:
raise
......@@ -485,7 +489,7 @@ class RosdepInstaller(object):
raise InstallFailed(failures=failures)
def install_resolved(self, installer_key, resolved, simulate=False, interactive=True,
reinstall=False, continue_on_error=False, verbose=False):
reinstall=False, continue_on_error=False, verbose=False, quiet=False):
"""
Lower-level API for installing a rosdep dependency. The
rosdep keys have already been resolved to *installer_key* and
......@@ -498,12 +502,13 @@ class RosdepInstaller(object):
:param reinstall: If ``True``, install dependencies if even
already installed (default ``False``).
:param verbose: If ``True``, print verbose output to screen (default ``False``)
:param quiet: If ``True``, supress output except for errors (default ``False``)
:raises: :exc:`InstallFailed` if any of *resolved* fail to install.
"""
installer_context = self.installer_context
installer = installer_context.get_installer(installer_key)
command = installer.get_install_command(resolved, interactive=interactive, reinstall=reinstall)
command = installer.get_install_command(resolved, interactive=interactive, reinstall=reinstall, quiet=quiet)
if not command:
if verbose:
print("#No packages to install")
......
......@@ -39,7 +39,17 @@ import sys
import traceback
try:
from urllib.error import URLError
from urllib.request import build_opener
from urllib.request import HTTPBasicAuthHandler
from urllib.request import HTTPHandler
from urllib.request import install_opener
from urllib.request import ProxyHandler
except ImportError:
from urllib2 import build_opener
from urllib2 import HTTPBasicAuthHandler
from urllib2 import HTTPHandler
from urllib2 import install_opener
from urllib2 import ProxyHandler
from urllib2 import URLError
import warnings
......@@ -49,7 +59,7 @@ import rospkg
from . import create_default_installer_context, get_default_installer
from . import __version__
from .core import RosdepInternalError, InstallFailed, UnsupportedOs, InvalidData
from .core import RosdepInternalError, InstallFailed, UnsupportedOs, InvalidData, CachePermissionError
from .installers import RosdepInstaller
from .lookup import RosdepLookup, ResolutionError
from .rospkg_loader import DEFAULT_VIEW_KEY
......@@ -99,6 +109,11 @@ rosdep what-needs <rosdeps>...
rosdep where-defined <rosdeps>...
print a list of yaml files that declare a rosdep on (at least
one of) <rosdeps>
rosdep fix-permissions
Recursively change the permissions of the user's ros home directory.
May require sudo. Can be useful to fix permissions after calling
"rosdep update" with sudo accidentally.
"""
def _get_default_RosdepLookup(options):
......@@ -149,6 +164,10 @@ ERROR: %s
%s
"""%(e.args[0], e), file=sys.stderr)
sys.exit(1)
except CachePermissionError as e:
print(str(e))
print("Try running 'sudo rosdep fix-permissions'")
sys.exit(1)
except UnsupportedOs as e:
print("Unsupported OS: %s\nSupported OSes are [%s]"%(e.args[0], ', '.join(e.args[1])), file=sys.stderr)
sys.exit(1)
......@@ -197,7 +216,42 @@ ERROR: your rosdep installation has not been initialized yet. Please run:
sys.exit(1)
else:
return True
def key_list_to_dict(key_list):
"""
Convert a list of strings of the form 'foo:bar' to a dictionary.
Splits strings of the form 'foo:bar quux:quax' into separate entries.
"""
try:
key_list = [key for s in key_list for key in s.split(' ')]
return dict(map(lambda s: [t.strip() for t in s.split(':')], key_list))
except ValueError as e:
raise UsageError("Invalid 'key:value' list: '%s'" % ' '.join(key_list))
def str_to_bool(s):
"""Maps a string to bool. Supports true/false, and yes/no, and is case-insensitive"""
s = s.lower()
if s in ['yes', 'true']:
return True
elif s in ['no', 'false']:
return False
else:
raise UsageError("Cannot parse '%s' as boolean" % s)
def setup_proxy_opener():
# check for http[s]?_proxy user
for scheme in ['http', 'https']:
key = scheme + '_proxy'
if key in os.environ:
proxy = ProxyHandler({scheme: os.environ[key]})
auth = HTTPBasicAuthHandler()
opener = build_opener(proxy, auth, HTTPHandler)
install_opener(opener)
def _rosdep_main(args):
# sources cache dir is our local database.
default_sources_cache = get_sources_cache_dir()
......@@ -219,6 +273,8 @@ def _rosdep_main(args):
action="store_true", help="Simulate install")
parser.add_option("-r", dest="robust", default=False,
action="store_true", help="Continue installing despite errors.")
parser.add_option("-q", dest="quiet", default=False,
action="store_true", help="Suprress output except for errors")
parser.add_option("-a", "--all", dest="rosdep_all", default=False,
action="store_true", help="select all packages")
parser.add_option("-n", dest="recursive", default=True,
......@@ -250,6 +306,11 @@ def _rosdep_main(args):
help="Explicitly sets the ROS distro to use, overriding "
"the normal method of detecting the ROS distro "
"using the ROS_DISTRO environment variable.")
parser.add_option("--as-root", default=[], action='append',
metavar="INSTALLER_KEY:<bool>", help="Override "
"whether sudo is used for a specific installer, "
"e.g. '--as-root pip:false' or '--as-root \"pip:no homebrew:yes\"'. "
"Can be specified multiple times.")
options, args = parser.parse_args(args)
if options.print_version:
......@@ -269,8 +330,13 @@ def _rosdep_main(args):
if options.ros_distro:
os.environ['ROS_DISTRO'] = options.ros_distro
# Convert list of keys to dictionary
options.as_root = dict((k, str_to_bool(v)) for k, v in key_list_to_dict(options.as_root).items())
if not command in ['init', 'update']:
check_for_sources_list_init(options.sources_cache_dir)
else:
setup_proxy_opener()
if command in _command_rosdep_args:
return _rosdep_args_handler(command, parser, options, args)
elif command in _command_no_args:
......@@ -385,15 +451,23 @@ def convert_os_override_option(options_os_override):
os_version = val[val.find(':')+1:]
return os_name, os_version
def configure_installer_context_os(installer_context, options):
def configure_installer_context(installer_context, options):
"""
Override the OS detector in *installer_context* if necessary.
Configure the *installer_context* from *options*.
- Override the OS detector in *installer_context* if necessary.
- Set *as_root* for installers if specified.
:raises: :exc:`UsageError` If user input options incorrectly
"""
os_override = convert_os_override_option(options.os_override)
if os_override is not None:
installer_context.set_os_override(*os_override)
for k,v in options.as_root.items():
try:
installer_context.get_installer(k).as_root = v
except KeyError:
raise UsageError("Installer '%s' not defined." % k)
def command_init(options):
try:
......@@ -403,6 +477,7 @@ def command_init(options):
return 4
# reuse path variable for error message
path = get_sources_list_dir()
old_umask = os.umask(0o022)
try:
if not os.path.exists(path):
os.makedirs(path)
......@@ -420,6 +495,8 @@ def command_init(options):
except OSError as e:
print("ERROR: cannot create %s:\n\t%s\nPerhaps you need to run 'sudo rosdep init' instead"%(path, e), file=sys.stderr)
return 3
finally:
os.umask(old_umask)
def command_update(options):
error_occured = []
......@@ -448,7 +525,7 @@ def command_update(options):
try:
if os.geteuid() == 0:
print("Warning: running 'rosdep update' as root is not recommended.", file=sys.stderr)
print(" You should remove the rosdep database with 'sudo rm -fr %s' and invoke 'rosdep update' again without sudo." % sources_cache_dir, file=sys.stderr)
print(" You should run 'sudo rosdep fix-permissions' and invoke 'rosdep update' again without sudo.", file=sys.stderr)
except AttributeError:
# nothing we wanna do under Windows
pass
......@@ -486,7 +563,7 @@ def command_check(lookup, packages, options):
verbose = options.verbose
installer_context = create_default_installer_context(verbose=verbose)
configure_installer_context_os(installer_context, options)
configure_installer_context(installer_context, options)
installer = RosdepInstaller(installer_context, lookup)
uninstalled, errors = installer.get_uninstalled(packages, implicit=options.recursive, verbose=verbose)
......@@ -513,21 +590,21 @@ def command_check(lookup, packages, options):
def error_to_human_readable(error):
if isinstance(error, rospkg.ResourceNotFound):
return "Missing resource %s"%(str(error))
return "Missing resource %s"%(error,)
elif isinstance(error, ResolutionError):
return str(error.args[0])
return "%s"%(error.args[0],)
else:
return str(error)
return "%s"%(error,)
def command_install(lookup, packages, options):
# map options
install_options = dict(interactive=not options.default_yes, verbose=options.verbose,
reinstall=options.reinstall,
continue_on_error=options.robust, simulate=options.simulate)
continue_on_error=options.robust, simulate=options.simulate, quiet=options.quiet)
# setup installer
installer_context = create_default_installer_context(verbose=options.verbose)
configure_installer_context_os(installer_context, options)
configure_installer_context(installer_context, options)
installer = RosdepInstaller(installer_context, lookup)
if options.reinstall:
......@@ -582,7 +659,7 @@ def command_db(options):
# exact same setup logic as command_resolve, should possibly combine
lookup = _get_default_RosdepLookup(options)
installer_context = create_default_installer_context(verbose=options.verbose)
configure_installer_context_os(installer_context, options)
configure_installer_context(installer_context, options)
os_name, os_version = installer_context.get_os_name_and_version()
try:
installer_keys = installer_context.get_os_installer_keys(os_name)
......@@ -648,7 +725,7 @@ def command_where_defined(args, options):
def command_resolve(args, options):
lookup = _get_default_RosdepLookup(options)
installer_context = create_default_installer_context(verbose=options.verbose)
configure_installer_context_os(installer_context, options)
configure_installer_context(installer_context, options)
installer, installer_keys, default_key, \
os_name, os_version = get_default_installer(installer_context=installer_context,
......@@ -680,6 +757,46 @@ def command_resolve(args, options):
if invalid_key_errors:
return 1 # error exit code
def command_fix_permissions(options):
import os
import pwd
import grp
stat_info = os.stat(os.path.expanduser('~'))
uid = stat_info.st_uid
gid = stat_info.st_gid
user_name = pwd.getpwuid(uid).pw_name
group_name = grp.getgrgid(gid).gr_name
ros_home = rospkg.get_ros_home()
print("Recursively changing ownership of ros home directory '{0}' "
"to '{1}:{2}' (current user)...".format(ros_home, user_name, group_name))
failed = []
try:
for dirpath, dirnames, filenames in os.walk(ros_home):
try:
os.lchown(dirpath, uid, gid)
except Exception as e:
failed.append((dirpath, str(e)))
for f in filenames:
try:
path = os.path.join(dirpath, f)
os.lchown(path, uid, gid)
except Exception as e:
failed.append((path, str(e)))
except Exception:
import traceback
traceback.print_exc()
print("Failed to walk directory. Try with sudo?")
else:
if failed:
print("Failed to change ownership for:")
for p, e in failed:
print("{0} --> {1}".format(p, e))
print("Try with sudo?")
else:
print("Done.")
command_handlers = {
'db': command_db,
'check': command_check,
......@@ -690,6 +807,7 @@ command_handlers = {
'resolve': command_resolve,
'init': command_init,
'update': command_update,
'fix-permissions': command_fix_permissions,
# backwards compat
'what_needs': command_what_needs,
......@@ -700,7 +818,7 @@ command_handlers = {
# commands that accept rosdep names as args
_command_rosdep_args = ['what-needs', 'what_needs', 'where-defined', 'where_defined', 'resolve']
# commands that take no args
_command_no_args = ['update', 'init', 'db']
_command_no_args = ['update', 'init', 'db', 'fix-permissions']
_commands = command_handlers.keys()
......
......@@ -55,10 +55,10 @@ class PacmanInstaller(PackageManagerInstaller):
def __init__(self):
super(PacmanInstaller, self).__init__(pacman_detect)
def get_install_command(self, resolved, interactive=True, reinstall=False):
def get_install_command(self, resolved, interactive=True, reinstall=False, quiet=False):
#TODO: interactive switch
packages = self.get_packages_to_install(resolved, reinstall=reinstall)
if not packages:
return []
else:
return [['sudo', 'pacman', '-Sy', '--needed', p] for p in packages]
return [self.elevate_priv(['pacman', '-Sy', '--needed', p]) for p in packages]
......@@ -62,14 +62,16 @@ class AptCygInstaller(PackageManagerInstaller):
def __init__(self):
super(AptCygInstaller, self).__init__(cygcheck_detect)
self.as_root = False
self.sudo_command = 'cygstart --action=runas'
def get_install_command(self, resolved, interactive=True, reinstall=False):
def get_install_command(self, resolved, interactive=True, reinstall=False, quiet=False):
packages = self.get_packages_to_install(resolved, reinstall=reinstall)
#TODO: interactive
if not packages:
return []
else:
return [['apt-cyg', '-m', 'ftp://sourceware.org/pub/cygwinports', 'install']+packages]
return [self.elevate_priv(['apt-cyg', '-m', 'ftp://sourceware.org/pub/cygwinports', 'install'])+packages]
if __name__ == '__main__':
print("test cygcheck_detect(true)", cygcheck_detect('cygwin'))
......@@ -28,12 +28,12 @@
# Author Tully Foote, Ken Conley
from rospkg.os_detect import OS_DEBIAN, OS_UBUNTU
from rospkg.os_detect import OS_DEBIAN, OS_UBUNTU, OsDetect
from .pip import PIP_INSTALLER
from .gem import GEM_INSTALLER
from .source import SOURCE_INSTALLER
from ..installers import PackageManagerInstaller, TYPE_CODENAME
from ..installers import PackageManagerInstaller
from ..shell_utils import read_stdout
# apt package manager key
......@@ -52,7 +52,7 @@ def register_debian(context):
context.add_os_installer_key(OS_DEBIAN, GEM_INSTALLER)
context.add_os_installer_key(OS_DEBIAN, SOURCE_INSTALLER)
context.set_default_os_installer_key(OS_DEBIAN, APT_INSTALLER)
context.set_os_version_type(OS_DEBIAN, TYPE_CODENAME)
context.set_os_version_type(OS_DEBIAN, OsDetect.get_codename)
def register_ubuntu(context):
context.add_os_installer_key(OS_UBUNTU, APT_INSTALLER)
......@@ -60,7 +60,7 @@ def register_ubuntu(context):
context.add_os_installer_key(OS_UBUNTU, GEM_INSTALLER)
context.add_os_installer_key(OS_UBUNTU, SOURCE_INSTALLER)
context.set_default_os_installer_key(OS_UBUNTU, APT_INSTALLER)
context.set_os_version_type(OS_UBUNTU, TYPE_CODENAME)
context.set_os_version_type(OS_UBUNTU, OsDetect.get_codename)
def dpkg_detect(pkgs, exec_fn=None):
"""
......@@ -92,19 +92,24 @@ def dpkg_detect(pkgs, exec_fn=None):
ret_list.append( pkg_row[0])
return [version_lock_map[r] for r in ret_list]
class AptInstaller(PackageManagerInstaller):
"""
"""
An implementation of the Installer for use on debian style
systems.
"""
def __init__(self):
super(AptInstaller, self).__init__(dpkg_detect)
def get_install_command(self, resolved, interactive=True, reinstall=False):
def get_install_command(self, resolved, interactive=True, reinstall=False, quiet=False):
packages = self.get_packages_to_install(resolved, reinstall=reinstall)
if not packages:
return []
if not interactive and quiet:
return [self.elevate_priv(['apt-get', 'install', '-y', '-qq', p]) for p in packages]
elif quiet:
return [self.elevate_priv(['apt-get', 'install', '-qq', p]) for p in packages]
if not interactive:
return [['sudo', 'apt-get', 'install', '-y', p] for p in packages]
return [self.elevate_priv(['apt-get', 'install', '-y', p]) for p in packages]
else:
return [['sudo', 'apt-get', 'install', p] for p in packages]
return [self.elevate_priv(['apt-get', 'install', p]) for p in packages]
......@@ -78,10 +78,10 @@ class PkgAddInstaller(Installer):
def __init__(self):
super(PkgAddInstaller, self).__init__(pkg_info_detect)
def get_install_command(self, resolved, interactive=True, reinstall=False):
def get_install_command(self, resolved, interactive=True, reinstall=False, quiet=False):
packages = self.get_packages_to_install(resolved, reinstall=reinstall)
if not packages:
return []
else:
#pkg_add does not have a non-interactive command
return [['sudo', '/usr/sbin/pkg_add', '-r']+packages]
return [self.elevate_priv(['/usr/sbin/pkg_add', '-r'])+packages]
......@@ -75,12 +75,12 @@ class GemInstaller(PackageManagerInstaller):
def __init__(self):
super(GemInstaller, self).__init__(gem_detect, supports_depends=True)
def get_install_command(self, resolved, interactive=True, reinstall=False):
def get_install_command(self, resolved, interactive=True, reinstall=False, quiet=False):
if not is_gem_installed():
raise InstallFailed((GEM_INSTALLER, "gem is not installed"))
packages = self.get_packages_to_install(resolved, reinstall=reinstall)
if not packages:
return []
else:
return [['sudo', 'gem', 'install', p] for p in packages]
return [self.elevate_priv(['gem', 'install', p]) for p in packages]
......@@ -107,10 +107,10 @@ class PortageInstaller(PackageManagerInstaller):
super(PortageInstaller, self).__init__(portage_detect)
def get_install_command(self, resolved, interactive=True, reinstall=False):
def get_install_command(self, resolved, interactive=True, reinstall=False, quiet=False):
atoms = self.get_packages_to_install(resolved, reinstall=reinstall)
cmd = [ 'sudo', 'emerge' ]
cmd = self.elevate_priv(['emerge'])
if not atoms:
return []
......
......@@ -60,11 +60,11 @@ class ZypperInstaller(PackageManagerInstaller):
def __init__(self):
super(ZypperInstaller, self).__init__(rpm_detect)
def get_install_command(self, resolved, interactive=True, reinstall=False):
def get_install_command(self, resolved, interactive=True, reinstall=False, quiet=False):
packages = self.get_packages_to_install(resolved, reinstall=reinstall)
if not packages:
return []
if not interactive:
return [['sudo', 'zypper', 'install', '-yl']+packages]
return [self.elevate_priv(['zypper', 'install', '-yl'])+packages]
else:
return [['sudo', 'zypper', 'install']+packages]
return [self.elevate_priv(['zypper', 'install'])+packages]
......@@ -29,14 +29,16 @@
# Author Tully Foote/tfoote@willowgarage.com, Ken Conley
import subprocess
import re
import json
import sys
import traceback
from rospkg.os_detect import OS_OSX
from rospkg.os_detect import OS_OSX, OsDetect
from ..core import InstallFailed
from ..core import InstallFailed, RosdepInternalError, InvalidData
from .pip import PIP_INSTALLER
from .source import SOURCE_INSTALLER
from ..installers import PackageManagerInstaller, TYPE_CODENAME
from ..installers import PackageManagerInstaller
from ..shell_utils import read_stdout
# add additional os names for brew, macports (TODO)
......@@ -45,6 +47,12 @@ OSXBREW_OS_NAME = 'osxbrew'
BREW_INSTALLER = 'homebrew'
MACPORTS_INSTALLER = 'macports'
#py3k
try:
_basestring = basestring
except NameError:
_basestring = str
def register_installers(context):
context.set_installer(MACPORTS_INSTALLER, MacportsInstaller())
context.set_installer(BREW_INSTALLER, HomebrewInstaller())
......@@ -55,7 +63,7 @@ def register_platforms(context):
context.add_os_installer_key(OS_OSX, PIP_INSTALLER)
context.add_os_installer_key(OS_OSX, SOURCE_INSTALLER)
context.set_default_os_installer_key(OS_OSX, BREW_INSTALLER)
context.set_os_version_type(OS_OSX, TYPE_CODENAME)
context.set_os_version_type(OS_OSX, OsDetect.get_codename)
def is_port_installed():
try:
......@@ -93,7 +101,7 @@ class MacportsInstaller(PackageManagerInstaller):
return []
else:
#TODO: interactive
return [['sudo', 'port', 'install', p] for p in packages]
return [self.elevate_priv(['port', 'install', p]) for p in packages]
def is_brew_installed():
try:
......@@ -102,64 +110,240 @@ def is_brew_installed():
except OSError:
return False
def brew_detect(formulas, exec_fn=None):
"""
Given a list of formulas, return the list of installed formulas.
:param formulas: List of homebrew formula names
class HomebrewResolution(object):
"""Resolution information for a single package of a Homebrew rosdep."""
def __init__(self, package, install_flags, options):
"""
:param package: Homebrew package name, possibly fully qualified
with tap.
:param install_flags: List of strings of additional flags for
``brew install`` and ``brew deps`` command which are not
options (e.g. ``--HEAD``)
:param options: List of strings of options for the homebrew
package.
"""
self.package = package
self.install_flags = install_flags
self.options = options
def __eq__(self, other):
return other.package == self.package and \
other.install_flags == self.install_flags and \
other.options == self.options
def __hash__(self):
return hash((
type(self),
self.package,
tuple(self.install_flags),
tuple(self.options)))
def __str__(self):
return ' '.join(self.to_list())
def to_list(self):
return [self.package] + self.install_flags + self.options
def brew_strip_pkg_name(package):
"""Strip the tap information of a fully qualified package name.
:returns: Unqualified package name. E.g. 'foo-pkg' for input
'ros/hydro/foo-pkg'
"""
return package.split('/')[-1]
def brew_detect(resolved, exec_fn=None):
"""Given a list of resolutions, return the list of installed resolutions.
:param resolved: List of HomebrewResolution objects
:returns: Filtered list of HomebrewResolution objects
"""
if exec_fn is None:
exec_fn = read_stdout
ret_list = []
std_out = exec_fn(['brew', 'list'])
installed_formulae = std_out.split()