summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/solver/asp.py163
-rw-r--r--lib/spack/spack/solver/concretize.lp29
2 files changed, 105 insertions, 87 deletions
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
index d32713da69..790e545878 100644
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -53,6 +53,26 @@ else:
from collections import Sequence
+#: Enumeration like object to mark version provenance
+version_provenance = collections.namedtuple( # type: ignore
+ 'VersionProvenance', ['external', 'packages_yaml', 'package_py', 'spec']
+)(spec=0, external=1, packages_yaml=2, package_py=3)
+
+#: String representation of version origins, to emit legible
+# facts for the ASP solver
+version_origin_str = {
+ 0: 'spec',
+ 1: 'external',
+ 2: 'packages_yaml',
+ 3: 'package_py'
+}
+
+#: Named tuple to contain information on declared versions
+DeclaredVersion = collections.namedtuple(
+ 'DeclaredVersion', ['version', 'idx', 'origin']
+)
+
+
def issequence(obj):
if isinstance(obj, string_types):
return False
@@ -417,10 +437,11 @@ class SpackSolverSetup(object):
def __init__(self):
self.gen = None # set by setup()
+
+ self.declared_versions = {}
self.possible_versions = {}
- self.versions_in_package_py = {}
self.deprecated_versions = {}
- self.versions_from_externals = {}
+
self.possible_virtuals = None
self.possible_compilers = []
self.variant_values_from_specs = set()
@@ -441,54 +462,23 @@ class SpackSolverSetup(object):
This uses self.possible_versions so that we include any versions
that arise from a spec.
"""
+ def key_fn(version):
+ # Origins are sorted by order of importance:
+ # 1. Spec from command line
+ # 2. Externals
+ # 3. Package preferences
+ # 4. Directives in package.py
+ return version.origin, version.idx
+
pkg = packagize(pkg)
+ declared_versions = self.declared_versions[pkg.name]
+ most_to_least_preferred = sorted(declared_versions, key=key_fn)
- config = spack.config.get("packages")
- version_prefs = config.get(pkg.name, {}).get("version", {})
- priority = dict((v, i) for i, v in enumerate(version_prefs))
-
- # The keys below show the order of precedence of factors used
- # to select a version when concretizing. The item with
- # the "largest" key will be selected.
- #
- # NOTE: When COMPARING VERSIONS, the '@develop' version is always
- # larger than other versions. BUT when CONCRETIZING,
- # the largest NON-develop version is selected by default.
- keyfn = lambda v: (
- # ------- Special direction from the user
- # Respect order listed in packages.yaml
- -priority.get(v, 0),
-
- # The preferred=True flag (packages or packages.yaml or both?)
- pkg.versions.get(v, {}).get('preferred', False),
-
- # ------- Regular case: use latest non-develop version by default.
- # Avoid @develop version, which would otherwise be the "largest"
- # in straight version comparisons
- not v.isdevelop(),
-
- # Compare the version itself
- # This includes the logic:
- # a) develop > everything (disabled by "not v.isdevelop() above)
- # b) numeric > non-numeric
- # c) Numeric or string comparison
- v)
-
- # Compute which versions appear only in packages.yaml
- from_externals = self.versions_from_externals[pkg.name]
- from_package_py = self.versions_in_package_py[pkg.name]
- only_from_externals = from_externals - from_package_py
-
- # These versions don't need a default weight, as they are
- # already weighted in a more favorable way when accounting
- # for externals. Assigning them a default weight would be
- # equivalent to state that they are also declared in
- # the package.py file
- considered = self.possible_versions[pkg.name] - only_from_externals
- most_to_least_preferred = sorted(considered, key=keyfn, reverse=True)
-
- for i, v in enumerate(most_to_least_preferred):
- self.gen.fact(fn.version_declared(pkg.name, v, i))
+ for weight, declared_version in enumerate(most_to_least_preferred):
+ self.gen.fact(fn.version_declared(
+ pkg.name, declared_version.version, weight,
+ version_origin_str[declared_version.origin]
+ ))
# Declare deprecated versions for this package, if any
deprecated = self.deprecated_versions[pkg.name]
@@ -801,20 +791,22 @@ class SpackSolverSetup(object):
externals = data.get('externals', [])
external_specs = [spack.spec.Spec(x['spec']) for x in externals]
- # Compute versions with appropriate weights. This accounts for the
- # fact that we should prefer more recent versions, but specs in
- # packages.yaml may not be ordered in that sense.
+ # Order the external versions to prefer more recent versions
+ # even if specs in packages.yaml are not ordered that way
external_versions = [
- (x.version, local_idx)
- for local_idx, x in enumerate(external_specs)
+ (x.version, external_id)
+ for external_id, x in enumerate(external_specs)
]
external_versions = [
- (v, -(w + 1), local_idx)
- for w, (v, local_idx) in enumerate(sorted(external_versions))
+ (v, idx, external_id)
+ for idx, (v, external_id) in
+ enumerate(sorted(external_versions, reverse=True))
]
- for version, weight, id in external_versions:
- self.gen.fact(fn.external_version_declared(
- pkg_name, str(version), weight, id
+ for version, idx, external_id in external_versions:
+ self.declared_versions[pkg_name].append(DeclaredVersion(
+ version=version,
+ idx=idx,
+ origin=version_provenance.external
))
# Declare external conditions with a local index into packages.yaml
@@ -823,7 +815,6 @@ class SpackSolverSetup(object):
self.gen.fact(
fn.possible_external(condition_id, pkg_name, local_idx)
)
- self.versions_from_externals[spec.name].add(spec.version)
self.possible_versions[spec.name].add(spec.version)
self.gen.newline()
@@ -872,17 +863,6 @@ class SpackSolverSetup(object):
str(preferred.architecture.target), pkg_name, -30
))
- def preferred_versions(self, pkg_name):
- packages_yaml = spack.config.get('packages')
- versions = packages_yaml.get(pkg_name, {}).get('version', [])
- if not versions:
- return
-
- for idx, version in enumerate(reversed(versions)):
- self.gen.fact(
- fn.preferred_version_declared(pkg_name, version, -(idx + 1))
- )
-
def flag_defaults(self):
self.gen.h2("Compiler flag defaults")
@@ -1032,23 +1012,56 @@ class SpackSolverSetup(object):
def build_version_dict(self, possible_pkgs, specs):
"""Declare any versions in specs not declared in packages."""
+ self.declared_versions = collections.defaultdict(list)
self.possible_versions = collections.defaultdict(set)
- self.versions_in_package_py = collections.defaultdict(set)
- self.versions_from_externals = collections.defaultdict(set)
self.deprecated_versions = collections.defaultdict(set)
+ packages_yaml = spack.config.get("packages")
+ packages_yaml = _normalize_packages_yaml(packages_yaml)
for pkg_name in possible_pkgs:
pkg = spack.repo.get(pkg_name)
- for v, version_info in pkg.versions.items():
- self.versions_in_package_py[pkg_name].add(v)
+
+ # All the versions from the corresponding package.py file. Since concepts
+ # like being a "develop" version or being preferred exist only at a
+ # package.py level, sort them in this partial list here
+ def key_fn(item):
+ version, info = item
+ # When COMPARING VERSIONS, the '@develop' version is always
+ # larger than other versions. BUT when CONCRETIZING, the largest
+ # NON-develop version is selected by default.
+ return info.get('preferred', False), not version.isdevelop(), version
+
+ for idx, item in enumerate(sorted(
+ pkg.versions.items(), key=key_fn, reverse=True
+ )):
+ v, version_info = item
self.possible_versions[pkg_name].add(v)
+ self.declared_versions[pkg_name].append(DeclaredVersion(
+ version=v, idx=idx, origin=version_provenance.package_py
+ ))
deprecated = version_info.get('deprecated', False)
if deprecated:
self.deprecated_versions[pkg_name].add(v)
+ # All the preferred version from packages.yaml, versions in external
+ # specs will be computed later
+ version_preferences = packages_yaml.get(pkg_name, {}).get("version", [])
+ for idx, v in enumerate(version_preferences):
+ self.declared_versions[pkg_name].append(DeclaredVersion(
+ version=v, idx=idx, origin=version_provenance.packages_yaml
+ ))
+
for spec in specs:
for dep in spec.traverse():
if dep.versions.concrete:
+ # Concrete versions used in abstract specs from cli. They
+ # all have idx equal to 0, which is the best possible. In
+ # any case they will be used due to being set from the cli.
+ self.declared_versions[dep.name].append(DeclaredVersion(
+ version=dep.version,
+ idx=0,
+ origin=version_provenance.spec
+ ))
self.possible_versions[dep.name].add(dep.version)
def _supported_targets(self, compiler_name, compiler_version, targets):
@@ -1344,7 +1357,6 @@ class SpackSolverSetup(object):
Arguments:
specs (list): list of Specs to solve
-
"""
self._condition_id_counter = itertools.count()
@@ -1394,7 +1406,6 @@ class SpackSolverSetup(object):
self.gen.h2('Package preferences: %s' % pkg)
self.preferred_variants(pkg)
self.preferred_targets(pkg)
- self.preferred_versions(pkg)
# Inject dev_path from environment
env = ev.active_environment()
diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp
index 0ee2e08a18..8006e5f94f 100644
--- a/lib/spack/spack/solver/concretize.lp
+++ b/lib/spack/spack/solver/concretize.lp
@@ -11,6 +11,15 @@
% Version semantics
%-----------------------------------------------------------------------------
+% Versions are declared with a weight and an origin, which indicates where the
+% version was declared (e.g. "package_py" or "external").
+version_declared(Package, Version, Weight) :- version_declared(Package, Version, Weight, _).
+
+% We can't emit the same version **with the same weight** from two different sources
+:- version_declared(Package, Version, Weight, Origin1),
+ version_declared(Package, Version, Weight, Origin2),
+ Origin1 != Origin2.
+
% versions are declared w/priority -- declared with priority implies declared
version_declared(Package, Version) :- version_declared(Package, Version, _).
@@ -23,11 +32,8 @@ version_declared(Package, Version) :- version_declared(Package, Version, _).
deprecated(Package, Version) :- version(Package, Version), deprecated_version(Package, Version).
possible_version_weight(Package, Weight)
- :- version(Package, Version), version_declared(Package, Version, Weight),
- not preferred_version_declared(Package, Version, _).
-
-possible_version_weight(Package, Weight)
- :- version(Package, Version), preferred_version_declared(Package, Version, Weight).
+ :- version(Package, Version),
+ version_declared(Package, Version, Weight).
1 { version_weight(Package, Weight) : possible_version_weight(Package, Weight) } 1 :- node(Package).
@@ -38,7 +44,6 @@ possible_version_weight(Package, Weight)
version_satisfies(Package, Constraint)
:- version(Package, Version), version_satisfies(Package, Constraint, Version).
-#defined preferred_version_declared/3.
#defined version_satisfies/3.
#defined deprecated_version/2.
@@ -294,11 +299,13 @@ attr("node_compiler_version_satisfies", Package, Compiler, Version)
% External semantics
%-----------------------------------------------------------------------------
-% if an external version is declared, it is also declared globally
-version_declared(Package, Version, Weight) :- external_version_declared(Package, Version, Weight, _).
-
% if a package is external its version must be one of the external versions
-1 { version(Package, Version): external_version_declared(Package, Version, _, _) } 1 :- external(Package).
+1 { external_version(Package, Version, Weight):
+ version_declared(Package, Version, Weight, "external") } 1
+ :- external(Package).
+
+version_weight(Package, Weight) :- external_version(Package, Version, Weight).
+version(Package, Version) :- external_version(Package, Version, Weight).
% if a package is not buildable (external_only), only externals are allowed
external(Package) :- external_only(Package), node(Package).
@@ -313,7 +320,7 @@ external(Package) :- external_spec_selected(Package, _).
% corresponding external spec.
:- version(Package, Version),
version_weight(Package, Weight),
- external_version_declared(Package, Version, Weight, ID),
+ version_declared(Package, Version, Weight, "external"),
not external(Package).
% determine if an external spec has been selected