summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHarmen Stoppels <me@harmenstoppels.nl>2023-11-30 18:38:05 +0100
committerGitHub <noreply@github.com>2023-11-30 09:38:05 -0800
commitd436e97fc6163ffda9c9b5a8f829dd77484d625d (patch)
tree65dbef84739d0fe6745c1d478e6609f956f9326d /lib
parentf3983d60c26a18d5294fdebc6f6ea1b92766a5aa (diff)
downloadspack-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.py6
-rw-r--r--lib/spack/spack/solver/asp.py33
-rw-r--r--lib/spack/spack/test/concretize.py78
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
+ )