spack external find: allow to search by tags (#21407)

This commit adds an option to the `external find`
command that allows it to search by tags. In this
way group of executables with common purposes can
be grouped under a single name and a simple command
can be used to detect all of them.

As an example introduce the 'build-tools' tag to
search for common development tools on a system
This commit is contained in:
Massimiliano Culpo
2021-02-04 13:17:32 +01:00
committed by GitHub
parent e9ae44fd8c
commit 694d633a2c
15 changed files with 76 additions and 12 deletions

View File

@@ -250,8 +250,8 @@ def very_long():
@arg
def tags():
return Args(
'-t', '--tags', action='append',
help='filter a package query by tags')
'-t', '--tag', action='append', dest='tags', metavar='TAG',
help='filter a package query by tag (multiple use allowed)')
@arg

View File

@@ -16,6 +16,7 @@
import six
import spack
import spack.cmd
import spack.cmd.common.arguments
import spack.error
import spack.util.environment
import spack.util.spack_yaml as syaml
@@ -42,6 +43,7 @@ def setup_parser(subparser):
'--scope', choices=scopes, metavar=scopes_metavar,
default=spack.config.default_modify_scope('packages'),
help="configuration scope to modify")
spack.cmd.common.arguments.add_common_arguments(find_parser, ['tags'])
find_parser.add_argument('packages', nargs=argparse.REMAINDER)
sp.add_parser(
@@ -148,9 +150,28 @@ def _spec_is_valid(spec):
def external_find(args):
# Construct the list of possible packages to be detected
packages_to_check = []
# Add the packages that have been required explicitly
if args.packages:
packages_to_check = list(spack.repo.get(pkg) for pkg in args.packages)
else:
if args.tags:
allowed = set(spack.repo.path.packages_with_tags(*args.tags))
packages_to_check = [x for x in packages_to_check if x in allowed]
if args.tags and not packages_to_check:
# If we arrived here we didn't have any explicit package passed
# as argument, which means to search all packages.
# Since tags are cached it's much faster to construct what we need
# to search directly, rather than filtering after the fact
packages_to_check = [
spack.repo.get(pkg) for pkg in
spack.repo.path.packages_with_tags(*args.tags)
]
# If the list of packages is empty, search for every possible package
if not args.tags and not packages_to_check:
packages_to_check = spack.repo.path.all_packages()
pkg_to_entries = _get_external_packages(packages_to_check)

View File

@@ -2,6 +2,8 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import pytest
import os
import os.path
@@ -242,3 +244,26 @@ def test_new_entries_are_reported_correctly(
# has been found
output = external('find', 'gcc')
assert 'No new external packages detected' in output
@pytest.mark.parametrize('command_args', [
('-t', 'build-tools'),
('-t', 'build-tools', 'cmake'),
])
def test_use_tags_for_detection(
command_args, mock_executable, mutable_config, monkeypatch
):
# Prepare an environment to detect a fake cmake
cmake_exe = mock_executable('cmake', output="echo cmake version 3.19.1")
prefix = os.path.dirname(cmake_exe)
monkeypatch.setenv('PATH', prefix)
openssl_exe = mock_executable('openssl', output="OpenSSL 2.8.3")
prefix = os.path.dirname(openssl_exe)
monkeypatch.setenv('PATH', prefix)
# Test that we detect specs
output = external('find', *command_args)
assert 'The following specs have been' in output
assert 'cmake' in output
assert 'openssl' not in output

View File

@@ -89,7 +89,7 @@ def test_query_arguments():
@pytest.mark.usefixtures('database', 'mock_display')
def test_tag1(parser, specs):
args = parser.parse_args(['--tags', 'tag1'])
args = parser.parse_args(['--tag', 'tag1'])
spack.cmd.find.find(parser, args)
assert len(specs) == 2
@@ -100,7 +100,7 @@ def test_tag1(parser, specs):
@pytest.mark.db
@pytest.mark.usefixtures('database', 'mock_display')
def test_tag2(parser, specs):
args = parser.parse_args(['--tags', 'tag2'])
args = parser.parse_args(['--tag', 'tag2'])
spack.cmd.find.find(parser, args)
assert len(specs) == 1
@@ -110,7 +110,7 @@ def test_tag2(parser, specs):
@pytest.mark.db
@pytest.mark.usefixtures('database', 'mock_display')
def test_tag2_tag3(parser, specs):
args = parser.parse_args(['--tags', 'tag2', '--tags', 'tag3'])
args = parser.parse_args(['--tag', 'tag2', '--tag', 'tag3'])
spack.cmd.find.find(parser, args)
assert len(specs) == 0

View File

@@ -33,15 +33,15 @@ def test_list_search_description():
def test_list_tags():
output = list('--tags', 'proxy-app')
output = list('--tag', 'proxy-app')
assert 'cloverleaf3d' in output
assert 'hdf5' not in output
output = list('--tags', 'hpc')
output = list('--tag', 'hpc')
assert 'nek5000' in output
assert 'mfem' in output
output = list('--tags', 'HPC')
output = list('--tag', 'HPC')
assert 'nek5000' in output
assert 'mfem' in output