concretizer: account for test dependencies only when required

This commit is contained in:
Massimiliano Culpo 2020-10-22 14:26:11 +02:00 committed by Todd Gamblin
parent 346beedfd4
commit ae1ef85af5
3 changed files with 38 additions and 39 deletions

View File

@ -593,8 +593,10 @@ def one_of_iff(self, head, versions):
self.backend.add_rule([], [head_atom, more_than_1]) self.backend.add_rule([], [head_atom, more_than_1])
self.backend.add_rule([], [head_atom, -at_least_1]) self.backend.add_rule([], [head_atom, -at_least_1])
def solve(self, solver_setup, specs, dump=None, nmodels=0, def solve(
timers=False, stats=False): self, solver_setup, specs, dump=None, nmodels=0,
timers=False, stats=False, tests=False
):
timer = Timer() timer = Timer()
# Initialize the control object for the solver # Initialize the control object for the solver
@ -607,7 +609,7 @@ def solve(self, solver_setup, specs, dump=None, nmodels=0,
self.assumptions = [] self.assumptions = []
with self.control.backend() as backend: with self.control.backend() as backend:
self.backend = backend self.backend = backend
solver_setup.setup(self, specs) solver_setup.setup(self, specs, tests=tests)
timer.phase("setup") timer.phase("setup")
# read in the main ASP program and display logic -- these are # read in the main ASP program and display logic -- these are
@ -837,7 +839,7 @@ def package_compiler_defaults(self, pkg):
self.gen.fact(fn.node_compiler_preference( self.gen.fact(fn.node_compiler_preference(
pkg.name, cspec.name, cspec.version, i)) pkg.name, cspec.name, cspec.version, i))
def pkg_rules(self, pkg): def pkg_rules(self, pkg, tests):
pkg = packagize(pkg) pkg = packagize(pkg)
# versions # versions
@ -888,7 +890,7 @@ def pkg_rules(self, pkg):
self.package_compiler_defaults(pkg) self.package_compiler_defaults(pkg)
# dependencies # dependencies
self.package_dependencies_rules(pkg) self.package_dependencies_rules(pkg, tests)
# virtual preferences # virtual preferences
self.virtual_preferences( self.virtual_preferences(
@ -898,7 +900,7 @@ def pkg_rules(self, pkg):
) )
) )
def package_dependencies_rules(self, pkg): def package_dependencies_rules(self, pkg, tests):
"""Translate 'depends_on' directives into ASP logic.""" """Translate 'depends_on' directives into ASP logic."""
for name, conditions in sorted(pkg.dependencies.items()): for name, conditions in sorted(pkg.dependencies.items()):
for cond, dep in sorted(conditions.items()): for cond, dep in sorted(conditions.items()):
@ -906,6 +908,10 @@ def package_dependencies_rules(self, pkg):
named_cond.name = named_cond.name or pkg.name named_cond.name = named_cond.name or pkg.name
for t in sorted(dep.type): for t in sorted(dep.type):
# Skip test dependencies if they're not requested
if t == 'test' and (not tests or pkg.name not in tests):
continue
if cond == spack.spec.Spec(): if cond == spack.spec.Spec():
self.gen.fact( self.gen.fact(
fn.declared_dependency( fn.declared_dependency(
@ -1411,7 +1417,7 @@ def define_compiler_version_constraints(self):
) )
self.gen.newline() self.gen.newline()
def setup(self, driver, specs): def setup(self, driver, specs, tests=False):
"""Generate an ASP program with relevant constraints for specs. """Generate an ASP program with relevant constraints for specs.
This calls methods on the solve driver to set up the problem with This calls methods on the solve driver to set up the problem with
@ -1464,7 +1470,7 @@ def setup(self, driver, specs):
self.gen.h1('Package Constraints') self.gen.h1('Package Constraints')
for pkg in sorted(pkgs): for pkg in sorted(pkgs):
self.gen.h2('Package rules: %s' % pkg) self.gen.h2('Package rules: %s' % pkg)
self.pkg_rules(pkg) self.pkg_rules(pkg, tests=tests)
self.gen.h2('Package preferences: %s' % pkg) self.gen.h2('Package preferences: %s' % pkg)
self.preferred_variants(pkg) self.preferred_variants(pkg)
self.preferred_targets(pkg) self.preferred_targets(pkg)
@ -1735,7 +1741,7 @@ def highlight(string):
# #
# These are handwritten parts for the Spack ASP model. # These are handwritten parts for the Spack ASP model.
# #
def solve(specs, dump=(), models=0, timers=False, stats=False): def solve(specs, dump=(), models=0, timers=False, stats=False, tests=False):
"""Solve for a stable model of specs. """Solve for a stable model of specs.
Arguments: Arguments:
@ -1748,4 +1754,4 @@ def solve(specs, dump=(), models=0, timers=False, stats=False):
driver.out = sys.stdout driver.out = sys.stdout
setup = SpackSolverSetup() setup = SpackSolverSetup()
return driver.solve(setup, specs, dump, models, timers, stats) return driver.solve(setup, specs, dump, models, timers, stats, tests)

View File

@ -2446,7 +2446,7 @@ def _new_concretize(self, tests=False):
raise spack.error.SpecError( raise spack.error.SpecError(
"Spec has no name; cannot concretize an anonymous spec") "Spec has no name; cannot concretize an anonymous spec")
result = spack.solver.asp.solve([self]) result = spack.solver.asp.solve([self], tests=tests)
if not result.satisfiable: if not result.satisfiable:
result.print_cores() result.print_cores()
raise spack.error.UnsatisfiableSpecError( raise spack.error.UnsatisfiableSpecError(

View File

@ -2,12 +2,12 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
""" """
These tests check Spec DAG operations using dummy packages. These tests check Spec DAG operations using dummy packages.
""" """
import pytest import pytest
import spack.architecture import spack.architecture
import spack.error
import spack.package import spack.package
from spack.spec import Spec from spack.spec import Spec
@ -90,6 +90,11 @@ def test_installed_deps():
that the installed instance of P should be used. In this case, D should that the installed instance of P should be used. In this case, D should
not be constrained by P since P is already built. not be constrained by P since P is already built.
""" """
# FIXME: this requires to concretize build deps separately if we are
# FIXME: using the clingo based concretizer
if spack.config.get('config:concretizer') == 'clingo':
pytest.xfail('requires separate concretization of build dependencies')
default = ('build', 'link') default = ('build', 'link')
build_only = ('build',) build_only = ('build',)
@ -152,7 +157,12 @@ def test_specify_preinstalled_dep():
@pytest.mark.usefixtures('config') @pytest.mark.usefixtures('config')
def test_conditional_dep_with_user_constraints(): @pytest.mark.parametrize('spec_str,expr_str,expected', [
('x ^y@2', 'y@2', True),
('x@1', 'y', False),
('x', 'y@3', True)
])
def test_conditional_dep_with_user_constraints(spec_str, expr_str, expected):
"""This sets up packages X->Y such that X depends on Y conditionally. It """This sets up packages X->Y such that X depends on Y conditionally. It
then constructs a Spec with X but with no constraints on X, so that the then constructs a Spec with X but with no constraints on X, so that the
initial normalization pass cannot determine whether the constraints are initial normalization pass cannot determine whether the constraints are
@ -171,27 +181,15 @@ def test_conditional_dep_with_user_constraints():
mock_repo.add_package('x', [y], [default], conditions=x_on_y_conditions) mock_repo.add_package('x', [y], [default], conditions=x_on_y_conditions)
with spack.repo.swap(mock_repo): with spack.repo.swap(mock_repo):
spec = Spec('x ^y@2') spec = Spec(spec_str)
spec.concretize() spec.concretize()
assert ('y@2' in spec) result = expr_str in spec
assert result is expected, '{0} in {1}'.format(expr_str, spec)
with spack.repo.swap(mock_repo):
spec = Spec('x@1')
spec.concretize()
assert ('y' not in spec)
with spack.repo.swap(mock_repo):
spec = Spec('x')
spec.concretize()
assert ('y@3' in spec)
@pytest.mark.usefixtures('mutable_mock_repo', 'config') @pytest.mark.usefixtures('mutable_mock_repo', 'config')
class TestSpecDag(object): class TestSpecDag(object):
def test_conflicting_package_constraints(self, set_dependency): def test_conflicting_package_constraints(self, set_dependency):
set_dependency('mpileaks', 'mpich@1.0') set_dependency('mpileaks', 'mpich@1.0')
set_dependency('callpath', 'mpich@2.0') set_dependency('callpath', 'mpich@2.0')
@ -387,17 +385,12 @@ def test_unsatisfiable_architecture(self, set_dependency):
with pytest.raises(spack.spec.UnsatisfiableArchitectureSpecError): with pytest.raises(spack.spec.UnsatisfiableArchitectureSpecError):
spec.normalize() spec.normalize()
def test_invalid_dep(self): @pytest.mark.parametrize('spec_str', [
spec = Spec('libelf ^mpich') 'libelf ^mpich', 'libelf ^libdwarf', 'mpich ^dyninst ^libelf'
with pytest.raises(spack.spec.InvalidDependencyError): ])
spec.concretize() def test_invalid_dep(self, spec_str):
spec = Spec(spec_str)
spec = Spec('libelf ^libdwarf') with pytest.raises(spack.error.SpecError):
with pytest.raises(spack.spec.InvalidDependencyError):
spec.concretize()
spec = Spec('mpich ^dyninst ^libelf')
with pytest.raises(spack.spec.InvalidDependencyError):
spec.concretize() spec.concretize()
def test_equal(self): def test_equal(self):