Fix wrong hash in spliced specs, and improve unit-tests (#48574)
Modifications: * Fix a severe bug where a spliced spec has the same hash as the original build spec * Removed CacheManager in favor of install_mockery. The latter sets up a temporary store, and removes it at the end of the test. * Deleted a couple of helper functions e.g. _has_dependencies * Checked that SolverException is raised, rather than Exception (more specific) * Extended, and renamed the splicing_setup fixture (now called install_specs) * Added more specific assertions in each test One test is currently flaky, due to some instability when sorting multiple specs. It's currently marked xfail
This commit is contained in:
parent
00e804a94b
commit
59a71959e7
@ -3653,6 +3653,7 @@ def _resolve_automatic_splices(self):
|
|||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
new_spec = spec.copy(deps=False)
|
new_spec = spec.copy(deps=False)
|
||||||
|
new_spec.clear_caches(ignore=("package_hash",))
|
||||||
new_spec.build_spec = spec
|
new_spec.build_spec = spec
|
||||||
for edge in spec.edges_to_dependencies():
|
for edge in spec.edges_to_dependencies():
|
||||||
depflag = edge.depflag & ~dt.BUILD
|
depflag = edge.depflag & ~dt.BUILD
|
||||||
|
@ -3591,25 +3591,16 @@ def patches(self):
|
|||||||
|
|
||||||
return self._patches
|
return self._patches
|
||||||
|
|
||||||
def _dup(self, other, deps: Union[bool, dt.DepTypes, dt.DepFlag] = True, cleardeps=True):
|
def _dup(self, other: "Spec", deps: Union[bool, dt.DepTypes, dt.DepFlag] = True) -> bool:
|
||||||
"""Copy the spec other into self. This is an overwriting
|
"""Copies "other" into self, by overwriting all attributes.
|
||||||
copy. It does not copy any dependents (parents), but by default
|
|
||||||
copies dependencies.
|
|
||||||
|
|
||||||
To duplicate an entire DAG, call _dup() on the root of the DAG.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
other (Spec): spec to be copied onto ``self``
|
other: spec to be copied onto ``self``
|
||||||
deps: if True copies all the dependencies. If
|
deps: if True copies all the dependencies. If False copies None.
|
||||||
False copies None. If deptype/depflag, copy matching types.
|
If deptype, or depflag, copy matching types.
|
||||||
cleardeps (bool): if True clears the dependencies of ``self``,
|
|
||||||
before possibly copying the dependencies of ``other`` onto
|
|
||||||
``self``
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if ``self`` changed because of the copy operation,
|
True if ``self`` changed because of the copy operation, False otherwise.
|
||||||
False otherwise.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# We don't count dependencies as changes here
|
# We don't count dependencies as changes here
|
||||||
changed = True
|
changed = True
|
||||||
@ -3634,14 +3625,15 @@ def _dup(self, other, deps: Union[bool, dt.DepTypes, dt.DepFlag] = True, clearde
|
|||||||
self.versions = other.versions.copy()
|
self.versions = other.versions.copy()
|
||||||
self.architecture = other.architecture.copy() if other.architecture else None
|
self.architecture = other.architecture.copy() if other.architecture else None
|
||||||
self.compiler = other.compiler.copy() if other.compiler else None
|
self.compiler = other.compiler.copy() if other.compiler else None
|
||||||
if cleardeps:
|
|
||||||
self._dependents = _EdgeMap(store_by_child=False)
|
|
||||||
self._dependencies = _EdgeMap(store_by_child=True)
|
|
||||||
self.compiler_flags = other.compiler_flags.copy()
|
self.compiler_flags = other.compiler_flags.copy()
|
||||||
self.compiler_flags.spec = self
|
self.compiler_flags.spec = self
|
||||||
self.variants = other.variants.copy()
|
self.variants = other.variants.copy()
|
||||||
self._build_spec = other._build_spec
|
self._build_spec = other._build_spec
|
||||||
|
|
||||||
|
# Clear dependencies
|
||||||
|
self._dependents = _EdgeMap(store_by_child=False)
|
||||||
|
self._dependencies = _EdgeMap(store_by_child=True)
|
||||||
|
|
||||||
# FIXME: we manage _patches_in_order_of_appearance specially here
|
# FIXME: we manage _patches_in_order_of_appearance specially here
|
||||||
# to keep it from leaking out of spec.py, but we should figure
|
# to keep it from leaking out of spec.py, but we should figure
|
||||||
# out how to handle it more elegantly in the Variant classes.
|
# out how to handle it more elegantly in the Variant classes.
|
||||||
@ -4524,7 +4516,7 @@ def mask_build_deps(in_spec):
|
|||||||
|
|
||||||
return spec
|
return spec
|
||||||
|
|
||||||
def clear_caches(self, ignore=()):
|
def clear_caches(self, ignore: Tuple[str, ...] = ()) -> None:
|
||||||
"""
|
"""
|
||||||
Clears all cached hashes in a Spec, while preserving other properties.
|
Clears all cached hashes in a Spec, while preserving other properties.
|
||||||
"""
|
"""
|
||||||
|
@ -10,33 +10,11 @@
|
|||||||
import spack.concretize
|
import spack.concretize
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.deptypes as dt
|
import spack.deptypes as dt
|
||||||
import spack.solver.asp
|
|
||||||
from spack.installer import PackageInstaller
|
from spack.installer import PackageInstaller
|
||||||
|
from spack.solver.asp import SolverError
|
||||||
from spack.spec import Spec
|
from spack.spec import Spec
|
||||||
|
|
||||||
|
|
||||||
class CacheManager:
|
|
||||||
def __init__(self, specs: List[str]) -> None:
|
|
||||||
self.req_specs = specs
|
|
||||||
self.concr_specs: List[Spec]
|
|
||||||
self.concr_specs = []
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.concr_specs = [spack.concretize.concretize_one(s) for s in self.req_specs]
|
|
||||||
for s in self.concr_specs:
|
|
||||||
PackageInstaller([s.package], fake=True, explicit=True).install()
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
||||||
for s in self.concr_specs:
|
|
||||||
s.package.do_uninstall()
|
|
||||||
|
|
||||||
|
|
||||||
# MacOS and Windows only work if you pass this function pointer rather than a
|
|
||||||
# closure
|
|
||||||
def _mock_has_runtime_dependencies(_x):
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _make_specs_non_buildable(specs: List[str]):
|
def _make_specs_non_buildable(specs: List[str]):
|
||||||
output_config = {}
|
output_config = {}
|
||||||
for spec in specs:
|
for spec in specs:
|
||||||
@ -45,203 +23,263 @@ def _make_specs_non_buildable(specs: List[str]):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def splicing_setup(mutable_database, mock_packages, monkeypatch):
|
def install_specs(
|
||||||
spack.config.set("concretizer:reuse", True)
|
mutable_database,
|
||||||
monkeypatch.setattr(
|
mock_packages,
|
||||||
spack.solver.asp, "_has_runtime_dependencies", _mock_has_runtime_dependencies
|
mutable_config,
|
||||||
)
|
do_not_check_runtimes_on_reuse,
|
||||||
|
install_mockery,
|
||||||
|
):
|
||||||
|
"""Returns a function that concretizes and installs a list of abstract specs"""
|
||||||
|
mutable_config.set("concretizer:reuse", True)
|
||||||
|
|
||||||
|
def _impl(*specs_str):
|
||||||
|
concrete_specs = [Spec(s).concretized() for s in specs_str]
|
||||||
|
PackageInstaller([s.package for s in concrete_specs], fake=True, explicit=True).install()
|
||||||
|
return concrete_specs
|
||||||
|
|
||||||
|
return _impl
|
||||||
|
|
||||||
|
|
||||||
def _enable_splicing():
|
def _enable_splicing():
|
||||||
spack.config.set("concretizer:splice", {"automatic": True})
|
spack.config.set("concretizer:splice", {"automatic": True})
|
||||||
|
|
||||||
|
|
||||||
def _has_build_dependency(spec: Spec, name: str):
|
@pytest.mark.parametrize("spec_str", ["splice-z", "splice-h@1"])
|
||||||
return any(s.name == name for s in spec.dependencies(None, dt.BUILD))
|
def test_spec_reuse(spec_str, install_specs, mutable_config):
|
||||||
|
"""Tests reuse of splice-z, without splicing, as a root and as a dependency of splice-h"""
|
||||||
|
splice_z = install_specs("splice-z@1.0.0+compat")[0]
|
||||||
|
mutable_config.set("packages", _make_specs_non_buildable(["splice-z"]))
|
||||||
|
concrete = spack.concretize.concretize_one(spec_str)
|
||||||
|
assert concrete["splice-z"].satisfies(splice_z)
|
||||||
|
|
||||||
|
|
||||||
def test_simple_reuse(splicing_setup):
|
@pytest.mark.regression("48578")
|
||||||
with CacheManager(["splice-z@1.0.0+compat"]):
|
def test_splice_installed_hash(install_specs, mutable_config):
|
||||||
spack.config.set("packages", _make_specs_non_buildable(["splice-z"]))
|
"""Tests splicing the dependency of an installed spec, for another installed spec"""
|
||||||
assert spack.concretize.concretize_one("splice-z").satisfies(Spec("splice-z"))
|
splice_t, splice_h = install_specs(
|
||||||
|
|
||||||
|
|
||||||
def test_simple_dep_reuse(splicing_setup):
|
|
||||||
with CacheManager(["splice-z@1.0.0+compat"]):
|
|
||||||
spack.config.set("packages", _make_specs_non_buildable(["splice-z"]))
|
|
||||||
assert spack.concretize.concretize_one("splice-h@1").satisfies(Spec("splice-h@1"))
|
|
||||||
|
|
||||||
|
|
||||||
def test_splice_installed_hash(splicing_setup):
|
|
||||||
cache = [
|
|
||||||
"splice-t@1 ^splice-h@1.0.0+compat ^splice-z@1.0.0",
|
"splice-t@1 ^splice-h@1.0.0+compat ^splice-z@1.0.0",
|
||||||
"splice-h@1.0.2+compat ^splice-z@1.0.0",
|
"splice-h@1.0.2+compat ^splice-z@1.0.0",
|
||||||
]
|
)
|
||||||
with CacheManager(cache):
|
packages_config = _make_specs_non_buildable(["splice-t", "splice-h"])
|
||||||
packages_config = _make_specs_non_buildable(["splice-t", "splice-h"])
|
mutable_config.set("packages", packages_config)
|
||||||
spack.config.set("packages", packages_config)
|
|
||||||
goal_spec = Spec("splice-t@1 ^splice-h@1.0.2+compat ^splice-z@1.0.0")
|
goal_spec = "splice-t@1 ^splice-h@1.0.2+compat ^splice-z@1.0.0"
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(SolverError):
|
||||||
spack.concretize.concretize_one(goal_spec)
|
spack.concretize.concretize_one(goal_spec)
|
||||||
_enable_splicing()
|
_enable_splicing()
|
||||||
assert spack.concretize.concretize_one(goal_spec).satisfies(goal_spec)
|
concrete = spack.concretize.concretize_one(goal_spec)
|
||||||
|
|
||||||
|
# splice-t has a dependency that is changing, thus its hash should be different
|
||||||
|
assert concrete.dag_hash() != splice_t.dag_hash()
|
||||||
|
assert concrete.build_spec.satisfies(splice_t)
|
||||||
|
assert not concrete.satisfies(splice_t)
|
||||||
|
|
||||||
|
# splice-h is reused, so the hash should stay the same
|
||||||
|
assert concrete["splice-h"].satisfies(splice_h)
|
||||||
|
assert concrete["splice-h"].build_spec.satisfies(splice_h)
|
||||||
|
assert concrete["splice-h"].dag_hash() == splice_h.dag_hash()
|
||||||
|
|
||||||
|
|
||||||
def test_splice_build_splice_node(splicing_setup):
|
def test_splice_build_splice_node(install_specs, mutable_config):
|
||||||
with CacheManager(["splice-t@1 ^splice-h@1.0.0+compat ^splice-z@1.0.0+compat"]):
|
"""Tests splicing the dependency of an installed spec, for a spec that is yet to be built"""
|
||||||
spack.config.set("packages", _make_specs_non_buildable(["splice-t"]))
|
splice_t = install_specs("splice-t@1 ^splice-h@1.0.0+compat ^splice-z@1.0.0+compat")[0]
|
||||||
goal_spec = Spec("splice-t@1 ^splice-h@1.0.2+compat ^splice-z@1.0.0+compat")
|
mutable_config.set("packages", _make_specs_non_buildable(["splice-t"]))
|
||||||
with pytest.raises(Exception):
|
|
||||||
spack.concretize.concretize_one(goal_spec)
|
goal_spec = "splice-t@1 ^splice-h@1.0.2+compat ^splice-z@1.0.0+compat"
|
||||||
_enable_splicing()
|
with pytest.raises(SolverError):
|
||||||
assert spack.concretize.concretize_one(goal_spec).satisfies(goal_spec)
|
spack.concretize.concretize_one(goal_spec)
|
||||||
|
|
||||||
|
_enable_splicing()
|
||||||
|
concrete = spack.concretize.concretize_one(goal_spec)
|
||||||
|
|
||||||
|
# splice-t has a dependency that is changing, thus its hash should be different
|
||||||
|
assert concrete.dag_hash() != splice_t.dag_hash()
|
||||||
|
assert concrete.build_spec.satisfies(splice_t)
|
||||||
|
assert not concrete.satisfies(splice_t)
|
||||||
|
|
||||||
|
# splice-h should be different
|
||||||
|
assert concrete["splice-h"].dag_hash() != splice_t["splice-h"].dag_hash()
|
||||||
|
assert concrete["splice-h"].build_spec.dag_hash() == concrete["splice-h"].dag_hash()
|
||||||
|
|
||||||
|
|
||||||
def test_double_splice(splicing_setup):
|
@pytest.mark.xfail(reason="the spliced splice-h has sometimes the original splice-h hash")
|
||||||
cache = [
|
def test_double_splice(install_specs, mutable_config):
|
||||||
|
"""Tests splicing two dependencies of an installed spec, for other installed specs"""
|
||||||
|
splice_t, splice_h, splice_z = install_specs(
|
||||||
"splice-t@1 ^splice-h@1.0.0+compat ^splice-z@1.0.0+compat",
|
"splice-t@1 ^splice-h@1.0.0+compat ^splice-z@1.0.0+compat",
|
||||||
"splice-h@1.0.2+compat ^splice-z@1.0.1+compat",
|
"splice-h@1.0.2+compat ^splice-z@1.0.1+compat",
|
||||||
"splice-z@1.0.2+compat",
|
"splice-z@1.0.2+compat",
|
||||||
]
|
)
|
||||||
with CacheManager(cache):
|
mutable_config.set("packages", _make_specs_non_buildable(["splice-t", "splice-h", "splice-z"]))
|
||||||
freeze_builds_config = _make_specs_non_buildable(["splice-t", "splice-h", "splice-z"])
|
|
||||||
spack.config.set("packages", freeze_builds_config)
|
goal_spec = "splice-t@1 ^splice-h@1.0.2+compat ^splice-z@1.0.2+compat"
|
||||||
goal_spec = Spec("splice-t@1 ^splice-h@1.0.2+compat ^splice-z@1.0.2+compat")
|
with pytest.raises(SolverError):
|
||||||
with pytest.raises(Exception):
|
spack.concretize.concretize_one(goal_spec)
|
||||||
spack.concretize.concretize_one(goal_spec)
|
|
||||||
_enable_splicing()
|
_enable_splicing()
|
||||||
assert spack.concretize.concretize_one(goal_spec).satisfies(goal_spec)
|
concrete = spack.concretize.concretize_one(goal_spec)
|
||||||
|
|
||||||
|
# splice-t and splice-h have a dependency that is changing, thus its hash should be different
|
||||||
|
assert concrete.dag_hash() != splice_t.dag_hash()
|
||||||
|
assert concrete.build_spec.satisfies(splice_t)
|
||||||
|
assert not concrete.satisfies(splice_t)
|
||||||
|
|
||||||
|
assert concrete["splice-h"].dag_hash() != splice_h.dag_hash()
|
||||||
|
assert concrete["splice-h"].build_spec.satisfies(splice_h)
|
||||||
|
assert not concrete["splice-h"].satisfies(splice_h)
|
||||||
|
|
||||||
|
# splice-z is reused, so the hash should stay the same
|
||||||
|
assert concrete["splice-z"].dag_hash() == splice_z.dag_hash()
|
||||||
|
|
||||||
|
|
||||||
# The next two tests are mirrors of one another
|
@pytest.mark.parametrize(
|
||||||
def test_virtual_multi_splices_in(splicing_setup):
|
"original_spec,goal_spec",
|
||||||
cache = [
|
[
|
||||||
"depends-on-virtual-with-abi ^virtual-abi-1",
|
# `virtual-abi-1` can be spliced for `virtual-abi-multi abi=one` and vice-versa
|
||||||
"depends-on-virtual-with-abi ^virtual-abi-2",
|
(
|
||||||
]
|
"depends-on-virtual-with-abi ^virtual-abi-1",
|
||||||
goal_specs = [
|
"depends-on-virtual-with-abi ^virtual-abi-multi abi=one",
|
||||||
"depends-on-virtual-with-abi ^virtual-abi-multi abi=one",
|
),
|
||||||
"depends-on-virtual-with-abi ^virtual-abi-multi abi=two",
|
(
|
||||||
]
|
"depends-on-virtual-with-abi ^virtual-abi-multi abi=one",
|
||||||
with CacheManager(cache):
|
"depends-on-virtual-with-abi ^virtual-abi-1",
|
||||||
spack.config.set("packages", _make_specs_non_buildable(["depends-on-virtual-with-abi"]))
|
),
|
||||||
for gs in goal_specs:
|
# `virtual-abi-2` can be spliced for `virtual-abi-multi abi=two` and vice-versa
|
||||||
with pytest.raises(Exception):
|
(
|
||||||
spack.concretize.concretize_one(gs)
|
"depends-on-virtual-with-abi ^virtual-abi-2",
|
||||||
_enable_splicing()
|
"depends-on-virtual-with-abi ^virtual-abi-multi abi=two",
|
||||||
for gs in goal_specs:
|
),
|
||||||
assert spack.concretize.concretize_one(gs).satisfies(gs)
|
(
|
||||||
|
"depends-on-virtual-with-abi ^virtual-abi-multi abi=two",
|
||||||
|
"depends-on-virtual-with-abi ^virtual-abi-2",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_virtual_multi_splices_in(original_spec, goal_spec, install_specs, mutable_config):
|
||||||
|
"""Tests that we can splice a virtual dependency with a different, but compatible, provider."""
|
||||||
|
original = install_specs(original_spec)[0]
|
||||||
|
mutable_config.set("packages", _make_specs_non_buildable(["depends-on-virtual-with-abi"]))
|
||||||
|
|
||||||
|
with pytest.raises(SolverError):
|
||||||
|
spack.concretize.concretize_one(goal_spec)
|
||||||
|
|
||||||
|
_enable_splicing()
|
||||||
|
spliced = spack.concretize.concretize_one(goal_spec)
|
||||||
|
|
||||||
|
assert spliced.dag_hash() != original.dag_hash()
|
||||||
|
assert spliced.build_spec.dag_hash() == original.dag_hash()
|
||||||
|
assert spliced["virtual-with-abi"].name != spliced.build_spec["virtual-with-abi"].name
|
||||||
|
|
||||||
|
|
||||||
def test_virtual_multi_can_be_spliced(splicing_setup):
|
@pytest.mark.parametrize(
|
||||||
cache = [
|
"original_spec,goal_spec",
|
||||||
"depends-on-virtual-with-abi ^virtual-abi-multi abi=one",
|
[
|
||||||
"depends-on-virtual-with-abi ^virtual-abi-multi abi=two",
|
|
||||||
]
|
|
||||||
goal_specs = [
|
|
||||||
"depends-on-virtual-with-abi ^virtual-abi-1",
|
|
||||||
"depends-on-virtual-with-abi ^virtual-abi-2",
|
|
||||||
]
|
|
||||||
with CacheManager(cache):
|
|
||||||
spack.config.set("packages", _make_specs_non_buildable(["depends-on-virtual-with-abi"]))
|
|
||||||
for gs in goal_specs:
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
spack.concretize.concretize_one(gs)
|
|
||||||
_enable_splicing()
|
|
||||||
for gs in goal_specs:
|
|
||||||
assert spack.concretize.concretize_one(gs).satisfies(gs)
|
|
||||||
|
|
||||||
|
|
||||||
def test_manyvariant_star_matching_variant_splice(splicing_setup):
|
|
||||||
cache = [
|
|
||||||
# can_splice("manyvariants@1.0.0", when="@1.0.1", match_variants="*")
|
# can_splice("manyvariants@1.0.0", when="@1.0.1", match_variants="*")
|
||||||
"depends-on-manyvariants ^manyvariants@1.0.0+a+b c=v1 d=v2",
|
(
|
||||||
"depends-on-manyvariants ^manyvariants@1.0.0~a~b c=v3 d=v3",
|
"depends-on-manyvariants ^manyvariants@1.0.0+a+b c=v1 d=v2",
|
||||||
]
|
"depends-on-manyvariants ^manyvariants@1.0.1+a+b c=v1 d=v2",
|
||||||
goal_specs = [
|
),
|
||||||
Spec("depends-on-manyvariants ^manyvariants@1.0.1+a+b c=v1 d=v2"),
|
(
|
||||||
Spec("depends-on-manyvariants ^manyvariants@1.0.1~a~b c=v3 d=v3"),
|
"depends-on-manyvariants ^manyvariants@1.0.0~a~b c=v3 d=v3",
|
||||||
]
|
"depends-on-manyvariants ^manyvariants@1.0.1~a~b c=v3 d=v3",
|
||||||
with CacheManager(cache):
|
),
|
||||||
freeze_build_config = {"depends-on-manyvariants": {"buildable": False}}
|
|
||||||
spack.config.set("packages", freeze_build_config)
|
|
||||||
for goal in goal_specs:
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
spack.concretize.concretize_one(goal)
|
|
||||||
_enable_splicing()
|
|
||||||
for goal in goal_specs:
|
|
||||||
assert spack.concretize.concretize_one(goal).satisfies(goal)
|
|
||||||
|
|
||||||
|
|
||||||
def test_manyvariant_limited_matching(splicing_setup):
|
|
||||||
cache = [
|
|
||||||
# can_splice("manyvariants@2.0.0+a~b", when="@2.0.1~a+b", match_variants=["c", "d"])
|
# can_splice("manyvariants@2.0.0+a~b", when="@2.0.1~a+b", match_variants=["c", "d"])
|
||||||
"depends-on-manyvariants@2.0 ^manyvariants@2.0.0+a~b c=v3 d=v2",
|
(
|
||||||
|
"depends-on-manyvariants@2.0 ^manyvariants@2.0.0+a~b c=v3 d=v2",
|
||||||
|
"depends-on-manyvariants@2.0 ^manyvariants@2.0.1~a+b c=v3 d=v2",
|
||||||
|
),
|
||||||
# can_splice("manyvariants@2.0.0 c=v1 d=v1", when="@2.0.1+a+b")
|
# can_splice("manyvariants@2.0.0 c=v1 d=v1", when="@2.0.1+a+b")
|
||||||
"depends-on-manyvariants@2.0 ^manyvariants@2.0.0~a~b c=v1 d=v1",
|
(
|
||||||
]
|
"depends-on-manyvariants@2.0 ^manyvariants@2.0.0~a~b c=v1 d=v1",
|
||||||
goal_specs = [
|
"depends-on-manyvariants@2.0 ^manyvariants@2.0.1+a+b c=v3 d=v3",
|
||||||
Spec("depends-on-manyvariants@2.0 ^manyvariants@2.0.1~a+b c=v3 d=v2"),
|
),
|
||||||
Spec("depends-on-manyvariants@2.0 ^manyvariants@2.0.1+a+b c=v3 d=v3"),
|
],
|
||||||
]
|
)
|
||||||
with CacheManager(cache):
|
def test_manyvariant_matching_variant_splice(
|
||||||
freeze_build_config = {"depends-on-manyvariants": {"buildable": False}}
|
original_spec, goal_spec, install_specs, mutable_config
|
||||||
spack.config.set("packages", freeze_build_config)
|
):
|
||||||
for s in goal_specs:
|
"""Tests splicing with different kind of matching on variants"""
|
||||||
with pytest.raises(Exception):
|
original = install_specs(original_spec)[0]
|
||||||
spack.concretize.concretize_one(s)
|
mutable_config.set("packages", {"depends-on-manyvariants": {"buildable": False}})
|
||||||
_enable_splicing()
|
|
||||||
for s in goal_specs:
|
with pytest.raises(SolverError):
|
||||||
assert spack.concretize.concretize_one(s).satisfies(s)
|
spack.concretize.concretize_one(goal_spec)
|
||||||
|
|
||||||
|
_enable_splicing()
|
||||||
|
spliced = spack.concretize.concretize_one(goal_spec)
|
||||||
|
|
||||||
|
assert spliced.dag_hash() != original.dag_hash()
|
||||||
|
assert spliced.build_spec.dag_hash() == original.dag_hash()
|
||||||
|
|
||||||
|
# The spliced 'manyvariants' is yet to be built
|
||||||
|
assert spliced["manyvariants"].dag_hash() != original["manyvariants"].dag_hash()
|
||||||
|
assert spliced["manyvariants"].build_spec.dag_hash() == spliced["manyvariants"].dag_hash()
|
||||||
|
|
||||||
|
|
||||||
def test_external_splice_same_name(splicing_setup):
|
def test_external_splice_same_name(install_specs, mutable_config):
|
||||||
cache = [
|
"""Tests that externals can be spliced for non-external specs"""
|
||||||
|
original_splice_h, original_splice_t = install_specs(
|
||||||
"splice-h@1.0.0 ^splice-z@1.0.0+compat",
|
"splice-h@1.0.0 ^splice-z@1.0.0+compat",
|
||||||
"splice-t@1.0 ^splice-h@1.0.1 ^splice-z@1.0.1+compat",
|
"splice-t@1.0 ^splice-h@1.0.1 ^splice-z@1.0.1+compat",
|
||||||
]
|
)
|
||||||
packages_yaml = {
|
mutable_config.set("packages", _make_specs_non_buildable(["splice-t", "splice-h"]))
|
||||||
"splice-z": {"externals": [{"spec": "splice-z@1.0.2+compat", "prefix": "/usr"}]}
|
mutable_config.set(
|
||||||
}
|
"packages",
|
||||||
goal_specs = [
|
{
|
||||||
Spec("splice-h@1.0.0 ^splice-z@1.0.2"),
|
"splice-z": {
|
||||||
Spec("splice-t@1.0 ^splice-h@1.0.1 ^splice-z@1.0.2"),
|
"externals": [{"spec": "splice-z@1.0.2+compat", "prefix": "/usr"}],
|
||||||
]
|
"buildable": False,
|
||||||
with CacheManager(cache):
|
}
|
||||||
spack.config.set("packages", packages_yaml)
|
},
|
||||||
_enable_splicing()
|
)
|
||||||
for s in goal_specs:
|
|
||||||
assert spack.concretize.concretize_one(s).satisfies(s)
|
_enable_splicing()
|
||||||
|
concrete_splice_h = spack.concretize.concretize_one("splice-h@1.0.0 ^splice-z@1.0.2")
|
||||||
|
concrete_splice_t = spack.concretize.concretize_one(
|
||||||
|
"splice-t@1.0 ^splice-h@1.0.1 ^splice-z@1.0.2"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert concrete_splice_h.dag_hash() != original_splice_h.dag_hash()
|
||||||
|
assert concrete_splice_h.build_spec.dag_hash() == original_splice_h.dag_hash()
|
||||||
|
assert concrete_splice_h["splice-z"].external
|
||||||
|
|
||||||
|
assert concrete_splice_t.dag_hash() != original_splice_t.dag_hash()
|
||||||
|
assert concrete_splice_t.build_spec.dag_hash() == original_splice_t.dag_hash()
|
||||||
|
assert concrete_splice_t["splice-z"].external
|
||||||
|
|
||||||
|
assert concrete_splice_t["splice-z"].dag_hash() == concrete_splice_h["splice-z"].dag_hash()
|
||||||
|
|
||||||
|
|
||||||
def test_spliced_build_deps_only_in_build_spec(splicing_setup):
|
def test_spliced_build_deps_only_in_build_spec(install_specs):
|
||||||
cache = ["splice-t@1.0 ^splice-h@1.0.1 ^splice-z@1.0.0"]
|
"""Tests that build specs are not reported in the spliced spec"""
|
||||||
goal_spec = Spec("splice-t@1.0 ^splice-h@1.0.2 ^splice-z@1.0.0")
|
install_specs("splice-t@1.0 ^splice-h@1.0.1 ^splice-z@1.0.0")
|
||||||
|
|
||||||
with CacheManager(cache):
|
_enable_splicing()
|
||||||
_enable_splicing()
|
spliced = spack.concretize.concretize_one("splice-t@1.0 ^splice-h@1.0.2 ^splice-z@1.0.0")
|
||||||
concr_goal = spack.concretize.concretize_one(goal_spec)
|
build_spec = spliced.build_spec
|
||||||
build_spec = concr_goal._build_spec
|
|
||||||
# Spec has been spliced
|
# Spec has been spliced
|
||||||
assert build_spec is not None
|
assert build_spec.dag_hash() != spliced.dag_hash()
|
||||||
# Build spec has spliced build dependencies
|
# Build spec has spliced build dependencies
|
||||||
assert _has_build_dependency(build_spec, "splice-h")
|
assert build_spec.dependencies("splice-h", dt.BUILD)
|
||||||
assert _has_build_dependency(build_spec, "splice-z")
|
assert build_spec.dependencies("splice-z", dt.BUILD)
|
||||||
# Spliced build dependencies are removed
|
# Spliced build dependencies are removed
|
||||||
assert len(concr_goal.dependencies(None, dt.BUILD)) == 0
|
assert len(spliced.dependencies(None, dt.BUILD)) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_spliced_transitive_dependency(splicing_setup):
|
def test_spliced_transitive_dependency(install_specs, mutable_config):
|
||||||
cache = ["splice-depends-on-t@1.0 ^splice-h@1.0.1"]
|
"""Tests that build specs are not reported, even for spliced transitive dependencies"""
|
||||||
goal_spec = Spec("splice-depends-on-t^splice-h@1.0.2")
|
install_specs("splice-depends-on-t@1.0 ^splice-h@1.0.1")
|
||||||
|
mutable_config.set("packages", _make_specs_non_buildable(["splice-depends-on-t"]))
|
||||||
|
|
||||||
with CacheManager(cache):
|
_enable_splicing()
|
||||||
spack.config.set("packages", _make_specs_non_buildable(["splice-depends-on-t"]))
|
spliced = spack.concretize.concretize_one("splice-depends-on-t^splice-h@1.0.2")
|
||||||
_enable_splicing()
|
|
||||||
concr_goal = spack.concretize.concretize_one(goal_spec)
|
# Spec has been spliced
|
||||||
# Spec has been spliced
|
assert spliced.build_spec.dag_hash() != spliced.dag_hash()
|
||||||
assert concr_goal._build_spec is not None
|
assert spliced["splice-t"].build_spec.dag_hash() != spliced["splice-t"].dag_hash()
|
||||||
assert concr_goal["splice-t"]._build_spec is not None
|
|
||||||
assert concr_goal.satisfies(goal_spec)
|
# Spliced build dependencies are removed
|
||||||
# Spliced build dependencies are removed
|
assert len(spliced.dependencies(None, dt.BUILD)) == 0
|
||||||
assert len(concr_goal.dependencies(None, dt.BUILD)) == 0
|
assert len(spliced["splice-t"].dependencies(None, dt.BUILD)) == 0
|
||||||
|
@ -2125,15 +2125,7 @@ def configure_reuse(reuse_mode, combined_env) -> Optional[ev.Environment]:
|
|||||||
"from_environment_raise",
|
"from_environment_raise",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_env_include_concrete_reuse(monkeypatch, reuse_mode):
|
def test_env_include_concrete_reuse(do_not_check_runtimes_on_reuse, reuse_mode):
|
||||||
|
|
||||||
# The mock packages do not use the gcc-runtime
|
|
||||||
def mock_has_runtime_dependencies(*args, **kwargs):
|
|
||||||
return True
|
|
||||||
|
|
||||||
monkeypatch.setattr(
|
|
||||||
spack.solver.asp, "_has_runtime_dependencies", mock_has_runtime_dependencies
|
|
||||||
)
|
|
||||||
# The default mpi version is 3.x provided by mpich in the mock repo.
|
# The default mpi version is 3.x provided by mpich in the mock repo.
|
||||||
# This test verifies that concretizing with an included concrete
|
# This test verifies that concretizing with an included concrete
|
||||||
# environment with "concretizer:reuse:true" the included
|
# environment with "concretizer:reuse:true" the included
|
||||||
|
@ -3122,14 +3122,13 @@ def test_concretization_version_order():
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("mutable_database", "mock_store")
|
@pytest.mark.usefixtures("mutable_database", "mock_store", "do_not_check_runtimes_on_reuse")
|
||||||
@pytest.mark.not_on_windows("Expected length is different on Windows")
|
@pytest.mark.not_on_windows("Expected length is different on Windows")
|
||||||
def test_filtering_reused_specs(
|
def test_filtering_reused_specs(
|
||||||
roots, reuse_yaml, expected, not_expected, expected_length, mutable_config, monkeypatch
|
roots, reuse_yaml, expected, not_expected, expected_length, mutable_config
|
||||||
):
|
):
|
||||||
"""Tests that we can select which specs are to be reused, using constraints as filters"""
|
"""Tests that we can select which specs are to be reused, using constraints as filters"""
|
||||||
# Assume all specs have a runtime dependency
|
# Assume all specs have a runtime dependency
|
||||||
monkeypatch.setattr(spack.solver.asp, "_has_runtime_dependencies", lambda x: True)
|
|
||||||
mutable_config.set("concretizer:reuse", reuse_yaml)
|
mutable_config.set("concretizer:reuse", reuse_yaml)
|
||||||
selector = spack.solver.asp.ReusableSpecsSelector(mutable_config)
|
selector = spack.solver.asp.ReusableSpecsSelector(mutable_config)
|
||||||
specs = selector.reusable_specs(roots)
|
specs = selector.reusable_specs(roots)
|
||||||
@ -3149,10 +3148,11 @@ def test_filtering_reused_specs(
|
|||||||
[({"from": [{"type": "local"}]}, 17), ({"from": [{"type": "buildcache"}]}, 0)],
|
[({"from": [{"type": "local"}]}, 17), ({"from": [{"type": "buildcache"}]}, 0)],
|
||||||
)
|
)
|
||||||
@pytest.mark.not_on_windows("Expected length is different on Windows")
|
@pytest.mark.not_on_windows("Expected length is different on Windows")
|
||||||
def test_selecting_reused_sources(reuse_yaml, expected_length, mutable_config, monkeypatch):
|
def test_selecting_reused_sources(
|
||||||
|
reuse_yaml, expected_length, mutable_config, do_not_check_runtimes_on_reuse
|
||||||
|
):
|
||||||
"""Tests that we can turn on/off sources of reusable specs"""
|
"""Tests that we can turn on/off sources of reusable specs"""
|
||||||
# Assume all specs have a runtime dependency
|
# Assume all specs have a runtime dependency
|
||||||
monkeypatch.setattr(spack.solver.asp, "_has_runtime_dependencies", lambda x: True)
|
|
||||||
mutable_config.set("concretizer:reuse", reuse_yaml)
|
mutable_config.set("concretizer:reuse", reuse_yaml)
|
||||||
selector = spack.solver.asp.ReusableSpecsSelector(mutable_config)
|
selector = spack.solver.asp.ReusableSpecsSelector(mutable_config)
|
||||||
specs = selector.reusable_specs(["mpileaks"])
|
specs = selector.reusable_specs(["mpileaks"])
|
||||||
|
Loading…
Reference in New Issue
Block a user