diff options
author | Harmen Stoppels <me@harmenstoppels.nl> | 2023-11-30 18:38:05 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-30 09:38:05 -0800 |
commit | d436e97fc6163ffda9c9b5a8f829dd77484d625d (patch) | |
tree | 65dbef84739d0fe6745c1d478e6609f956f9326d /lib | |
parent | f3983d60c26a18d5294fdebc6f6ea1b92766a5aa (diff) | |
download | spack-d436e97fc6163ffda9c9b5a8f829dd77484d625d.tar.gz spack-d436e97fc6163ffda9c9b5a8f829dd77484d625d.tar.bz2 spack-d436e97fc6163ffda9c9b5a8f829dd77484d625d.tar.xz spack-d436e97fc6163ffda9c9b5a8f829dd77484d625d.zip |
reuse concretization: allow externals from remote when locally configured (#35975)
This looks to me like the best compromise regarding externals in a
build cache. I wouldn't want `spack install` on my machine to install
specs that were marked external on another. At the same time there are
centers that control the target systems on which spack is used, and
would want to use external in buildcaches.
As a solution, reuse concretization will now consider those externals
used in buildcaches that match a locally configured external in
packages.yaml.
So for example person A installs and pushes specs with this config:
```yaml
packages:
ncurses:
externals:
- spec: ncurses@6.0.12345 +feature
prefix: /usr
```
and person B concretizes and installs using that buildcache with the
following config:
```yaml
packages:
ncurses:
externals:
- spec: ncurses@6
prefix: /usr
```
the spec will be reused (or rather, will be considered for reuse...)
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 + ) |