explicit splice: do not fail for bad config replacement if target not matched (#46925)

Originally, concretization failed if the splice config points to an invalid replacement.

This PR defers the check until we know the splice is needed, so that irrelevant splices
with bad config cannot stop concretization.

While I was at it, I improved an error message from an assert to a ValueError.
This commit is contained in:
Greg Becker 2024-10-27 11:35:10 -07:00 committed by GitHub
parent fea2171672
commit 47e70c5c3a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 42 additions and 2 deletions

View File

@ -3829,8 +3829,16 @@ def execute_explicit_splices(self):
for splice_set in splice_config: for splice_set in splice_config:
target = splice_set["target"] target = splice_set["target"]
replacement = spack.spec.Spec(splice_set["replacement"]) replacement = spack.spec.Spec(splice_set["replacement"])
assert replacement.abstract_hash
replacement.replace_hash() if not replacement.abstract_hash:
location = getattr(
splice_set["replacement"], "_start_mark", " at unknown line number"
)
msg = f"Explicit splice replacement '{replacement}' does not include a hash.\n"
msg += f"{location}\n\n"
msg += " Splice replacements must be specified by hash"
raise InvalidSpliceError(msg)
transitive = splice_set.get("transitive", False) transitive = splice_set.get("transitive", False)
splice_triples.append((target, replacement, transitive)) splice_triples.append((target, replacement, transitive))
@ -3841,6 +3849,10 @@ def execute_explicit_splices(self):
if target in current_spec: if target in current_spec:
# matches root or non-root # matches root or non-root
# e.g. mvapich2%gcc # e.g. mvapich2%gcc
# The first iteration, we need to replace the abstract hash
if not replacement.concrete:
replacement.replace_hash()
current_spec = current_spec.splice(replacement, transitive) current_spec = current_spec.splice(replacement, transitive)
new_key = NodeArgument(id=key.id, pkg=current_spec.name) new_key = NodeArgument(id=key.id, pkg=current_spec.name)
specs[new_key] = current_spec specs[new_key] = current_spec
@ -4226,3 +4238,7 @@ def __init__(self, provided, conflicts):
# Add attribute expected of the superclass interface # Add attribute expected of the superclass interface
self.required = None self.required = None
self.constraint_type = None self.constraint_type = None
class InvalidSpliceError(spack.error.SpackError):
"""For cases in which the splice configuration is invalid."""

View File

@ -2306,6 +2306,30 @@ def test_explicit_splices(
assert "hdf5 ^zmpi" in captured.err assert "hdf5 ^zmpi" in captured.err
assert str(spec) in captured.err assert str(spec) in captured.err
def test_explicit_splice_fails_nonexistent(mutable_config, mock_packages, mock_store):
splice_info = {"target": "mpi", "replacement": "mpich/doesnotexist"}
spack.config.CONFIG.set("concretizer", {"splice": {"explicit": [splice_info]}})
with pytest.raises(spack.spec.InvalidHashError):
_ = spack.spec.Spec("hdf5^zmpi").concretized()
def test_explicit_splice_fails_no_hash(mutable_config, mock_packages, mock_store):
splice_info = {"target": "mpi", "replacement": "mpich"}
spack.config.CONFIG.set("concretizer", {"splice": {"explicit": [splice_info]}})
with pytest.raises(spack.solver.asp.InvalidSpliceError, match="must be specified by hash"):
_ = spack.spec.Spec("hdf5^zmpi").concretized()
def test_explicit_splice_non_match_nonexistent_succeeds(
mutable_config, mock_packages, mock_store
):
"""When we have a nonexistent splice configured but are not using it, don't fail."""
splice_info = {"target": "will_not_match", "replacement": "nonexistent/doesnotexist"}
spack.config.CONFIG.set("concretizer", {"splice": {"explicit": [splice_info]}})
spec = spack.spec.Spec("zlib").concretized()
# the main test is that it does not raise
assert not spec.spliced
@pytest.mark.db @pytest.mark.db
@pytest.mark.parametrize( @pytest.mark.parametrize(
"spec_str,mpi_name", "spec_str,mpi_name",