diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index 69fb895bf51..36ea507bd61 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -1817,7 +1817,9 @@ def _add_dependency( ) except StopIteration: # Error if we have overlapping or incompatible deptypes - if any(not dt.compatible(dspec.depflag, depflag) for dspec in orig): + if any(not dt.compatible(dspec.depflag, depflag) for dspec in orig) and all( + dspec.when == when for dspec in orig + ): edge_attrs = f"deptypes={dt.flag_to_chars(depflag).strip()}" required_dep_str = f"^[{edge_attrs}] {str(spec)}" @@ -3440,7 +3442,11 @@ def satisfies(self, other: Union[str, "Spec"], deps: bool = True) -> bool: mock_nodes_from_old_specfiles = set() for rhs_edge in other.traverse_edges(root=False, cover="edges"): # Skip checking any conditional edge that is not satisfied - if rhs_edge.when != Spec() and not rhs_edge.parent.satisfies(rhs_edge.when): + if ( + rhs_edge.when != Spec() + and not rhs_edge.parent.satisfies(rhs_edge.when) + and not self.satisfies(rhs_edge.when) + ): continue # If we are checking for ^mpi we need to verify if there is any edge @@ -3527,13 +3533,17 @@ def satisfies(self, other: Union[str, "Spec"], deps: bool = True) -> bool: for rhs in other.traverse(root=False): # Possible lhs nodes to match this rhs node lhss = [lhs for lhs in lhs_nodes if lhs.satisfies(rhs, deps=False)] + lhs_parents = [ + lhs + for lhs in lhs_nodes + if any(lhs.satisfies(parent, deps=False) for parent in rhs.dependents()) + ] # Check whether the node needs matching (not a conditional that isn't satisfied) in_edges = [ e for e in rhs.edges_from_dependents() - if e.parent.satisfies(e.when) - or (lhss and all(lhs.satisfies(e.when) for lhs in lhss)) + if e.parent.satisfies(e.when) or any(lhp.satisfies(e.when) for lhp in lhs_parents) ] if not in_edges: continue @@ -3676,6 +3686,7 @@ def spid(spec): depflag=edge.depflag, virtuals=edge.virtuals, direct=edge.direct, + when=edge.when, ) def copy(self, deps: Union[bool, dt.DepTypes, dt.DepFlag] = True, **kwargs): diff --git a/lib/spack/spack/test/spec_semantics.py b/lib/spack/spack/test/spec_semantics.py index 70bbb7fb078..8a0f977a11c 100644 --- a/lib/spack/spack/test/spec_semantics.py +++ b/lib/spack/spack/test/spec_semantics.py @@ -327,6 +327,26 @@ class TestSpecSemantics: "ba5e334fe247335f3a116decfb5284100791dc302b5571ff5e664d8f9a6806c2" ), ), + ( + "libelf", + "%[when='^c' virtuals=c]gcc ^[when='+mpi' virtuals=mpi]mpich", + "libelf %[when='^c' virtuals=c]gcc ^[when='+mpi' virtuals=mpi]mpich", + ), + ( + "libelf %[when='^c' virtuals=c]gcc", + "%[when='^c' virtuals=c]gcc@10.3.1", + "libelf%[when='^c' virtuals=c]gcc@10.3.1", + ), + ( + "libelf %[when='^c' virtuals=c]gcc", + "%[when='^c' virtuals=c]gcc@10.3.1 ^[when='+mpi'] mpich", + "libelf%[when='^c' virtuals=c]gcc@10.3.1 ^[when='+mpi']mpich", + ), + ( + "libelf %[when='^c' virtuals=c]gcc", + "%[when='^cxx' virtuals=cxx]gcc@10.3.1", + "libelf%[when='^c' virtuals=c]gcc %[when='^cxx' virtuals=cxx]gcc@10.3.1", + ), ], ) def test_abstract_specs_can_constrain_each_other(self, lhs, rhs, expected): @@ -573,6 +593,14 @@ def test_abstract_specs_with_propagation(self, lhs, rhs, expected, constrained): c.constrain(lhs) assert c == constrained + def test_satisfies_conditional_dep(self): + concrete = spack.concretize.concretize_one("mpileaks^mpich") + assert concrete.satisfies("^[when='^mpi' virtuals=mpi]mpich") + assert concrete.satisfies("^[when='^notapackage']zmpi") + assert not concrete.satisfies("^[virtuals=blas]mpich") + assert not concrete.satisfies("^[when='^mpi' virtuals=blas]mpich") + assert not concrete.satisfies("^[when='^mpi']zmpi") + def test_satisfies_single_valued_variant(self): """Tests that the case reported in https://github.com/spack/spack/pull/2386#issuecomment-282147639