diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/solver/asp.py | 130 | ||||
-rw-r--r-- | lib/spack/spack/solver/concretize.lp | 170 | ||||
-rw-r--r-- | lib/spack/spack/solver/display.lp | 2 |
3 files changed, 119 insertions, 183 deletions
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index e61ebc74fd..c2dd241477 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -483,28 +483,12 @@ class SpackSolverSetup(object): def conflict_rules(self, pkg): for trigger, constraints in pkg.conflicts.items(): + trigger_id = self.condition(spack.spec.Spec(trigger), name=pkg.name) + self.gen.fact(fn.conflict_trigger(trigger_id)) + for constraint, _ in constraints: - constraint_body = spack.spec.Spec(pkg.name) - constraint_body.constrain(constraint) - constraint_body.constrain(trigger) - - clauses = [] - for s in constraint_body.traverse(): - clauses += self.spec_clauses(s, body=True) - - # TODO: find a better way to generate clauses for integrity - # TODO: constraints, instead of generating them for the body - # TODO: of a rule and filter unwanted functions. - to_be_filtered = ['node_compiler_hard'] - clauses = [x for x in clauses if x.name not in to_be_filtered] - - # Emit facts based on clauses - condition_id = next(self._condition_id_counter) - self.gen.fact(fn.conflict(condition_id, pkg.name)) - for clause in clauses: - self.gen.fact(fn.conflict_condition( - condition_id, clause.name, *clause.args - )) + constraint_id = self.condition(constraint, name=pkg.name) + self.gen.fact(fn.conflict(pkg.name, trigger_id, constraint_id)) self.gen.newline() def available_compilers(self): @@ -642,50 +626,44 @@ class SpackSolverSetup(object): ) ) - def _condition_facts( - self, pkg_name, cond_spec, dep_spec, - cond_fn, require_fn, impose_fn - ): + def condition(self, required_spec, imposed_spec=None, name=None): """Generate facts for a dependency or virtual provider condition. Arguments: - pkg_name (str): name of the package that triggers the - condition (e.g., the dependent or the provider) - cond_spec (Spec): the dependency spec representing the - condition that needs to be True (can be anonymous) - dep_spec (Spec): the sepc of the dependency or provider - to be depended on/provided if the condition holds. - cond_fn (AspFunction): function to use to declare the condition; - will be called with the cond id, pkg_name, an dep_spec.name - require_fn (AspFunction): function to use to declare the conditions - required of the dependent/provider to trigger - impose_fn (AspFunction): function to use for constraints imposed - on the dependency/virtual + required_spec (Spec): the spec that triggers this condition + imposed_spec (optional, Spec): the sepc with constraints that + are imposed when this condition is triggered + name (optional, str): name for `required_spec` (required if + required_spec is anonymous, ignored if not) Returns: (int): id of the condition created by this function """ - condition_id = next(self._condition_id_counter) - named_cond = cond_spec.copy() - named_cond.name = named_cond.name or pkg_name + named_cond = required_spec.copy() + named_cond.name = named_cond.name or name + assert named_cond.name, "must provide name for anonymous condtions!" - self.gen.fact(cond_fn(condition_id, pkg_name, dep_spec.name)) + condition_id = next(self._condition_id_counter) + self.gen.fact(fn.condition(condition_id)) - # conditions that trigger the condition - conditions = self.checked_spec_clauses( - named_cond, body=True, required_from=pkg_name - ) - for pred in conditions: - self.gen.fact(require_fn(condition_id, pred.name, *pred.args)) + # requirements trigger the condition + requirements = self.checked_spec_clauses( + named_cond, body=True, required_from=name) + for pred in requirements: + self.gen.fact( + fn.condition_requirement(condition_id, pred.name, *pred.args) + ) - imposed_constraints = self.checked_spec_clauses( - dep_spec, required_from=pkg_name - ) - for pred in imposed_constraints: - # imposed "node"-like conditions are no-ops - if pred.name in ("node", "virtual_node"): - continue - self.gen.fact(impose_fn(condition_id, pred.name, *pred.args)) + if imposed_spec: + imposed_constraints = self.checked_spec_clauses( + imposed_spec, body=False, required_from=name) + for pred in imposed_constraints: + # imposed "node"-like conditions are no-ops + if pred.name in ("node", "virtual_node"): + continue + self.gen.fact( + fn.imposed_constraint(condition_id, pred.name, *pred.args) + ) return condition_id @@ -695,25 +673,20 @@ class SpackSolverSetup(object): for provided, whens in pkg.provided.items(): for when in whens: - self._condition_facts( - pkg.name, when, provided, - fn.provider_condition, - fn.required_provider_condition, - fn.imposed_dependency_condition - ) - + condition_id = self.condition(when, provided, pkg.name) + self.gen.fact(fn.provider_condition( + condition_id, when.name, provided.name + )) self.gen.newline() def package_dependencies_rules(self, pkg, tests): """Translate 'depends_on' directives into ASP logic.""" for _, conditions in sorted(pkg.dependencies.items()): for cond, dep in sorted(conditions.items()): - condition_id = self._condition_facts( - pkg.name, cond, dep.spec, - fn.dependency_condition, - fn.required_dependency_condition, - fn.imposed_dependency_condition - ) + condition_id = self.condition(cond, dep.spec, pkg.name) + self.gen.fact(fn.dependency_condition( + condition_id, pkg.name, dep.spec.name + )) for t in sorted(dep.type): # Skip test dependencies if they're not requested at all @@ -794,24 +767,13 @@ class SpackSolverSetup(object): pkg_name, str(version), weight, id )) + # Declare external conditions with a local index into packages.yaml for local_idx, spec in enumerate(external_specs): - condition_id = next(self._condition_id_counter) - - # Declare the global ID associated with this external spec - self.gen.fact(fn.external_spec(condition_id, pkg_name)) - - # Local index into packages.yaml + condition_id = self.condition(spec) self.gen.fact( - fn.external_spec_index(condition_id, pkg_name, local_idx)) - - # Add conditions to be satisfied for this external + fn.possible_external(condition_id, pkg_name, local_idx) + ) self.possible_versions[spec.name].add(spec.version) - clauses = self.spec_clauses(spec, body=True) - for clause in clauses: - self.gen.fact( - fn.external_spec_condition( - condition_id, clause.name, *clause.args) - ) self.gen.newline() def preferred_variants(self, pkg_name): @@ -1475,7 +1437,7 @@ class SpecBuilder(object): def no_flags(self, pkg, flag_type): self._specs[pkg].compiler_flags[flag_type] = [] - def external_spec_selected(self, condition_id, pkg, idx): + def external_spec_selected(self, pkg, idx): """This means that the external spec and index idx has been selected for this package. """ diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp index 22ba77142a..6e3f89a10d 100644 --- a/lib/spack/spack/solver/concretize.lp +++ b/lib/spack/spack/solver/concretize.lp @@ -37,15 +37,53 @@ version_satisfies(Package, Constraint) #defined version_satisfies/3. %----------------------------------------------------------------------------- +% Spec conditions and imposed constraints +% +% Given Spack directives like these: +% depends_on("foo@1.0+bar", when="@2.0+variant") +% provides("mpi@2:", when="@1.9:") +% +% The conditions are `@2.0+variant` and `@1.9:`, and the imposed constraints +% are `@1.0+bar` on `foo` and `@2:` on `mpi`. +%----------------------------------------------------------------------------- +% conditions are specified with `condition_requirement` and hold when +% corresponding spec attributes hold. +condition_holds(ID) :- + condition(ID); + attr(Name, A1) : condition_requirement(ID, Name, A1); + attr(Name, A1, A2) : condition_requirement(ID, Name, A1, A2); + attr(Name, A1, A2, A3) : condition_requirement(ID, Name, A1, A2, A3). + +% conditions that hold impose constraints on other specs +attr(Name, A1) :- condition_holds(ID), imposed_constraint(ID, Name, A1). +attr(Name, A1, A2) :- condition_holds(ID), imposed_constraint(ID, Name, A1, A2). +attr(Name, A1, A2, A3) :- condition_holds(ID), imposed_constraint(ID, Name, A1, A2, A3). + +#defined condition/1. +#defined condition_requirement/3. +#defined condition_requirement/4. +#defined condition_requirement/5. +#defined imposed_constraint/3. +#defined imposed_constraint/4. +#defined imposed_constraint/5. + +%----------------------------------------------------------------------------- % Dependency semantics %----------------------------------------------------------------------------- % Dependencies of any type imply that one package "depends on" another depends_on(Package, Dependency) :- depends_on(Package, Dependency, _). +% a dependency holds if its condition holds +dependency_holds(Package, Dependency, Type) :- + dependency_condition(ID, Package, Dependency), + dependency_type(ID, Type), + condition_holds(ID). + % declared dependencies are real if they're not virtual AND -% the package is not an external +% the package is not an external. +% They're only triggered if the associated dependnecy condition holds. depends_on(Package, Dependency, Type) - :- dependency_conditions(Package, Dependency, Type), + :- dependency_holds(Package, Dependency, Type), not virtual(Dependency), not external(Package). @@ -69,74 +107,19 @@ path(Parent, Child) :- depends_on(Parent, Child). path(Parent, Descendant) :- path(Parent, A), depends_on(A, Descendant). :- path(A, B), path(B, A). -%----------------------------------------------------------------------------- -% Conditional dependencies -% -% This takes care of `when=SPEC` in `depends_on("foo@1.0+bar", when="SPEC")`. -%----------------------------------------------------------------------------- -% if any individual condition below is true, trigger the dependency. -dependency_conditions(Package, Dependency, Type) :- - dependency_conditions_hold(ID, Package, Dependency), - dependency_type(ID, Type). - #defined dependency_type/2. - -% collect all the dependency conditions into a single conditional rule -% distinguishing between Parent and Package (Arg1) is needed to account -% for conditions like: -% -% depends_on('patchelf@0.9', when='@1.0:1.1 ^python@:2') -% -% that include dependencies -dependency_conditions_hold(ID, Parent, Dependency) :- - attr(Name, Arg1) : required_dependency_condition(ID, Name, Arg1); - attr(Name, Arg1, Arg2) : required_dependency_condition(ID, Name, Arg1, Arg2); - attr(Name, Arg1, Arg2, Arg3) : required_dependency_condition(ID, Name, Arg1, Arg2, Arg3); - dependency_condition(ID, Parent, Dependency); - % There must be at least a dependency type declared, - % otherwise the dependency doesn't hold - dependency_type(ID, _); - node(Parent); - not external(Parent). - #defined dependency_condition/3. -#defined required_dependency_condition/3. -#defined required_dependency_condition/4. -#defined required_dependency_condition/5. - -%----------------------------------------------------------------------------- -% Imposed constraints on dependencies -% -% This handles the `@1.0+bar` in `depends_on("foo@1.0+bar", when="SPEC")`, or -% the `mpi@2:` in `provides("mpi@2:", when="@1.9:")`. -%----------------------------------------------------------------------------- -% NOTE: `attr(Name, Arg1)` is omitted here b/c the only single-arg attribute is -% NOTE: `node()`, which is handled above under "Dependency Semantics" - -attr(Name, Arg1, Arg2) :- - dependency_conditions_hold(ID, Package, Dependency), - imposed_dependency_condition(ID, Name, Arg1, Arg2). - -attr(Name, Arg1, Arg2, Arg3) :- - dependency_conditions_hold(ID, Package, Dependency), - imposed_dependency_condition(ID, Name, Arg1, Arg2, Arg3). - -#defined imposed_dependency_condition/4. -#defined imposed_dependency_condition/5. %----------------------------------------------------------------------------- % Conflicts %----------------------------------------------------------------------------- -:- not external(Package) : conflict_condition(ID, "node", Package); - attr(Name, Arg1) : conflict_condition(ID, Name, Arg1); - attr(Name, Arg1, Arg2) : conflict_condition(ID, Name, Arg1, Arg2); - attr(Name, Arg1, Arg2, Arg3) : conflict_condition(ID, Name, Arg1, Arg2, Arg3); - conflict(ID, Package). +:- node(Package), + not external(Package), + conflict(Package, TriggerID, ConstraintID), + condition_holds(TriggerID), + condition_holds(ConstraintID). -#defined conflict/2. -#defined conflict_condition/3. -#defined conflict_condition/4. -#defined conflict_condition/5. +#defined conflict/3. %----------------------------------------------------------------------------- % Virtual dependencies @@ -145,10 +128,15 @@ attr(Name, Arg1, Arg2, Arg3) :- % if a package depends on a virtual, it's not external and we have a % provider for that virtual then it depends on the provider depends_on(Package, Provider, Type) - :- dependency_conditions(Package, Virtual, Type), + :- dependency_holds(Package, Virtual, Type), provides_virtual(Provider, Virtual), not external(Package). +% dependencies on virtuals also imply that the virtual is a virtual node +virtual_node(Virtual) + :- dependency_holds(Package, Virtual, Type), + virtual(Virtual), not external(Package). + % if there's a virtual node, we must select one provider 1 { provides_virtual(Package, Virtual) : possible_provider(Package, Virtual) } 1 :- virtual_node(Virtual). @@ -158,32 +146,21 @@ virtual_node(Virtual) :- virtual_root(Virtual). 1 { root(Package) : provides_virtual(Package, Virtual) } 1 :- virtual_root(Virtual). -% all virtual providers come from provider conditions like this -dependency_conditions_hold(ID, Provider, Virtual) :- - attr(Name, Arg1) : required_provider_condition(ID, Name, Arg1); - attr(Name, Arg1, Arg2) : required_provider_condition(ID, Name, Arg1, Arg2); - attr(Name, Arg1, Arg2, Arg3) : required_provider_condition(ID, Name, Arg1, Arg2, Arg3); - virtual(Virtual); - provider_condition(ID, Provider, Virtual). - % The provider provides the virtual if some provider condition holds. provides_virtual(Provider, Virtual) :- provider_condition(ID, Provider, Virtual), - dependency_conditions_hold(ID, Provider, Virtual), + condition_holds(ID), virtual(Virtual). % a node that provides a virtual is a provider provider(Package, Virtual) :- node(Package), provides_virtual(Package, Virtual). -% dependencies on virtuals also imply that the virtual is a virtual node -virtual_node(Virtual) - :- dependency_conditions(Package, Virtual, Type), - virtual(Virtual), not external(Package). - % for any virtual, there can be at most one provider in the DAG 0 { node(Package) : provides_virtual(Package, Virtual) } 1 :- virtual(Virtual). +#defined possible_provider/2. + %----------------------------------------------------------------------------- % Virtual dependency weights %----------------------------------------------------------------------------- @@ -308,32 +285,29 @@ external(Package) :- external_only(Package), node(Package). % a package is a real_node if it is not external real_node(Package) :- node(Package), not external(Package). -% if an external version is selected, the package is external and -% we are using the corresponding spec -external(Package) :- - version(Package, Version), version_weight(Package, Weight), - external_version_declared(Package, Version, Weight, ID). +% a package is external if we are using an external spec for it +external(Package) :- external_spec_selected(Package, _). + +% we can't use the weight for an external version if we don't use the +% corresponding external spec. +:- version(Package, Version), + version_weight(Package, Weight), + external_version_declared(Package, Version, Weight, ID), + not external(Package). % determine if an external spec has been selected -external_spec_selected(ID, Package, LocalIndex) :- - version(Package, Version), version_weight(Package, Weight), - external_spec_index(ID, Package, LocalIndex), - external_version_declared(Package, Version, Weight, LocalIndex), - external_spec_conditions_hold(ID, Package). - -% determine if all the conditions on an external spec hold. If they do -% the spec can be selected. -external_spec_conditions_hold(ID, Package) :- - attr(Name, Arg1) : external_spec_condition(ID, Name, Arg1); - attr(Name, Arg1, Arg2) : external_spec_condition(ID, Name, Arg1, Arg2); - attr(Name, Arg1, Arg2, Arg3) : external_spec_condition(ID, Name, Arg1, Arg2, Arg3); - external_spec(ID, Package); - node(Package). +external_spec_selected(Package, LocalIndex) :- + external_conditions_hold(Package, LocalIndex), + node(Package). + +external_conditions_hold(Package, LocalIndex) :- + possible_external(ID, Package, LocalIndex), condition_holds(ID). % it cannot happen that a spec is external, but none of the external specs % conditions hold. -:- external(Package), not external_spec_conditions_hold(_, Package). +:- external(Package), not external_conditions_hold(Package, _). +#defined possible_external/3. #defined external_spec_index/3. #defined external_spec_condition/3. #defined external_spec_condition/4. diff --git a/lib/spack/spack/solver/display.lp b/lib/spack/spack/solver/display.lp index ad4cfdd2ec..f67c4b6c3c 100644 --- a/lib/spack/spack/solver/display.lp +++ b/lib/spack/spack/solver/display.lp @@ -29,4 +29,4 @@ #show compiler_weight/2. #show node_target_match/2. #show node_target_weight/2. -#show external_spec_selected/3. +#show external_spec_selected/2. |