diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index a7c69ae8e3..18c261a317 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -696,6 +696,7 @@ def __init__(self): self.possible_virtuals = None self.possible_compilers = [] self.version_constraints = set() + self.target_constraints = set() self.providers_by_vspec_name = collections.defaultdict(list) self.virtual_constraints = set() self.compiler_version_constraints = set() @@ -765,6 +766,16 @@ def spec_versions(self, spec): self.version_constraints.add((spec.name, spec.versions)) return [fn.version_satisfies(spec.name, spec.versions)] + def target_ranges(self, spec, single_target_fn): + target = spec.architecture.target + + # Check if the target is a concrete target + if str(target) in archspec.cpu.TARGETS: + return [single_target_fn(spec.name, target)] + + self.target_constraints.add((spec.name, target)) + return [fn.node_target_satisfies(spec.name, target)] + def conflict_rules(self, pkg): for trigger, constraints in pkg.conflicts.items(): for constraint, _ in constraints: @@ -1167,7 +1178,7 @@ class Body(object): if arch.os: clauses.append(f.node_os(spec.name, arch.os)) if arch.target: - clauses.append(f.node_target(spec.name, arch.target)) + clauses.extend(self.target_ranges(spec, f.node_target)) # variants for vname, variant in sorted(spec.variants.items()): @@ -1437,6 +1448,45 @@ def define_compiler_version_constraints(self): ) self.gen.newline() + def define_target_constraints(self): + + def _all_targets_satisfiying(single_constraint): + allowed_targets = [] + t_min, _, t_max = single_constraint.partition(':') + for test_target in archspec.cpu.TARGETS.values(): + # Check lower bound + if t_min and not t_min <= test_target: + continue + + # Check upper bound + if t_max and not t_max >= test_target: + continue + + allowed_targets.append(test_target) + return allowed_targets + + cache = {} + for spec_name, target_constraint in sorted(self.target_constraints): + + # Construct the list of allowed targets for this constraint + allowed_targets = [] + for single_constraint in str(target_constraint).split(','): + if single_constraint not in cache: + cache[single_constraint] = _all_targets_satisfiying( + single_constraint + ) + allowed_targets.extend(cache[single_constraint]) + + allowed_targets = [ + fn.node_target(spec_name, t) for t in allowed_targets + ] + + self.gen.one_of_iff( + fn.node_target_satisfies(spec_name, target_constraint), + allowed_targets, + ) + self.gen.newline() + def setup(self, driver, specs, tests=False): """Generate an ASP program with relevant constraints for specs. @@ -1560,6 +1610,9 @@ def setup(self, driver, specs, tests=False): self.gen.h1("Compiler Version Constraints") self.define_compiler_version_constraints() + self.gen.h1("Target Constraints") + self.define_target_constraints() + def virtual_spec_clauses(self, dep): assert dep.virtual self.virtual_constraints.add(str(dep)) diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 11c2764822..39799fc1bc 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -938,3 +938,8 @@ def test_compiler_match_is_preferred_to_newer_version(self): assert 'openblas@0.2.13' in s assert s['openblas'].satisfies('%gcc@4.4.0') + + @pytest.mark.regression('19981') + def test_target_ranges_in_conflicts(self): + with pytest.raises(spack.error.SpackError): + Spec('impossible-concretization').concretized() diff --git a/var/spack/repos/builtin.mock/packages/impossible-concretization/package.py b/var/spack/repos/builtin.mock/packages/impossible-concretization/package.py new file mode 100644 index 0000000000..bb8ad02b49 --- /dev/null +++ b/var/spack/repos/builtin.mock/packages/impossible-concretization/package.py @@ -0,0 +1,15 @@ +# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) +class ImpossibleConcretization(Package): + """Package that should be impossible to concretize due to a conflict + with target ranges. See Issue 19981. + """ + + homepage = "http://www.example.com" + url = "http://www.example.com/example-1.0.tar.gz" + + version(1.0, 'foobarbaz') + + conflicts('target=x86_64:')