concretizer: modified weights for providers and matching for externals
This commit address the case of concretizing a root spec with a transitive conditional dependency on a virtual package, provided by an external. Before these modifications default variant values for the dependency bringing in the virtual package were not respected, and the external package providing the virtual was added to the DAG. The issue stems from two facts: - Selecting a provider has higher precedence than selecting default variants - To ensure that an external is preferred, we used a negative weight To solve it we shift all the providers weight so that: - External providers have a weight of 0 - Non external provider have a weight of 10 or more Using a weight of zero for external providers is such that having an external provider, if present, or not having a provider at all has the same effect on the higher priority minimization. Also fixed a few minor bugs in concretize.lp, that were causing spurious entries in the final answer set. Cleaned concretize.lp from leftover rules.
This commit is contained in:
parent
ca31f52be3
commit
7ffad278d3
6 changed files with 56 additions and 35 deletions
|
@ -634,7 +634,7 @@ def solve(
|
||||||
|
|
||||||
# With a grounded program, we can run the solve.
|
# With a grounded program, we can run the solve.
|
||||||
result = Result()
|
result = Result()
|
||||||
models = [] # stable moodels if things go well
|
models = [] # stable models if things go well
|
||||||
cores = [] # unsatisfiable cores if they do not
|
cores = [] # unsatisfiable cores if they do not
|
||||||
|
|
||||||
def on_model(model):
|
def on_model(model):
|
||||||
|
@ -973,7 +973,7 @@ def virtual_preferences(self, pkg_name, func):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for i, provider in enumerate(providers):
|
for i, provider in enumerate(providers):
|
||||||
func(vspec, provider, i)
|
func(vspec, provider, i + 10)
|
||||||
|
|
||||||
def provider_defaults(self):
|
def provider_defaults(self):
|
||||||
self.gen.h2("Default virtual providers")
|
self.gen.h2("Default virtual providers")
|
||||||
|
|
|
@ -57,10 +57,11 @@ provider(Package, Virtual)
|
||||||
:- node(Package), provides_virtual(Package, Virtual).
|
:- node(Package), provides_virtual(Package, Virtual).
|
||||||
|
|
||||||
% for any virtual, there can be at most one provider in the DAG
|
% for any virtual, there can be at most one provider in the DAG
|
||||||
0 { provider(Package, Virtual) : node(Package) } 1 :- virtual(Virtual).
|
0 { provider(Package, Virtual) :
|
||||||
|
node(Package), provides_virtual(Package, Virtual) } 1 :- virtual(Virtual).
|
||||||
|
|
||||||
% give dependents the virtuals they want
|
% give dependents the virtuals they want
|
||||||
provider_weight(Dependency, -10)
|
provider_weight(Dependency, 0)
|
||||||
:- virtual(Virtual), depends_on(Package, Dependency),
|
:- virtual(Virtual), depends_on(Package, Dependency),
|
||||||
provider(Dependency, Virtual),
|
provider(Dependency, Virtual),
|
||||||
external(Dependency).
|
external(Dependency).
|
||||||
|
@ -84,6 +85,7 @@ provider_weight(Dependency, 100)
|
||||||
:- virtual(Virtual),
|
:- virtual(Virtual),
|
||||||
provider(Dependency, Virtual),
|
provider(Dependency, Virtual),
|
||||||
depends_on(Package, Dependency),
|
depends_on(Package, Dependency),
|
||||||
|
not external(Dependency),
|
||||||
not pkg_provider_preference(Package, Virtual, Dependency, _),
|
not pkg_provider_preference(Package, Virtual, Dependency, _),
|
||||||
not default_provider_preference(Virtual, Dependency, _).
|
not default_provider_preference(Virtual, Dependency, _).
|
||||||
|
|
||||||
|
@ -355,37 +357,27 @@ node_compiler_version(Package, Compiler, Version) :- node_compiler_version_hard(
|
||||||
not compiler_supports_os(Compiler, Version, OS),
|
not compiler_supports_os(Compiler, Version, OS),
|
||||||
not allow_compiler(Compiler, Version).
|
not allow_compiler(Compiler, Version).
|
||||||
|
|
||||||
% dependencies imply we should try to match hard compiler constraints
|
|
||||||
% todo: look at what to do about intersecting constraints here. we'd
|
|
||||||
% ideally go with the "lowest" pref in the DAG
|
|
||||||
node_compiler_match_pref(Package, Compiler)
|
|
||||||
:- node_compiler_hard(Package, Compiler).
|
|
||||||
node_compiler_match_pref(Dependency, Compiler)
|
|
||||||
:- depends_on(Package, Dependency),
|
|
||||||
node_compiler_match_pref(Package, Compiler),
|
|
||||||
not node_compiler_hard(Dependency, _).
|
|
||||||
compiler_match(Package, 1)
|
|
||||||
:- node_compiler(Package, Compiler),
|
|
||||||
node_compiler_match_pref(Package, Compiler).
|
|
||||||
|
|
||||||
% If the compiler is what was prescribed from command line etc.
|
% If the compiler is what was prescribed from command line etc.
|
||||||
% or is the same as a root node, there is a version match
|
% or is the same as a root node, there is a version match
|
||||||
|
|
||||||
% Compiler prescribed from command line
|
% Compiler prescribed in the root spec
|
||||||
node_compiler_version_match_pref(Package, Compiler, V)
|
node_compiler_version_match_pref(Package, Compiler, V)
|
||||||
:- node_compiler_hard(Package, Compiler),
|
:- node_compiler_hard(Package, Compiler),
|
||||||
node_compiler_version(Package, Compiler, V).
|
node_compiler_version(Package, Compiler, V),
|
||||||
|
not external(Package).
|
||||||
|
|
||||||
% Compiler inherited from a root node
|
% Compiler inherited from a parent node
|
||||||
node_compiler_version_match_pref(Dependency, Compiler, V)
|
node_compiler_version_match_pref(Dependency, Compiler, V)
|
||||||
:- depends_on(Package, Dependency),
|
:- depends_on(Package, Dependency),
|
||||||
node_compiler_version_match_pref(Package, Compiler, V),
|
node_compiler_version_match_pref(Package, Compiler, V),
|
||||||
|
node_compiler_version(Dependency, Compiler, V),
|
||||||
not node_compiler_hard(Dependency, Compiler).
|
not node_compiler_hard(Dependency, Compiler).
|
||||||
|
|
||||||
% Compiler inherited from the root package
|
% Compiler inherited from the root package
|
||||||
node_compiler_version_match_pref(Dependency, Compiler, V)
|
node_compiler_version_match_pref(Dependency, Compiler, V)
|
||||||
:- depends_on(Package, Dependency),
|
:- depends_on(Package, Dependency),
|
||||||
node_compiler_version(Package, Compiler, V), root(Package),
|
node_compiler_version(Package, Compiler, V), root(Package),
|
||||||
|
node_compiler_version(Dependency, Compiler, V),
|
||||||
not node_compiler_hard(Dependency, Compiler).
|
not node_compiler_hard(Dependency, Compiler).
|
||||||
|
|
||||||
compiler_version_match(Package, 1)
|
compiler_version_match(Package, 1)
|
||||||
|
@ -516,25 +508,18 @@ root(Dependency, 1) :- not root(Dependency), node(Dependency).
|
||||||
: provider_weight(Provider, Weight), root(Provider)
|
: provider_weight(Provider, Weight), root(Provider)
|
||||||
}.
|
}.
|
||||||
|
|
||||||
% Next, we want to minimize:
|
% Next, we want to minimize the weights of the providers
|
||||||
% 1. The weights of the providers
|
% i.e. use as much as possible the most preferred providers
|
||||||
% 2. The version weight of the providers
|
|
||||||
% i.e. use as much as possible the most preferred
|
|
||||||
% providers at latest versions
|
|
||||||
#minimize{
|
#minimize{
|
||||||
Weight@11,Provider
|
Weight@11,Provider
|
||||||
: provider_weight(Provider, Weight), not root(Provider)
|
: provider_weight(Provider, Weight), not root(Provider)
|
||||||
}.
|
}.
|
||||||
#minimize{
|
|
||||||
Weight@10,Provider
|
|
||||||
: provider_weight(Provider, _), version_weight(Provider, Weight)
|
|
||||||
}.
|
|
||||||
|
|
||||||
% For external packages it's more important than for others
|
% For external packages it's more important than for others
|
||||||
% to match the compiler
|
% to match the compiler with their parent node
|
||||||
#minimize{
|
#maximize{
|
||||||
Weight@10,Package
|
Weight@10,Package
|
||||||
: compiler_weight(Package, Weight), external(Package)
|
: compiler_version_match(Package, Weight), external(Package)
|
||||||
}.
|
}.
|
||||||
|
|
||||||
% Then try to use as much as possible:
|
% Then try to use as much as possible:
|
||||||
|
|
|
@ -17,12 +17,10 @@
|
||||||
#show node_flag_source/2.
|
#show node_flag_source/2.
|
||||||
#show no_flags/2.
|
#show no_flags/2.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#show variant_not_default/4.
|
#show variant_not_default/4.
|
||||||
#show provider_weight/2.
|
#show provider_weight/2.
|
||||||
#show version_weight/2.
|
#show version_weight/2.
|
||||||
#show compiler_match/2.
|
#show compiler_version_match/2.
|
||||||
#show compiler_weight/2.
|
#show compiler_weight/2.
|
||||||
#show node_target_match/2.
|
#show node_target_match/2.
|
||||||
#show node_target_weight/2.
|
#show node_target_weight/2.
|
||||||
|
|
|
@ -872,3 +872,14 @@ def test_external_that_would_require_a_virtual_dependency(self):
|
||||||
|
|
||||||
assert s.external
|
assert s.external
|
||||||
assert 'stuff' not in s
|
assert 'stuff' not in s
|
||||||
|
|
||||||
|
def test_transitive_conditional_virtual_dependency(self):
|
||||||
|
s = Spec('transitive-conditional-virtual-dependency').concretized()
|
||||||
|
|
||||||
|
# The default for conditional-virtual-dependency is to have
|
||||||
|
# +stuff~mpi, so check that these defaults are respected
|
||||||
|
assert '+stuff' in s['conditional-virtual-dependency']
|
||||||
|
assert '~mpi' in s['conditional-virtual-dependency']
|
||||||
|
|
||||||
|
# 'stuff' is provided by an external package, so check it's present
|
||||||
|
assert 'externalvirtual' in s
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
|
||||||
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
class ConditionalVirtualDependency(Package):
|
||||||
|
"""Brings in a virtual dependency if certain conditions are met."""
|
||||||
|
homepage = "https://dev.null"
|
||||||
|
|
||||||
|
version('1.0')
|
||||||
|
|
||||||
|
variant('stuff', default=True, description='nope')
|
||||||
|
variant('mpi', default=False, description='nope')
|
||||||
|
|
||||||
|
depends_on('stuff', when='+stuff')
|
||||||
|
depends_on('mpi', when='+mpi')
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
|
||||||
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
class TransitiveConditionalVirtualDependency(Package):
|
||||||
|
"""Depends on a package with a conditional virtual dependency."""
|
||||||
|
homepage = "https://dev.null"
|
||||||
|
has_code = False
|
||||||
|
phases = []
|
||||||
|
|
||||||
|
version('1.0')
|
||||||
|
depends_on('conditional-virtual-dependency')
|
Loading…
Reference in a new issue