From ae1ef85af5a4419eef83ed9f4f112770543d999d Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Thu, 22 Oct 2020 14:26:11 +0200 Subject: concretizer: account for test dependencies only when required --- lib/spack/spack/solver/asp.py | 26 +++++++++++++-------- lib/spack/spack/spec.py | 2 +- lib/spack/spack/test/spec_dag.py | 49 +++++++++++++++++----------------------- 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 55e18e88e8..264ab9e54b 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -593,8 +593,10 @@ class PyclingoDriver(object): self.backend.add_rule([], [head_atom, more_than_1]) self.backend.add_rule([], [head_atom, -at_least_1]) - def solve(self, solver_setup, specs, dump=None, nmodels=0, - timers=False, stats=False): + def solve( + self, solver_setup, specs, dump=None, nmodels=0, + timers=False, stats=False, tests=False + ): timer = Timer() # Initialize the control object for the solver @@ -607,7 +609,7 @@ class PyclingoDriver(object): self.assumptions = [] with self.control.backend() as backend: self.backend = backend - solver_setup.setup(self, specs) + solver_setup.setup(self, specs, tests=tests) timer.phase("setup") # read in the main ASP program and display logic -- these are @@ -837,7 +839,7 @@ class SpackSolverSetup(object): self.gen.fact(fn.node_compiler_preference( pkg.name, cspec.name, cspec.version, i)) - def pkg_rules(self, pkg): + def pkg_rules(self, pkg, tests): pkg = packagize(pkg) # versions @@ -888,7 +890,7 @@ class SpackSolverSetup(object): self.package_compiler_defaults(pkg) # dependencies - self.package_dependencies_rules(pkg) + self.package_dependencies_rules(pkg, tests) # virtual preferences self.virtual_preferences( @@ -898,7 +900,7 @@ class SpackSolverSetup(object): ) ) - def package_dependencies_rules(self, pkg): + def package_dependencies_rules(self, pkg, tests): """Translate 'depends_on' directives into ASP logic.""" for name, conditions in sorted(pkg.dependencies.items()): for cond, dep in sorted(conditions.items()): @@ -906,6 +908,10 @@ class SpackSolverSetup(object): named_cond.name = named_cond.name or pkg.name 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(): self.gen.fact( fn.declared_dependency( @@ -1411,7 +1417,7 @@ class SpackSolverSetup(object): ) self.gen.newline() - def setup(self, driver, specs): + def setup(self, driver, specs, tests=False): """Generate an ASP program with relevant constraints for specs. This calls methods on the solve driver to set up the problem with @@ -1464,7 +1470,7 @@ class SpackSolverSetup(object): self.gen.h1('Package Constraints') for pkg in sorted(pkgs): 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.preferred_variants(pkg) self.preferred_targets(pkg) @@ -1735,7 +1741,7 @@ def highlight(string): # # 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. Arguments: @@ -1748,4 +1754,4 @@ def solve(specs, dump=(), models=0, timers=False, stats=False): driver.out = sys.stdout setup = SpackSolverSetup() - return driver.solve(setup, specs, dump, models, timers, stats) + return driver.solve(setup, specs, dump, models, timers, stats, tests) diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index fb7c0a93ce..cacad483f6 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -2446,7 +2446,7 @@ class Spec(object): raise spack.error.SpecError( "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: result.print_cores() raise spack.error.UnsatisfiableSpecError( diff --git a/lib/spack/spack/test/spec_dag.py b/lib/spack/spack/test/spec_dag.py index 0b638ada04..b68e582657 100644 --- a/lib/spack/spack/test/spec_dag.py +++ b/lib/spack/spack/test/spec_dag.py @@ -2,12 +2,12 @@ # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) - """ These tests check Spec DAG operations using dummy packages. """ import pytest import spack.architecture +import spack.error import spack.package 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 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') build_only = ('build',) @@ -152,7 +157,12 @@ def test_specify_preinstalled_dep(): @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 then constructs a Spec with X but with no constraints on X, so that the 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) with spack.repo.swap(mock_repo): - spec = Spec('x ^y@2') - spec.concretize() - - assert ('y@2' in spec) - - with spack.repo.swap(mock_repo): - spec = Spec('x@1') + spec = Spec(spec_str) spec.concretize() - assert ('y' not in spec) - - with spack.repo.swap(mock_repo): - spec = Spec('x') - spec.concretize() - - assert ('y@3' in spec) + result = expr_str in spec + assert result is expected, '{0} in {1}'.format(expr_str, spec) @pytest.mark.usefixtures('mutable_mock_repo', 'config') class TestSpecDag(object): - def test_conflicting_package_constraints(self, set_dependency): set_dependency('mpileaks', 'mpich@1.0') set_dependency('callpath', 'mpich@2.0') @@ -387,17 +385,12 @@ class TestSpecDag(object): with pytest.raises(spack.spec.UnsatisfiableArchitectureSpecError): spec.normalize() - def test_invalid_dep(self): - spec = Spec('libelf ^mpich') - with pytest.raises(spack.spec.InvalidDependencyError): - spec.concretize() - - spec = Spec('libelf ^libdwarf') - with pytest.raises(spack.spec.InvalidDependencyError): - spec.concretize() - - spec = Spec('mpich ^dyninst ^libelf') - with pytest.raises(spack.spec.InvalidDependencyError): + @pytest.mark.parametrize('spec_str', [ + 'libelf ^mpich', 'libelf ^libdwarf', 'mpich ^dyninst ^libelf' + ]) + def test_invalid_dep(self, spec_str): + spec = Spec(spec_str) + with pytest.raises(spack.error.SpecError): spec.concretize() def test_equal(self): -- cgit v1.2.3-60-g2f50