allow multiple compatible deps from CLI (#21262)
Currently, Spack can fail for a valid spec if the spec is constructed from overlapping, but not conflicting, concrete specs via the hash. For example, if abcdef and ghijkl are the hashes of specs that both depend on zlib/mnopqr, then foo ^/abcdef ^/ghijkl will fail to construct a spec, with the error message "Cannot depend on zlib... twice". This PR changes this behavior to check whether the specs are compatible before failing. With this PR, foo ^/abcdef ^/ghijkl will concretize. As a side-effect, so will foo ^zlib ^zlib and other specs that are redundant on their dependencies.
This commit is contained in:
parent
8b4b26fcbd
commit
d4b45605c8
@ -1559,8 +1559,24 @@ def _set_compiler(self, compiler):
|
|||||||
def _add_dependency(self, spec, deptypes):
|
def _add_dependency(self, spec, deptypes):
|
||||||
"""Called by the parser to add another spec as a dependency."""
|
"""Called by the parser to add another spec as a dependency."""
|
||||||
if spec.name in self._dependencies:
|
if spec.name in self._dependencies:
|
||||||
|
# allow redundant compatible dependency specifications
|
||||||
|
# depspec equality checks by name, so we need to check components
|
||||||
|
# separately to test whether the specs are identical
|
||||||
|
orig = self._dependencies[spec.name]
|
||||||
|
for dspec in orig:
|
||||||
|
if deptypes == dspec.deptypes:
|
||||||
|
try:
|
||||||
|
dspec.spec.constrain(spec)
|
||||||
|
return
|
||||||
|
except spack.error.UnsatisfiableSpecError:
|
||||||
|
raise DuplicateDependencyError(
|
||||||
|
"Cannot depend on incompatible specs '%s' and '%s'"
|
||||||
|
% (dspec.spec, spec)
|
||||||
|
)
|
||||||
|
else:
|
||||||
raise DuplicateDependencyError("Cannot depend on '%s' twice" % spec)
|
raise DuplicateDependencyError("Cannot depend on '%s' twice" % spec)
|
||||||
|
|
||||||
|
# create an edge and add to parent and child
|
||||||
self.add_dependency_edge(spec, deptypes)
|
self.add_dependency_edge(spec, deptypes)
|
||||||
|
|
||||||
def add_dependency_edge(self, dependency_spec, deptype):
|
def add_dependency_edge(self, dependency_spec, deptype):
|
||||||
|
@ -293,6 +293,12 @@ def test_canonicalize(self):
|
|||||||
|
|
||||||
self.check_parse("x ^y", "x@: ^y@:")
|
self.check_parse("x ^y", "x@: ^y@:")
|
||||||
|
|
||||||
|
def test_parse_redundant_deps(self):
|
||||||
|
self.check_parse("x ^y@foo", "x ^y@foo ^y@foo")
|
||||||
|
self.check_parse("x ^y@foo+bar", "x ^y@foo ^y+bar")
|
||||||
|
self.check_parse("x ^y@foo+bar", "x ^y@foo+bar ^y")
|
||||||
|
self.check_parse("x ^y@foo+bar", "x ^y ^y@foo+bar")
|
||||||
|
|
||||||
def test_parse_errors(self):
|
def test_parse_errors(self):
|
||||||
errors = ["x@@1.2", "x ^y@@1.2", "x@1.2::", "x::"]
|
errors = ["x@@1.2", "x ^y@@1.2", "x@1.2::", "x::"]
|
||||||
self._check_raises(SpecParseError, errors)
|
self._check_raises(SpecParseError, errors)
|
||||||
@ -481,7 +487,7 @@ def test_multiple_versions(self):
|
|||||||
self._check_raises(MultipleVersionError, multiples)
|
self._check_raises(MultipleVersionError, multiples)
|
||||||
|
|
||||||
def test_duplicate_dependency(self):
|
def test_duplicate_dependency(self):
|
||||||
self._check_raises(DuplicateDependencyError, ["x ^y ^y"])
|
self._check_raises(DuplicateDependencyError, ["x ^y@1 ^y@2"])
|
||||||
|
|
||||||
def test_duplicate_compiler(self):
|
def test_duplicate_compiler(self):
|
||||||
duplicates = [
|
duplicates = [
|
||||||
|
Loading…
Reference in New Issue
Block a user