summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/spec.py15
-rw-r--r--lib/spack/spack/test/spec_dag.py35
2 files changed, 48 insertions, 2 deletions
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 4c1421e1e8..9256be5803 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -3970,12 +3970,23 @@ class Spec(object):
csv = query_parameters.pop().strip()
query_parameters = re.split(r"\s*,\s*", csv)
+ # In some cases a package appears multiple times in the same DAG for *distinct*
+ # specs. For example, a build-type dependency may itself depend on a package
+ # the current spec depends on, but their specs may differ. Therefore we iterate
+ # in an order here that prioritizes the build, test and runtime dependencies;
+ # only when we don't find the package do we consider the full DAG.
+ order = lambda: itertools.chain(
+ self.traverse(deptype="link"),
+ self.dependencies(deptype=("build", "run", "test")),
+ self.traverse(), # fall back to a full search
+ )
+
try:
value = next(
itertools.chain(
# Regular specs
- (x for x in self.traverse() if x.name == name),
- (x for x in self.traverse() if (not x.virtual) and x.package.provides(name)),
+ (x for x in order() if x.name == name),
+ (x for x in order() if (not x.virtual) and x.package.provides(name)),
)
)
except StopIteration:
diff --git a/lib/spack/spack/test/spec_dag.py b/lib/spack/spack/test/spec_dag.py
index 66e5a0bc2f..898ae15f74 100644
--- a/lib/spack/spack/test/spec_dag.py
+++ b/lib/spack/spack/test/spec_dag.py
@@ -1068,3 +1068,38 @@ def test_adding_same_deptype_with_the_same_name_raises(
p.add_dependency_edge(c1, deptype=c1_deptypes)
with pytest.raises(spack.error.SpackError):
p.add_dependency_edge(c2, deptype=c2_deptypes)
+
+
+@pytest.mark.regression("33499")
+def test_indexing_prefers_direct_or_transitive_link_deps():
+ # Test whether spec indexing prefers direct/transitive link type deps over deps of
+ # build/run/test deps, and whether it does fall back to a full dag search.
+ root = Spec("root")
+
+ # Use a and z to since we typically traverse by edges sorted alphabetically.
+ a1 = Spec("a1")
+ a2 = Spec("a2")
+ z1 = Spec("z1")
+ z2 = Spec("z2")
+
+ # Same package, different spec.
+ z3_flavor_1 = Spec("z3 +through_a1")
+ z3_flavor_2 = Spec("z3 +through_z1")
+
+ root.add_dependency_edge(a1, deptype=("build", "run", "test"))
+
+ # unique package as a dep of a build/run/test type dep.
+ a1.add_dependency_edge(a2, deptype="all")
+ a1.add_dependency_edge(z3_flavor_1, deptype="all")
+
+ # chain of link type deps root -> z1 -> z2 -> z3
+ root.add_dependency_edge(z1, deptype="link")
+ z1.add_dependency_edge(z2, deptype="link")
+ z2.add_dependency_edge(z3_flavor_2, deptype="link")
+
+ # Indexing should prefer the link-type dep.
+ assert "through_z1" in root["z3"].variants
+ assert "through_a1" in a1["z3"].variants
+
+ # Ensure that the full DAG is still searched
+ assert root["a2"]