spack dependencies: support --deptype argument

- `spack dependencies` can now take a --deptype argument to only traverse
  particular deptypes

- add a new "common" argument for deptype in spack.cmd.common.arguments

- Database.installed_relatives() can now also take a deptype argument
  - this is used by `spack dependencies --installed`
This commit is contained in:
Todd Gamblin 2019-04-20 22:41:14 -07:00
parent 3dac78fc19
commit 2e22fc1090
4 changed files with 48 additions and 11 deletions

View File

@ -9,6 +9,7 @@
import spack.cmd import spack.cmd
import spack.config import spack.config
import spack.dependency as dep
import spack.environment as ev import spack.environment as ev
import spack.modules import spack.modules
import spack.spec import spack.spec
@ -104,6 +105,19 @@ def default(self, value):
pass pass
class DeptypeAction(argparse.Action):
"""Creates a tuple of valid dependency tpyes from a deptype argument."""
def __call__(self, parser, namespace, values, option_string=None):
deptype = dep.all_deptypes
if values:
deptype = tuple(x.strip() for x in values.split(','))
if deptype == ('all',):
deptype = 'all'
deptype = dep.canonical_deptype(deptype)
setattr(namespace, self.dest, deptype)
_arguments['constraint'] = Args( _arguments['constraint'] = Args(
'constraint', nargs=argparse.REMAINDER, action=ConstraintAction, 'constraint', nargs=argparse.REMAINDER, action=ConstraintAction,
help='constraint to select a subset of installed packages') help='constraint to select a subset of installed packages')
@ -128,6 +142,11 @@ def default(self, value):
dest='dirty', dest='dirty',
help='unset harmful variables in the build environment (default)') help='unset harmful variables in the build environment (default)')
_arguments['deptype'] = Args(
'--deptype', action=DeptypeAction, default=dep.all_deptypes,
help="comma-separated list of deptypes to traverse\ndefault=%s"
% ','.join(dep.all_deptypes))
_arguments['dirty'] = Args( _arguments['dirty'] = Args(
'--dirty', '--dirty',
action='store_true', action='store_true',

View File

@ -8,10 +8,11 @@
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.tty.colify import colify from llnl.util.tty.colify import colify
import spack.environment as ev
import spack.store
import spack.repo
import spack.cmd import spack.cmd
import spack.cmd.common.arguments as arguments
import spack.environment as ev
import spack.repo
import spack.store
description = "show dependencies of a package" description = "show dependencies of a package"
section = "basic" section = "basic"
@ -26,6 +27,7 @@ def setup_parser(subparser):
subparser.add_argument( subparser.add_argument(
'-t', '--transitive', action='store_true', default=False, '-t', '--transitive', action='store_true', default=False,
help="show all transitive dependencies") help="show all transitive dependencies")
arguments.add_common_arguments(subparser, ['deptype'])
subparser.add_argument( subparser.add_argument(
'-V', '--no-expand-virtuals', action='store_false', default=True, '-V', '--no-expand-virtuals', action='store_false', default=True,
dest="expand_virtuals", help="do not expand virtual dependencies") dest="expand_virtuals", help="do not expand virtual dependencies")
@ -45,7 +47,7 @@ def dependencies(parser, args):
format_string = '{name}{@version}{%compiler}{/hash:7}' format_string = '{name}{@version}{%compiler}{/hash:7}'
tty.msg("Dependencies of %s" % spec.format(format_string, color=True)) tty.msg("Dependencies of %s" % spec.format(format_string, color=True))
deps = spack.store.db.installed_relatives( deps = spack.store.db.installed_relatives(
spec, 'children', args.transitive) spec, 'children', args.transitive, deptype=args.deptype)
if deps: if deps:
spack.cmd.display_specs(deps, long=True) spack.cmd.display_specs(deps, long=True)
else: else:
@ -63,9 +65,9 @@ def dependencies(parser, args):
dependencies = set() dependencies = set()
for pkg in packages: for pkg in packages:
dependencies.update( possible = pkg.possible_dependencies(
set(pkg.possible_dependencies( args.transitive, args.expand_virtuals, deptype=args.deptype)
args.transitive, args.expand_virtuals))) dependencies.update(possible)
if spec.name in dependencies: if spec.name in dependencies:
dependencies.remove(spec.name) dependencies.remove(spec.name)

View File

@ -875,7 +875,8 @@ def remove(self, spec):
return self._remove(spec) return self._remove(spec)
@_autospec @_autospec
def installed_relatives(self, spec, direction='children', transitive=True): def installed_relatives(self, spec, direction='children', transitive=True,
deptype='all'):
"""Return installed specs related to this one.""" """Return installed specs related to this one."""
if direction not in ('parents', 'children'): if direction not in ('parents', 'children'):
raise ValueError("Invalid direction: %s" % direction) raise ValueError("Invalid direction: %s" % direction)
@ -883,11 +884,12 @@ def installed_relatives(self, spec, direction='children', transitive=True):
relatives = set() relatives = set()
for spec in self.query(spec): for spec in self.query(spec):
if transitive: if transitive:
to_add = spec.traverse(direction=direction, root=False) to_add = spec.traverse(
direction=direction, root=False, deptype=deptype)
elif direction == 'parents': elif direction == 'parents':
to_add = spec.dependents() to_add = spec.dependents(deptype=deptype)
else: # direction == 'children' else: # direction == 'children'
to_add = spec.dependencies() to_add = spec.dependencies(deptype=deptype)
for relative in to_add: for relative in to_add:
hash_key = relative.dag_hash() hash_key = relative.dag_hash()

View File

@ -32,6 +32,20 @@ def test_transitive_dependencies(mock_packages):
assert expected == actual assert expected == actual
def test_transitive_dependencies_with_deptypes(mock_packages):
out = dependencies('--transitive', '--deptype=link,run', 'dtbuild1')
deps = set(re.split(r'\s+', out.strip()))
assert set(['dtlink2', 'dtrun2']) == deps
out = dependencies('--transitive', '--deptype=build', 'dtbuild1')
deps = set(re.split(r'\s+', out.strip()))
assert set(['dtbuild2', 'dtlink2']) == deps
out = dependencies('--transitive', '--deptype=link', 'dtbuild1')
deps = set(re.split(r'\s+', out.strip()))
assert set(['dtlink2']) == deps
@pytest.mark.db @pytest.mark.db
def test_immediate_installed_dependencies(mock_packages, database): def test_immediate_installed_dependencies(mock_packages, database):
with color_when(False): with color_when(False):