summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2020-12-16 00:57:03 -0800
committerTamara Dahlgren <dahlgren1@llnl.gov>2021-02-17 17:07:29 -0800
commitff9ee9f2437cbdf2ef9a5e34c798ba418af7ad91 (patch)
treee5289f272fcfbe1febff71c29b7c7c9622b6c519
parent6c6631cfdce3b63a491590eea7c2bc7d487eb0a3 (diff)
downloadspack-ff9ee9f2437cbdf2ef9a5e34c798ba418af7ad91.tar.gz
spack-ff9ee9f2437cbdf2ef9a5e34c798ba418af7ad91.tar.bz2
spack-ff9ee9f2437cbdf2ef9a5e34c798ba418af7ad91.tar.xz
spack-ff9ee9f2437cbdf2ef9a5e34c798ba418af7ad91.zip
concretizer: move conditional dependency logic into `concretize.lp`
Continuing to convert everything in `asp.py` into facts, make the generation of ground rules for conditional dependencies use facts, and move the semantics into `concretize.lp`. This is probably the most complex logic in Spack, as dependencies can be conditional on anything, and we need conditional ASP rules to accumulate and map all the dependency conditions to spec attributes. The logic looks complicated, but essentially it accumulates any constraints associated with particular conditions into a fact associated with the condition by id. Then, if *any* condition id's fact is True, we trigger the dependency. This simplifies the way `declared_dependency()` works -- the dependency is now declared regardless of whether it is conditional, and the conditions are handled by `dependency_condition()` facts.
-rw-r--r--lib/spack/spack/solver/asp.py41
-rw-r--r--lib/spack/spack/solver/concretize.lp37
2 files changed, 60 insertions, 18 deletions
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
index 9de674ab60..167baea0da 100644
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -729,10 +729,17 @@ class SpackSolverSetup(object):
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()):
+ for cond_id, (cond, dep) in enumerate(sorted(conditions.items())):
named_cond = cond.copy()
named_cond.name = named_cond.name or pkg.name
+ # each independent condition has an id
+ self.gen.fact(
+ fn.dependency_condition(
+ dep.pkg.name, dep.spec.name, cond_id
+ )
+ )
+
for t in sorted(dep.type):
# Skip test dependencies if they're not requested at all
if t == 'test' and not tests:
@@ -743,22 +750,29 @@ class SpackSolverSetup(object):
and pkg.name not in tests):
continue
- if cond == spack.spec.Spec():
+ # there is a declared dependency of type t
+
+ # TODO: this ends up being redundant in the output --
+ # TODO: not sure if we really need it anymore.
+ # TODO: Look at simplifying the logic in concretize.lp
+ self.gen.fact(
+ fn.declared_dependency(dep.pkg.name, dep.spec.name, t))
+
+ # if it has conditions, declare them.
+ conditions = self.spec_clauses(named_cond, body=True)
+ for cond in conditions:
self.gen.fact(
- fn.declared_dependency(
- dep.pkg.name, dep.spec.name, t
+ fn.dep_cond(
+ dep.pkg.name, dep.spec.name, t, cond_id,
+ cond.name, *cond.args
)
)
- else:
- clauses = self.spec_clauses(named_cond, body=True)
-
- self.gen.rule(
- fn.declared_dependency(
- dep.pkg.name, dep.spec.name, t
- ), self.gen._and(*clauses)
- )
# add constraints on the dependency from dep spec.
+
+ # TODO: nest this in the type loop so that dependency
+ # TODO: constraints apply only for their deptypes and
+ # TODO: specific conditions.
if spack.repo.path.is_virtual(dep.spec.name):
self.virtual_constraints.add(str(dep.spec))
conditions = ([fn.real_node(pkg.name)] +
@@ -779,7 +793,8 @@ class SpackSolverSetup(object):
*self.spec_clauses(named_cond, body=True)
)
)
- self.gen.newline()
+
+ self.gen.newline()
def virtual_preferences(self, pkg_name, func):
"""Call func(vspec, provider, i) for each of pkg's provider prefs."""
diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp
index 0affb9f297..7c4e0d6d02 100644
--- a/lib/spack/spack/solver/concretize.lp
+++ b/lib/spack/spack/solver/concretize.lp
@@ -40,8 +40,7 @@ depends_on(Package, Dependency) :- depends_on(Package, Dependency, _).
% declared dependencies are real if they're not virtual AND
% the package is not an external
depends_on(Package, Dependency, Type)
- :- declared_dependency(Package, Dependency, Type),
- node(Package),
+ :- dependency_conditions(Package, Dependency, Type),
not virtual(Dependency),
not external(Package).
@@ -51,10 +50,38 @@ depends_on(Package, Dependency, Type)
depends_on(Package, Provider, Type)
: provides_virtual(Provider, Virtual)
} 1
- :- declared_dependency(Package, Virtual, Type),
+ :- dependency_conditions(Package, Virtual, Type),
virtual(Virtual),
- not external(Package),
- node(Package).
+ not external(Package).
+
+% if any individual condition below is true, trigger the dependency.
+dependency_conditions(P, D, T) :- dependency_conditions(P, D, T, _).
+
+% collect all the dependency condtions into a single conditional rule
+dependency_conditions(P, D, T, I) :-
+ node(Package)
+ : dep_cond(P, D, T, I, "node", Package);
+ version(Package, Version)
+ : dep_cond(P, D, T, I, "version", Package, Version);
+ version_satisfies(Package, Constraint)
+ : dep_cond(P, D, T, I, "version_satisfies", Package, Constraint);
+ node_platform(Package, Platform)
+ : dep_cond(P, D, T, I, "node_platform", Package, Platform);
+ node_os(Package, OS)
+ : dep_cond(P, D, T, I, "node_os", Package, OS);
+ node_target(Package, Target)
+ : dep_cond(P, D, T, I, "node_target", Package, Target);
+ variant_value(Package, Variant, Value)
+ : dep_cond(P, D, T, I, "variant_value", Package, Variant, Value);
+ node_compiler(Package, Compiler)
+ : dep_cond(P, D, T, I, "node_compiler", Package, Compiler);
+ node_compiler_version(Package, Compiler, Version)
+ : dep_cond(P, D, T, I, "node_compiler_version", Package, Compiler, Version);
+ node_flag(Package, FlagType, Flag)
+ : dep_cond(P, D, T, I, "node_flag", Package, FlagType, Flag);
+ dependency_condition(P, D, I);
+ declared_dependency(P, D, T);
+ node(P).
% if a virtual was required by some root spec, one provider is in the DAG
1 { node(Package) : provides_virtual(Package, Virtual) } 1