diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/binary_distribution.py | 6 | ||||
-rw-r--r-- | lib/spack/spack/solver/asp.py | 33 | ||||
-rw-r--r-- | lib/spack/spack/test/concretize.py | 78 |
3 files changed, 114 insertions, 3 deletions
diff --git a/lib/spack/spack/binary_distribution.py b/lib/spack/spack/binary_distribution.py index 8cfb891640..159b07eb94 100644 --- a/lib/spack/spack/binary_distribution.py +++ b/lib/spack/spack/binary_distribution.py @@ -230,7 +230,11 @@ class BinaryCacheIndex: ) return - spec_list = db.query_local(installed=False, in_buildcache=True) + spec_list = [ + s + for s in db.query_local(installed=any, in_buildcache=any) + if s.external or db.query_local_by_spec_hash(s.dag_hash()).in_buildcache + ] for indexed_spec in spec_list: dag_hash = indexed_spec.dag_hash() diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 8ad8d5c5c0..4d9b5a0c39 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -3134,6 +3134,25 @@ def _develop_specs_from_env(spec, env): spec.constrain(dev_info["spec"]) +def _is_reusable_external(packages, spec: spack.spec.Spec) -> bool: + """Returns true iff spec is an external that can be reused. + + Arguments: + packages: the packages configuration + spec: the spec to check + """ + for name in {spec.name, *(p.name for p in spec.package.provided)}: + for entry in packages.get(name, {}).get("externals", []): + if ( + spec.satisfies(entry["spec"]) + and spec.external_path == entry.get("prefix") + and spec.external_modules == entry.get("modules") + ): + return True + + return False + + class Solver: """This is the main external interface class for solving. @@ -3181,8 +3200,18 @@ class Solver: # Specs from buildcaches try: - index = spack.binary_distribution.update_cache_and_get_specs() - reusable_specs.extend(index) + # Specs in a build cache that depend on externals are reusable as long as local + # config has matching externals. This should guard against picking up binaries + # linked against externals not available locally, while still supporting the use + # case of distributing binaries across machines with similar externals. + packages = spack.config.get("packages") + reusable_specs.extend( + [ + s + for s in spack.binary_distribution.update_cache_and_get_specs() + if not s.external or _is_reusable_external(packages, s) + ] + ) except (spack.binary_distribution.FetchCacheError, IndexError): # this is raised when no mirrors had indices. # TODO: update mirror configuration so it can indicate that the diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 2818aad042..2eb75edb6c 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -2427,3 +2427,81 @@ class TestConcretizeEdges: s = Spec("blas-only-client ^openblas").concretized() assert s.satisfies("^[virtuals=blas] openblas") assert not s.satisfies("^[virtuals=blas,lapack] openblas") + + +def test_reusable_externals_match(mock_packages, tmpdir): + spec = Spec("mpich@4.1%gcc@13.1.0~debug build_system=generic arch=linux-ubuntu23.04-zen2") + spec.external_path = tmpdir.strpath + spec.external_modules = ["mpich/4.1"] + spec._mark_concrete() + assert spack.solver.asp._is_reusable_external( + { + "mpich": { + "externals": [ + {"spec": "mpich@4.1", "prefix": tmpdir.strpath, "modules": ["mpich/4.1"]} + ] + } + }, + spec, + ) + + +def test_reusable_externals_match_virtual(mock_packages, tmpdir): + spec = Spec("mpich@4.1%gcc@13.1.0~debug build_system=generic arch=linux-ubuntu23.04-zen2") + spec.external_path = tmpdir.strpath + spec.external_modules = ["mpich/4.1"] + spec._mark_concrete() + assert spack.solver.asp._is_reusable_external( + { + "mpi": { + "externals": [ + {"spec": "mpich@4.1", "prefix": tmpdir.strpath, "modules": ["mpich/4.1"]} + ] + } + }, + spec, + ) + + +def test_reusable_externals_different_prefix(mock_packages, tmpdir): + spec = Spec("mpich@4.1%gcc@13.1.0~debug build_system=generic arch=linux-ubuntu23.04-zen2") + spec.external_path = "/other/path" + spec.external_modules = ["mpich/4.1"] + spec._mark_concrete() + assert not spack.solver.asp._is_reusable_external( + { + "mpich": { + "externals": [ + {"spec": "mpich@4.1", "prefix": tmpdir.strpath, "modules": ["mpich/4.1"]} + ] + } + }, + spec, + ) + + +@pytest.mark.parametrize("modules", [None, ["mpich/4.1", "libfabric/1.19"]]) +def test_reusable_externals_different_modules(mock_packages, tmpdir, modules): + spec = Spec("mpich@4.1%gcc@13.1.0~debug build_system=generic arch=linux-ubuntu23.04-zen2") + spec.external_path = tmpdir.strpath + spec.external_modules = modules + spec._mark_concrete() + assert not spack.solver.asp._is_reusable_external( + { + "mpich": { + "externals": [ + {"spec": "mpich@4.1", "prefix": tmpdir.strpath, "modules": ["mpich/4.1"]} + ] + } + }, + spec, + ) + + +def test_reusable_externals_different_spec(mock_packages, tmpdir): + spec = Spec("mpich@4.1%gcc@13.1.0~debug build_system=generic arch=linux-ubuntu23.04-zen2") + spec.external_path = tmpdir.strpath + spec._mark_concrete() + assert not spack.solver.asp._is_reusable_external( + {"mpich": {"externals": [{"spec": "mpich@4.1 +debug", "prefix": tmpdir.strpath}]}}, spec + ) |