concretizer: added support for versioned virtual specs

This commit is contained in:
Massimiliano Culpo 2020-09-30 17:27:19 +02:00 committed by Todd Gamblin
parent 48eb50921a
commit 28afdb9530
2 changed files with 96 additions and 29 deletions

View File

@ -467,13 +467,19 @@ def _register_rule_for_cores(self, rule_str):
def fact(self, head): def fact(self, head):
"""ASP fact (a rule without a body).""" """ASP fact (a rule without a body)."""
sym = head.symbol() symbols = _normalize(head)
self.out.write("%s.\n" % sym) self.out.write("%s.\n" % ','.join(str(a) for a in symbols))
atom = self.backend.add_atom(sym) atoms = {}
self.backend.add_rule([atom], [], choice=self.cores) for s in symbols:
atoms[s] = self.backend.add_atom(s)
self.backend.add_rule(
[atoms[s] for s in symbols], [], choice=self.cores
)
if self.cores: if self.cores:
self.assumptions.append(atom) for s in symbols:
self.assumptions.append(atoms[s])
def rule(self, head, body): def rule(self, head, body):
"""ASP rule (an implication).""" """ASP rule (an implication)."""
@ -651,6 +657,8 @@ def __init__(self):
self.possible_virtuals = None self.possible_virtuals = None
self.possible_compilers = [] self.possible_compilers = []
self.version_constraints = set() self.version_constraints = set()
self.providers_by_vspec_name = collections.defaultdict(list)
self.virtual_constraints = set()
self.compiler_version_constraints = set() self.compiler_version_constraints = set()
self.post_facts = [] self.post_facts = []
@ -838,21 +846,31 @@ def pkg_rules(self, pkg):
self.package_compiler_defaults(pkg) self.package_compiler_defaults(pkg)
# dependencies # dependencies
self.package_dependencies_rules(pkg)
# virtual preferences
self.virtual_preferences(
pkg.name,
lambda v, p, i: self.gen.fact(
fn.pkg_provider_preference(pkg.name, v, p, i)
)
)
def package_dependencies_rules(self, pkg):
"""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()):
named_cond = cond.copy() named_cond = cond.copy()
if not named_cond.name: named_cond.name = named_cond.name or pkg.name
named_cond.name = pkg.name
if cond == spack.spec.Spec():
for t in sorted(dep.type): for t in sorted(dep.type):
if cond == spack.spec.Spec():
self.gen.fact( self.gen.fact(
fn.declared_dependency( fn.declared_dependency(
dep.pkg.name, dep.spec.name, t dep.pkg.name, dep.spec.name, t
) )
) )
else: else:
for t in sorted(dep.type):
self.gen.rule( self.gen.rule(
fn.declared_dependency( fn.declared_dependency(
dep.pkg.name, dep.spec.name, t dep.pkg.name, dep.spec.name, t
@ -863,9 +881,18 @@ def pkg_rules(self, pkg):
) )
# add constraints on the dependency from dep spec. # add constraints on the dependency from dep spec.
clauses = self.spec_clauses(dep.spec)
if spack.repo.path.is_virtual(dep.spec.name): if spack.repo.path.is_virtual(dep.spec.name):
clauses = [] self.virtual_constraints.add(str(dep.spec))
self.gen.rule(
head=fn.single_provider_for(
str(dep.spec.name), str(dep.spec.versions)
),
body=self.gen._and(
*self.spec_clauses(named_cond, body=True)
)
)
else:
clauses = self.spec_clauses(dep.spec)
for clause in clauses: for clause in clauses:
self.gen.rule( self.gen.rule(
clause, clause,
@ -876,14 +903,6 @@ def pkg_rules(self, pkg):
) )
self.gen.newline() self.gen.newline()
# virtual preferences
self.virtual_preferences(
pkg.name,
lambda v, p, i: self.gen.fact(
fn.pkg_provider_preference(pkg.name, v, p, i)
)
)
def virtual_preferences(self, pkg_name, func): def virtual_preferences(self, pkg_name, func):
"""Call func(vspec, provider, i) for each of pkg's provider prefs.""" """Call func(vspec, provider, i) for each of pkg's provider prefs."""
config = spack.config.get("packages") config = spack.config.get("packages")
@ -1191,9 +1210,19 @@ def virtual_providers(self):
# what provides what # what provides what
for vspec in sorted(self.possible_virtuals): for vspec in sorted(self.possible_virtuals):
self.gen.fact(fn.virtual(vspec)) self.gen.fact(fn.virtual(vspec))
for provider in sorted(spack.repo.path.providers_for(vspec)): all_providers = sorted(spack.repo.path.providers_for(vspec))
# TODO: handle versioned and conditional virtuals for idx, provider in enumerate(all_providers):
self.gen.fact(fn.provides_virtual(provider.name, vspec)) self.gen.fact(fn.provides_virtual(provider.name, vspec))
possible_provider_fn = fn.possible_provider(
vspec, provider.name, idx
)
item = (idx, provider, possible_provider_fn)
self.providers_by_vspec_name[vspec].append(item)
clauses = self.spec_clauses(provider, body=True)
for clause in clauses:
self.gen.rule(clause, possible_provider_fn)
self.gen.newline()
self.gen.newline()
def generate_possible_compilers(self, specs): def generate_possible_compilers(self, specs):
compilers = compilers_for_default_arch() compilers = compilers_for_default_arch()
@ -1240,6 +1269,27 @@ def define_version_constraints(self):
) )
self.gen.newline() self.gen.newline()
def define_virtual_constraints(self):
for vspec_str in sorted(self.virtual_constraints):
vspec = spack.spec.Spec(vspec_str)
self.gen.h2("Virtual spec: {0}".format(vspec_str))
providers = spack.repo.path.providers_for(vspec_str)
candidates = self.providers_by_vspec_name[vspec.name]
possible_providers = [
func for idx, spec, func in candidates if spec in providers
]
self.gen.newline()
single_provider_for = fn.single_provider_for(
vspec.name, vspec.versions
)
one_of_the_possibles = self.gen.one_of(*possible_providers)
single_provider_rule = "{0} :- {1}.\n{1} :- {0}.\n".format(
single_provider_for, str(one_of_the_possibles)
)
self.gen.out.write(single_provider_rule)
self.gen.control.add("base", [], single_provider_rule)
def define_compiler_version_constraints(self): def define_compiler_version_constraints(self):
compiler_list = spack.compilers.all_compiler_specs() compiler_list = spack.compilers.all_compiler_specs()
compiler_list = list(sorted(set(compiler_list))) compiler_list = list(sorted(set(compiler_list)))
@ -1318,17 +1368,30 @@ def setup(self, driver, specs):
self.gen.h2('Spec: %s' % str(dep)) self.gen.h2('Spec: %s' % str(dep))
if dep.virtual: if dep.virtual:
self.gen.fact(fn.virtual_node(dep.name)) for clause in self.virtual_spec_clauses(dep):
self.gen.fact(clause)
else: else:
for clause in self.spec_clauses(dep): for clause in self.spec_clauses(dep):
self.gen.fact(clause) self.gen.fact(clause)
self.gen.h1("Virtual Constraints")
self.define_virtual_constraints()
self.gen.h1("Version Constraints") self.gen.h1("Version Constraints")
self.define_version_constraints() self.define_version_constraints()
self.gen.h1("Compiler Version Constraints") self.gen.h1("Compiler Version Constraints")
self.define_compiler_version_constraints() self.define_compiler_version_constraints()
def virtual_spec_clauses(self, dep):
assert dep.virtual
self.virtual_constraints.add(str(dep))
clauses = [
fn.virtual_node(dep.name),
fn.single_provider_for(str(dep.name), str(dep.versions))
]
return clauses
class SpecBuilder(object): class SpecBuilder(object):
"""Class with actions to rebuild a spec from ASP results.""" """Class with actions to rebuild a spec from ASP results."""

View File

@ -113,7 +113,11 @@ external(Package) :- external_only(Package), node(Package).
% if an external version is selected, the package is external and % if an external version is selected, the package is external and
% we are using the corresponding spec % we are using the corresponding spec
external(Package), external_spec(Package, ID) :- external(Package) :-
version(Package, Version), version_weight(Package, Weight),
external_version_declared(Package, Version, Weight, ID).
external_spec(Package, ID) :-
version(Package, Version), version_weight(Package, Weight), version(Package, Version), version_weight(Package, Weight),
external_version_declared(Package, Version, Weight, ID). external_version_declared(Package, Version, Weight, ID).