From b88f55e523590d33b03b0d90f2a2d0875f4c60cb Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Mon, 19 Jun 2017 00:51:34 -0700 Subject: Add `spack dependencies` command and tests for it and dependents. --- lib/spack/llnl/util/tty/color.py | 4 +- lib/spack/spack/cmd/dependencies.py | 87 ++++++++++++++++++++++++++++++++ lib/spack/spack/cmd/dependents.py | 2 +- lib/spack/spack/main.py | 5 +- lib/spack/spack/package.py | 27 ++++++---- lib/spack/spack/test/cmd/dependencies.py | 77 ++++++++++++++++++++++++++++ lib/spack/spack/test/cmd/dependents.py | 74 +++++++++++++++++++++++++++ 7 files changed, 261 insertions(+), 15 deletions(-) create mode 100644 lib/spack/spack/cmd/dependencies.py create mode 100644 lib/spack/spack/test/cmd/dependencies.py create mode 100644 lib/spack/spack/test/cmd/dependents.py (limited to 'lib') diff --git a/lib/spack/llnl/util/tty/color.py b/lib/spack/llnl/util/tty/color.py index a39b5b95f8..1081acd0f1 100644 --- a/lib/spack/llnl/util/tty/color.py +++ b/lib/spack/llnl/util/tty/color.py @@ -155,9 +155,9 @@ def set_color_when(when): def color_when(value): """Context manager to temporarily use a particular color setting.""" old_value = value - set_color(value) + set_color_when(value) yield - set_color(old_value) + set_color_when(old_value) class match_to_ansi(object): diff --git a/lib/spack/spack/cmd/dependencies.py b/lib/spack/spack/cmd/dependencies.py new file mode 100644 index 0000000000..b35c324fc7 --- /dev/null +++ b/lib/spack/spack/cmd/dependencies.py @@ -0,0 +1,87 @@ +############################################################################## +# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/llnl/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +import argparse + +import llnl.util.tty as tty +from llnl.util.tty.colify import colify + +import spack +import spack.store +import spack.cmd + +description = "show dependencies of a package" +section = "basic" +level = "long" + + +def setup_parser(subparser): + subparser.add_argument( + '-i', '--installed', action='store_true', default=False, + help="List installed dependencies of an installed spec, " + "instead of possible dependencies of a package.") + subparser.add_argument( + '-t', '--transitive', action='store_true', default=False, + help="Show all transitive dependencies.") + subparser.add_argument( + 'spec', nargs=argparse.REMAINDER, help="spec or package name") + + +def dependencies(parser, args): + specs = spack.cmd.parse_specs(args.spec) + if len(specs) != 1: + tty.die("spack dependencies takes only one spec.") + + if args.installed: + spec = spack.cmd.disambiguate_spec(specs[0]) + + tty.msg("Dependencies of %s" % spec.format('$_$@$%@$/', color=True)) + deps = spack.store.db.installed_relatives( + spec, 'children', args.transitive) + if deps: + spack.cmd.display_specs(deps, long=True) + else: + print("No dependencies") + + else: + spec = specs[0] + + if not spec.virtual: + packages = [spec.package] + else: + packages = [spack.repo.get(s.name) + for s in spack.repo.providers_for(spec)] + + dependencies = set() + for pkg in packages: + dependencies.update( + set(pkg.possible_dependencies(args.transitive))) + + if spec.name in dependencies: + dependencies.remove(spec.name) + + if dependencies: + colify(sorted(dependencies)) + else: + print("No dependencies") diff --git a/lib/spack/spack/cmd/dependents.py b/lib/spack/spack/cmd/dependents.py index 82a85e1180..3413ac3227 100644 --- a/lib/spack/spack/cmd/dependents.py +++ b/lib/spack/spack/cmd/dependents.py @@ -106,7 +106,7 @@ def dependents(parser, args): deps = spack.store.db.installed_relatives( spec, 'parents', args.transitive) if deps: - spack.cmd.display_specs(deps) + spack.cmd.display_specs(deps, long=True) else: print("No dependents") diff --git a/lib/spack/spack/main.py b/lib/spack/spack/main.py index ee74c915df..d10047e325 100644 --- a/lib/spack/spack/main.py +++ b/lib/spack/spack/main.py @@ -377,12 +377,15 @@ class SpackCommand(object): self.command = spack.cmd.get_command(command) self.fail_on_error = fail_on_error - def __call__(self, *argv): + def __call__(self, *argv, **kwargs): """Invoke this SpackCommand. Args: argv (list of str): command line arguments. + Keyword Args: + color (optional bool): force-disable or force-enable color + Returns: (str, str): output and error as a strings diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index bdf56af608..e8e741fefa 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -594,26 +594,31 @@ class PackageBase(with_metaclass(PackageMeta, object)): self.extra_args = {} - def possible_dependencies(self, visited=None): - """Return set of possible transitive dependencies of this package.""" + def possible_dependencies(self, transitive=True, visited=None): + """Return set of possible transitive dependencies of this package. + + Args: + transitive (bool): include all transitive dependencies if True, + only direct dependencies if False. + """ if visited is None: visited = set() visited.add(self.name) for name in self.dependencies: - if name in visited: - continue - spec = spack.spec.Spec(name) + if not spec.virtual: - pkg = spack.repo.get(name) - for name in pkg.possible_dependencies(visited): - visited.add(name) + visited.add(name) + if transitive: + pkg = spack.repo.get(name) + pkg.possible_dependencies(transitive, visited) else: for provider in spack.repo.providers_for(spec): - pkg = spack.repo.get(provider.name) - for name in pkg.possible_dependencies(visited): - visited.add(name) + visited.add(provider.name) + if transitive: + pkg = spack.repo.get(provider.name) + pkg.possible_dependencies(transitive, visited) return visited diff --git a/lib/spack/spack/test/cmd/dependencies.py b/lib/spack/spack/test/cmd/dependencies.py new file mode 100644 index 0000000000..e024fcc2e6 --- /dev/null +++ b/lib/spack/spack/test/cmd/dependencies.py @@ -0,0 +1,77 @@ +############################################################################## +# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/llnl/spack +# Please also see the NOTICE and LICENSE files for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +import re + +from llnl.util.tty.color import color_when + +import spack +from spack.main import SpackCommand + +dependencies = SpackCommand('dependencies') + +mpis = ['mpich', 'mpich2', 'multi-provider-mpi', 'zmpi'] +mpi_deps = ['fake'] + + +def test_immediate_dependencies(builtin_mock): + out, err = dependencies('mpileaks') + actual = set(re.split(r'\s+', out.strip())) + expected = set(['callpath'] + mpis) + assert expected == actual + + +def test_transitive_dependencies(builtin_mock): + out, err = dependencies('--transitive', 'mpileaks') + actual = set(re.split(r'\s+', out.strip())) + expected = set( + ['callpath', 'dyninst', 'libdwarf', 'libelf'] + mpis + mpi_deps) + assert expected == actual + + +def test_immediate_installed_dependencies(builtin_mock, database): + with color_when(False): + out, err = dependencies('--installed', 'mpileaks^mpich') + + lines = [l for l in out.strip().split('\n') if not l.startswith('--')] + hashes = set([re.split(r'\s+', l)[0] for l in lines]) + + expected = set([spack.store.db.query_one(s).dag_hash(7) + for s in ['mpich', 'callpath^mpich']]) + + assert expected == hashes + + +def test_transitive_installed_dependencies(builtin_mock, database): + with color_when(False): + out, err = dependencies('--installed', '--transitive', 'mpileaks^zmpi') + + lines = [l for l in out.strip().split('\n') if not l.startswith('--')] + hashes = set([re.split(r'\s+', l)[0] for l in lines]) + + expected = set([spack.store.db.query_one(s).dag_hash(7) + for s in ['zmpi', 'callpath^zmpi', 'fake', + 'dyninst', 'libdwarf', 'libelf']]) + + assert expected == hashes diff --git a/lib/spack/spack/test/cmd/dependents.py b/lib/spack/spack/test/cmd/dependents.py new file mode 100644 index 0000000000..69f57d88a3 --- /dev/null +++ b/lib/spack/spack/test/cmd/dependents.py @@ -0,0 +1,74 @@ +############################################################################## +# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/llnl/spack +# Please also see the NOTICE and LICENSE files for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +import re + +from llnl.util.tty.color import color_when + +import spack +from spack.main import SpackCommand + +dependents = SpackCommand('dependents') + + +def test_immediate_dependents(builtin_mock): + out, err = dependents('libelf') + actual = set(re.split(r'\s+', out.strip())) + assert actual == set(['dyninst', 'libdwarf']) + + +def test_transitive_dependents(builtin_mock): + out, err = dependents('--transitive', 'libelf') + actual = set(re.split(r'\s+', out.strip())) + assert actual == set( + ['callpath', 'dyninst', 'libdwarf', 'mpileaks', 'multivalue_variant']) + + +def test_immediate_installed_dependents(builtin_mock, database): + with color_when(False): + out, err = dependents('--installed', 'libelf') + + lines = [l for l in out.strip().split('\n') if not l.startswith('--')] + hashes = set([re.split(r'\s+', l)[0] for l in lines]) + + expected = set([spack.store.db.query_one(s).dag_hash(7) + for s in ['dyninst', 'libdwarf']]) + + libelf = spack.store.db.query_one('libelf') + expected = set([d.dag_hash(7) for d in libelf.dependents()]) + + assert expected == hashes + + +def test_transitive_installed_dependents(builtin_mock, database): + with color_when(False): + out, err = dependents('--installed', '--transitive', 'fake') + + lines = [l for l in out.strip().split('\n') if not l.startswith('--')] + hashes = set([re.split(r'\s+', l)[0] for l in lines]) + + expected = set([spack.store.db.query_one(s).dag_hash(7) + for s in ['zmpi', 'callpath^zmpi', 'mpileaks^zmpi']]) + + assert expected == hashes -- cgit v1.2.3-60-g2f50