concretize.lp: enforce target compatibility through DAG (#29694)

Spack currently allows dependencies to be concretized for an 
architecture incompatible with the root. This commit adds rules
to make this situation impossible by design.
This commit is contained in:
Greg Becker 2022-04-08 02:01:04 -07:00 committed by GitHub
parent b667be470e
commit 79ba0c50c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 8 deletions

View File

@ -615,17 +615,43 @@ node_os(Package, OS) :- node_os_set(Package, OS), node(Package).
% Each node has only one target chosen among the known targets
1 { node_target(Package, Target) : target(Target) } 1 :- node(Package), error("Each node must have exactly one target").
% If a node must satisfy a target constraint the choice is reduced among the targets
% that satisfy that constraint
1 { node_target(Package, Target) : target_satisfies(Constraint, Target) } 1
:- node_target_satisfies(Package, Constraint), error("Each node must have exactly one target").
% If a node must satisfy a target constraint, enforce it
:- node_target(Package, Target),
node_target_satisfies(Package, Constraint),
not target_satisfies(Constraint, Target),
error("Node targets must satisfy node target constraints").
% If a node has a target and the target satisfies a constraint, then the target
% associated with the node satisfies the same constraint
node_target_satisfies(Package, Constraint)
:- node_target(Package, Target), target_satisfies(Constraint, Target).
% If a node has a target, all of its dependencies must be compatible with that target
:- depends_on(Package, Dependency),
node_target(Package, Target),
not node_target_compatible(Dependency, Target),
error("Dependency node targets must be compatible with dependent targets").
% Intermediate step for performance reasons
% When the integrity constraint above was formulated including this logic
% we suffered a substantial performance penalty
node_target_compatible(Package, Target)
:- node_target(Package, MyTarget),
target_compatible(Target, MyTarget).
% target_compatible(T1, T2) means code for T2 can run on T1
% This order is dependent -> dependency in the node DAG, which
% is contravariant with the target DAG.
target_compatible(Target, Target) :- target(Target).
target_compatible(Child, Parent) :- target_parent(Child, Parent).
target_compatible(Descendent, Ancestor)
:- target_parent(Target, Ancestor),
target_compatible(Descendent, Target),
target(Target).
#defined target_satisfies/2.
#defined target_parent/2.
% The target weight is either the default target weight
% or a more specific per-package weight if set

View File

@ -768,7 +768,7 @@ def test_concretize_anonymous_dep(self, spec_str):
])
def test_compiler_conflicts_in_package_py(self, spec_str, expected_str):
if spack.config.get('config:concretizer') == 'original':
pytest.skip('Original concretizer cannot work around conflicts')
pytest.xfail('Original concretizer cannot work around conflicts')
s = Spec(spec_str).concretized()
assert s.satisfies(expected_str)
@ -1103,6 +1103,12 @@ def test_target_ranges_in_conflicts(self):
with pytest.raises(spack.error.SpackError):
Spec('impossible-concretization').concretized()
def test_target_compatibility(self):
if spack.config.get('config:concretizer') == 'original':
pytest.xfail('Known failure of the original concretizer')
with pytest.raises(spack.error.SpackError):
Spec('libdwarf target=x86_64 ^libelf target=x86_64_v2').concretized()
@pytest.mark.regression('20040')
def test_variant_not_default(self):
s = Spec('ecp-viz-sdk').concretized()
@ -1369,7 +1375,7 @@ def test_concrete_specs_are_not_modified_on_reuse(
self, mutable_database, spec_str, expect_installed, config
):
if spack.config.get('config:concretizer') == 'original':
pytest.skip('Original concretizer cannot reuse specs')
pytest.xfail('Original concretizer cannot reuse specs')
# Test the internal consistency of solve + DAG reconstruction
# when reused specs are added to the mix. This prevents things
@ -1383,7 +1389,7 @@ def test_concrete_specs_are_not_modified_on_reuse(
@pytest.mark.regression('26721,19736')
def test_sticky_variant_in_package(self):
if spack.config.get('config:concretizer') == 'original':
pytest.skip('Original concretizer cannot use sticky variants')
pytest.xfail('Original concretizer cannot use sticky variants')
# Here we test that a sticky variant cannot be changed from its default value
# by the ASP solver if not set explicitly. The package used in the test needs
@ -1400,7 +1406,7 @@ def test_sticky_variant_in_package(self):
def test_do_not_invent_new_concrete_versions_unless_necessary(self):
if spack.config.get('config:concretizer') == 'original':
pytest.skip(
pytest.xfail(
"Original concretizer doesn't resolve concrete versions to known ones"
)