summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/solver/asp.py15
-rw-r--r--lib/spack/spack/test/concretize.py40
2 files changed, 54 insertions, 1 deletions
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
index e3dd3ca6df..6b2205a6bb 100644
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -944,14 +944,26 @@ class ConcreteSpecsByHash(collections.abc.Mapping):
def __init__(self) -> None:
self.data: Dict[str, spack.spec.Spec] = {}
+ self.explicit: Set[str] = set()
def __getitem__(self, dag_hash: str) -> spack.spec.Spec:
return self.data[dag_hash]
+ def explicit_items(self) -> Iterator[Tuple[str, spack.spec.Spec]]:
+ """Iterate on items that have been added explicitly, and not just as a dependency
+ of other nodes.
+ """
+ for h, s in self.items():
+ # We need to make an exception for gcc-runtime, until we can splice it.
+ if h in self.explicit or s.name == "gcc-runtime":
+ yield h, s
+
def add(self, spec: spack.spec.Spec) -> bool:
"""Adds a new concrete spec to the mapping. Returns True if the spec was just added,
False if the spec was already in the mapping.
+ Calling this function marks the spec as added explicitly.
+
Args:
spec: spec to be added
@@ -966,6 +978,7 @@ class ConcreteSpecsByHash(collections.abc.Mapping):
raise ValueError(msg)
dag_hash = spec.dag_hash()
+ self.explicit.add(dag_hash)
if dag_hash in self.data:
return False
@@ -2349,7 +2362,7 @@ class SpackSolverSetup:
def concrete_specs(self):
"""Emit facts for reusable specs"""
- for h, spec in self.reusable_and_possible.items():
+ for h, spec in self.reusable_and_possible.explicit_items():
# this indicates that there is a spec like this installed
self.gen.fact(fn.installed_hash(spec.name, h))
# this describes what constraints it imposes on the solve
diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py
index 009d9b6a36..f0b92c98f5 100644
--- a/lib/spack/spack/test/concretize.py
+++ b/lib/spack/spack/test/concretize.py
@@ -2463,6 +2463,46 @@ class TestConcretize:
s = Spec(f"dtuse ^{str(json_file)}").concretized()
assert s["dttop"].dag_hash() == build_dep.dag_hash()
+ @pytest.mark.regression("44040")
+ def test_exclude_specs_from_reuse(self, monkeypatch):
+ """Tests that we can exclude a spec from reuse when concretizing, and that the spec
+ is not added back to the solve as a dependency of another reusable spec.
+
+ The expected spec is:
+
+ o callpath@1.0
+ |\
+ | |\
+ o | | mpich@3.0.4
+ |/ /
+ | o dyninst@8.2
+ |/|
+ | |\
+ | | o libdwarf@20130729
+ | |/|
+ |/|/
+ | o libelf@0.8.13
+ |/
+ o glibc@2.31
+ """
+ # Prepare a mock mirror that returns an old version of dyninst
+ request_str = "callpath ^mpich"
+ reused = Spec(f"{request_str} ^dyninst@8.1.1").concretized()
+ monkeypatch.setattr(spack.solver.asp, "_specs_from_mirror", lambda: [reused])
+
+ # Exclude dyninst from reuse, so we expect that the old version is not taken into account
+ with spack.config.override(
+ "concretizer:reuse", {"from": [{"type": "buildcache", "exclude": ["dyninst"]}]}
+ ):
+ result = Spec(request_str).concretized()
+
+ assert result.dag_hash() != reused.dag_hash()
+ assert result["mpich"].dag_hash() == reused["mpich"].dag_hash()
+ assert result["dyninst"].dag_hash() != reused["dyninst"].dag_hash()
+ assert result["dyninst"].satisfies("@=8.2")
+ for dep in result["dyninst"].traverse(root=False):
+ assert dep.dag_hash() == reused[dep.name].dag_hash()
+
@pytest.fixture()
def duplicates_test_repository():