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.
This commit is contained in:
Todd Gamblin 2020-12-16 00:57:03 -08:00 committed by Tamara Dahlgren
parent 6c6631cfdc
commit ff9ee9f243
2 changed files with 61 additions and 19 deletions

View file

@ -729,10 +729,17 @@ def pkg_rules(self, pkg, tests):
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 @@ def package_dependencies_rules(self, pkg, tests):
and pkg.name not in tests):
continue
if cond == spack.spec.Spec():
self.gen.fact(
fn.declared_dependency(
dep.pkg.name, dep.spec.name, t
)
)
else:
clauses = self.spec_clauses(named_cond, body=True)
# there is a declared dependency of type t
self.gen.rule(
fn.declared_dependency(
dep.pkg.name, dep.spec.name, t
), self.gen._and(*clauses)
# 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.dep_cond(
dep.pkg.name, dep.spec.name, t, cond_id,
cond.name, *cond.args
)
)
# 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 @@ def package_dependencies_rules(self, pkg, tests):
*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."""

View file

@ -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