concretizer: handle virtual provider preferences from packages.yaml

This commit is contained in:
Todd Gamblin 2019-10-13 21:06:54 -07:00
parent 36ec66d997
commit b4e6d9d28e
2 changed files with 83 additions and 11 deletions

View File

@ -19,6 +19,7 @@
import spack
import spack.cmd
import spack.config
import spack.spec
import spack.package
import spack.package_prefs
@ -61,7 +62,14 @@ class AspObject(object):
def _id(thing):
"""Quote string if needed for it to be a valid identifier."""
return thing if isinstance(thing, AspObject) else '"%s"' % str(thing)
if isinstance(thing, AspObject):
return thing
elif isinstance(thing, bool):
return '"%s"' % str(thing)
elif isinstance(thing, int):
return str(thing)
else:
return '"%s"' % str(thing)
class AspFunction(AspObject):
@ -73,6 +81,10 @@ def __call__(self, *args):
self.args[:] = args
return self
def __getitem___(self, *args):
self.args[:] = args
return self
def __str__(self):
return "%s(%s)" % (
self.name, ', '.join(_id(arg) for arg in self.args))
@ -128,6 +140,7 @@ def __init__(self, out):
self.out = out
self.func = AspFunctionBuilder()
self.possible_versions = {}
self.possible_virtuals = None
def title(self, name, char):
self.out.write('\n')
@ -309,6 +322,33 @@ def pkg_rules(self, pkg):
self._and(*self.spec_clauses(named_cond, body=True))
)
# virtual preferences
self.virtual_preferences(
pkg.name,
lambda v, p, i: self.fact(
fn.pkg_provider_preference(pkg.name, v, p, i)
)
)
def virtual_preferences(self, pkg_name, func):
"""Call func(vspec, provider, i) for each of pkg's provider prefs."""
config = spack.config.get("packages")
pkg_prefs = config.get(pkg_name, {}).get("providers", {})
for vspec, providers in pkg_prefs.items():
if vspec not in self.possible_virtuals:
continue
for i, provider in enumerate(providers):
func(vspec, provider, i)
def provider_defaults(self):
self.h2("Default virtual providers")
assert self.possible_virtuals is not None
self.virtual_preferences(
"all",
lambda v, p, i: self.fact(fn.default_provider_preference(v, p, i))
)
def spec_clauses(self, spec, body=False):
"""Return a list of clauses for a spec mandates are true.
@ -358,7 +398,12 @@ class Body(object):
# variants
for vname, variant in spec.variants.items():
clauses.append(f.variant(spec.name, vname, variant.value))
value = variant.value
if isinstance(value, tuple):
for v in value:
clauses.append(f.variant(spec.name, vname, v))
else:
clauses.append(f.variant(spec.name, vname, variant.value))
# compiler and compiler version
if spec.compiler:
@ -397,9 +442,12 @@ def arch_defaults(self):
self.fact(fn.arch_os_default(default_arch.os))
self.fact(fn.arch_target_default(default_arch.target))
def virtual_providers(self, virtuals):
def virtual_providers(self):
self.h2("Virtual providers")
for vspec in virtuals:
assert self.possible_virtuals is not None
# what provides what
for vspec in self.possible_virtuals:
self.fact(fn.virtual(vspec))
for provider in spack.repo.path.providers_for(vspec):
# TODO: handle versioned virtuals
@ -417,10 +465,12 @@ def generate_asp_program(self, specs):
pkg_names = set(spec.fullname for spec in specs)
possible = set()
virtuals = set()
self.possible_virtuals = set()
for name in pkg_names:
pkg = spack.repo.path.get_pkg_class(name)
possible.update(pkg.possible_dependencies(virtuals=virtuals))
possible.update(
pkg.possible_dependencies(virtuals=self.possible_virtuals)
)
pkgs = set(possible) | set(pkg_names)
@ -433,7 +483,8 @@ def generate_asp_program(self, specs):
self.h1('General Constraints')
self.compiler_defaults()
self.arch_defaults()
self.virtual_providers(virtuals)
self.virtual_providers()
self.provider_defaults()
self.h1('Package Constraints')
for pkg in pkgs:

View File

@ -28,12 +28,30 @@ depends_on(P, D) :- declared_dependency(P, D), not virtual(D), node(P).
provider(P, V) :- node(P), provides_virtual(P, V).
1 { provider(P, V) : node(P) } 1 :- virtual(V).
% give dependents the virtuals they want
provider_weight(D, N)
:- virtual(V), depends_on(P, D), provider(D, V),
pkg_provider_preference(P, V, D, N).
provider_weight(D, N)
:- virtual(V), depends_on(P, D), provider(D, V),
not pkg_provider_preference(P, V, D, _),
default_provider_preference(V, D, N).
% if there's no preference for something, it costs 100 to discourage its
% use with minimization
provider_weight(D, 100)
:- virtual(V), depends_on(P, D), provider(D, V),
not pkg_provider_preference(P, V, D, _),
not default_provider_preference(V, D, _).
% pick most preferred virtual providers
#minimize{ N@2,D : provider_weight(D, N) }.
% all nodes must be reachable from some root
% TODO: this doesn't seem to be working yet
needed(D) :- depends_on(_, D), node(D).
needed(P) :- root(P).
needed(D) :- root(D), node(D).
needed(D) :- root(P), depends_on(P, D).
needed(D) :- needed(P), depends_on(P, D), node(P).
:- node(P), not needed(P).
#defined root/1.
% real dependencies imply new nodes.
node(D) :- node(P), depends_on(P, D).
@ -43,6 +61,9 @@ node(D) :- node(P), depends_on(P, D).
#defined declared_dependency/2.
#defined virtual/1.
#defined provides_virtual/2.
#defined pkg_provider_preference/4.
#defined default_provider_preference/3.
#defined root/1.
%-----------------------------------------------------------------------------
% Variant semantics