From cde10692b0b12f859098cb8abe5450e72ea1a040 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Sun, 13 Oct 2019 23:35:57 -0700 Subject: concretizer: prioritize versions by package pref, newest, preferred, actual Solver now prefers newer versions like the old concretizer. Prefer package preferences from packages.yaml, preferred=True, package definition, and finally each version itself. --- lib/spack/spack/solver/asp.py | 48 ++++++++++++++++++++++++++++++++++-- lib/spack/spack/solver/concretize.lp | 7 ++++++ 2 files changed, 53 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 05954d6335..85b42453a4 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -195,8 +195,44 @@ class AspGenerator(object): that arise from a spec. """ pkg = packagize(pkg) - for v in self.possible_versions[pkg.name]: - self.fact(fn.version_declared(pkg.name, v)) + + 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) + + most_to_least_preferred = sorted( + self.possible_versions[pkg.name], key=keyfn, reverse=True + ) + + for i, v in enumerate(most_to_least_preferred): + self.fact(fn.version_declared(pkg.name, v, i)) def spec_versions(self, spec): """Return list of clauses expressing spec's version constraints.""" @@ -718,6 +754,14 @@ def solve(specs, dump=None, models=0): # 1 is "competition" format with just optimal answer # 2 is JSON format with all explored answers '--outf=1', + # Use a highest priority criteria-first optimization + # strategy, which means we'll explore recent + # versions, preferred packages first. This works + # well because Spack solutions are pretty easy to + # find -- there are just a lot of them. Without + # this, it can take a VERY long time to find good + # solutions, and a lot of models are explored. + '--opt-strategy=bb,hier', input=program, output=output, error=warnings, diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp index e43feb4ae1..3b58cb4e80 100644 --- a/lib/spack/spack/solver/concretize.lp +++ b/lib/spack/spack/solver/concretize.lp @@ -6,12 +6,19 @@ % Version semantics %----------------------------------------------------------------------------- +% versions are declared w/priority -- declared with priority implies declared +version_declared(P, V) :- version_declared(P, V, _). + % If something is a package, it has only one version and that must be a % possible version. 1 { version(P, V) : version_possible(P, V) } 1 :- node(P). % If a version is declared but conflicted, it's not possible. version_possible(P, V) :- version_declared(P, V), not version_conflict(P, V). + +version_weight(P, V, N) :- version(P, V), version_declared(P, V, N). +#minimize{ N@3,P,V : version_weight(P, V, N) }. + #defined version_conflict/2. %----------------------------------------------------------------------------- -- cgit v1.2.3-60-g2f50