diff --git a/lib/spack/spack/cmd/solve.py b/lib/spack/spack/cmd/solve.py index d4dc6a37b7f..f329bfd829e 100644 --- a/lib/spack/spack/cmd/solve.py +++ b/lib/spack/spack/cmd/solve.py @@ -136,13 +136,13 @@ def solve(parser, args): ) fmt = " @K{%%-8d} %%-%ds%%9s %%7s" % maxlen - for i, (idx, build_idx, name) in enumerate(result.criteria, 1): + for i, (installed_cost, build_cost, name) in enumerate(result.criteria, 1): color.cprint( fmt % ( i, name, - "-" if build_idx is None else opt[idx], - opt[idx] if build_idx is None else opt[build_idx], + "-" if build_cost is None else installed_cost, + installed_cost if build_cost is None else build_cost, ) ) print() diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 7d877e397e5..cabeaf7b69a 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -59,6 +59,8 @@ #: may be very large or take the time (sometimes hours) to minimize them minimize_cores = False +# TODO: Remove this after fixing tests that touch it +full_cores = None # backward compatibility functions for clingo ASTs def ast_getter(*names): @@ -111,9 +113,10 @@ def getter(node): def build_criteria_names(costs, tuples): - """Construct an ordered mapping from criteria names to indices in the cost list.""" + """Construct an ordered mapping from criteria names to costs.""" # pull optimization criteria names out of the solution priorities_names = [] + costs = costs[28:] # first 28 are high-priority error avoiding criteria num_fixed = 0 for pred, args in tuples: @@ -123,6 +126,10 @@ def build_criteria_names(costs, tuples): priority, name = args[:2] priority = int(priority) + if priority >= 1000: + # We don't print the error-avoiding criteria + continue + # add the priority of this opt criterion and its name priorities_names.append((priority, name)) @@ -151,12 +158,12 @@ def build_criteria_names(costs, tuples): # mapping from priority to index in cost list indices = dict((p, i) for i, (p, n) in enumerate(priorities_names)) - # make a list that has each name with its build and non-build priority + # make a list that has each name with its build and non-build costs criteria = [ - (p - fixed_priority_offset + num_build, None, name) for p, name in fixed + (costs[p - fixed_priority_offset + num_build], None, name) for p, name in fixed ] for (i, name), (b, _) in zip(installed, build): - criteria.append((indices[i], indices[b], name)) + criteria.append((costs[indices[i]], costs[indices[b]], name)) return criteria @@ -495,6 +502,7 @@ def __init__(self, cores=True): 'depends_on', 'hash', 'node_flag_compiler_default', 'node_flag_source', 'build', 'node', 'root', ] + self.assumption_names = ['error', 'internal_error'] # At high debug levels, include internal errors in output if spack.error.debug > 2: @@ -532,7 +540,7 @@ def fact(self, head): # Only functions relevant for constructing good error messages are # assumptions, and only when using cores. choice = self.cores and symbol.name in self.assumption_names - + choice = False self.backend.add_rule([atom], [], choice=choice) if choice: self.assumptions.append(atom) @@ -650,6 +658,9 @@ def stringify(x): (sym.name, [stringify(a) for a in sym.arguments]) for sym in best_model ] +# print(tuples) +# for t in tuples: +# print(t) answers = builder.build_specs(tuples) # add best spec to the results @@ -1953,6 +1964,103 @@ def node_os(self, pkg, os): def node_target(self, pkg, target): self._arch(pkg).target = target + def conflict_triggered(self, msg): + raise spack.error.SpackError(msg) + + def no_version(self, pkg): + raise RuntimeError("No versions available for package %s" % pkg) + + def versions_conflict(self, pkg, version1, version2): + msg = "No version for %s satisfies @%s and @%s" % (pkg, version1, version2) + raise spack.error.SpackError(msg) + + def version_unsatisfiable(self, pkg, constraint): + raise spack.error.SpackError("%s spec cannot satisfy @%s" % (pkg, constraint)) + + def no_variant_value(self, pkg, variant): + raise RuntimeError("No satisfying value for variant %s of package %s" % (variant, pkg)) + + def multiple_values_sv_variant(self, pkg, variant, value1, value2): + raise spack.error.SpackError("multiple values for variant") + + def invalid_variant_value(self, pkg, variant, value): + raise RuntimeError("Invalid variant value") + + def unnecessary(self, pkg): + raise RuntimeError("something doesn't depend on package %s" % pkg) + + def cyclic_dependency(self, pkg1, pkg2): + raise RuntimeError("Cyclic dependency between %s and %s" % (pkg1, pkg2)) + + def no_provider(self, virtual): + raise RuntimeError("No provider for virtual %s" % virtual) + + def multiple_providers(self, virtual, provider1, provider2): + raise spack.error.SpackError("Multiple providers") + + def invalid_external_spec(self, pkg): + raise spack.error.SpecError("Invalid external spec") + + def inactive_variant_set(self, pkg, variant): + raise spack.error.SpackError("Cannot set conditional variant %s for package %s that cannot be satisfied" % (variant, pkg)) + + def disjoint_variant_values(self, pkg, variant, value1, value2): + msg = "%s variant %s cannot have both values %s and %s, as they come from disjoing value sets" % (pkg, variant, value1, value2) + raise spack.error.SpackError(msg) + + def variant_none_and_other(self, pkg, variant, value): + msg = "%s variant %s cannot have values %s and 'none'" % (pkg, variant, value) + raise spack.error.SpackError(msg) + + def no_os(self, pkg): + raise spack.error.SpackError("No os for %s" % pkg) + + def multiple_os(self, pkg, os1, os2): + msg = "Multiple OS for %s: %s and %s" % (pkg, os1, os2) + raise spack.error.SpackError(msg) + + def os_not_buildable(self, pkg, os1): + msg = "%s os %s is not buildable" % (pkg, os1) + raise spack.error.SpackError(msg) + + def os_incompatible(self, pkg, dep, p_os, d_os): + msg = "%s and dependency %s have incompatible OSs %s and %s" % (pkg, dep, p_os, d_os) + raise spack.error.SpackError(msg) + + def no_target(self, pkg): + raise spack.error.SpackError("%s has no target" % pkg) + + def multiple_targets(self, pkg, target1, target2): + msg = "%s has multiple targets, %s and %s" % (pkg, target1, target2) + raise spack.error.SpackError(msg) + + def target_unsatisfiable(self, pkg, target, constraint): + msg = "%s target cannot satisfy constraint %s" % (pkg, constraint) + raise spack.error.SpackError(msg) + + def target_incompatible(self, pkg, dep): + msg = "No compatible targets possible for %s and %s" % (pkg, dep) + raise spack.error.SpackError(msg) + + def compiler_target_mismatch(self, pkg, target, compiler, version): + msg = "%s compiler %s@%s incompatible with target %s" + raise spack.error.SpackError(msg) + + def invalid_target(self, pkg, target): + msg = "%s target %s is not compatible with this machine" % (pkg, target) + raise spack.error.SpackError(msg) + + def no_compiler_version(self, pkg): + raise spack.error.SpackError("%s has no compiler version" % pkg) + + def multiple_compiler_versions(self, pkg, compiler1, ver1, compiler2, ver2): + msg = "%s compilers %s@%s and %s@%s incompatible" % (pkg, compiler1, ver1, compiler2, ver2) + raise spack.error.SpackError(msg) + + def compiler_os_mismatch(self, pkg, compiler, version, os): + msg = "%s compiler %s@%s incompatible with os %s" % (pkg, compiler, version, os) + raise spack.error.SpackError(msg) + def variant_value(self, pkg, name, value): # FIXME: is there a way not to special case 'dev_path' everywhere? if name == 'dev_path': @@ -2080,6 +2188,9 @@ def build_specs(self, function_tuples): # them here so that directives that build objects (like node and # node_compiler) are called in the right order. function_tuples.sort(key=lambda f: { + "conflict_triggered": -4, + "multiple_values_sv_variant": -4, + "no_variant_value": -4, "hash": -3, "node": -2, "node_compiler": -1, @@ -2091,7 +2202,6 @@ def build_specs(self, function_tuples): continue action = getattr(self, name, None) - # print out unknown actions so we can display them for debugging if not action: msg = "%s(%s)" % (name, ", ".join(str(a) for a in args)) @@ -2103,7 +2213,7 @@ def build_specs(self, function_tuples): # ignore predicates on virtual packages, as they're used for # solving but don't construct anything pkg = args[0] - if spack.repo.path.is_virtual(pkg): + if spack.repo.path.is_virtual(pkg) and name not in ("no_provider", "multiple_providers"): continue # if we've already gotten a concrete spec for this pkg, diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp index b58b17da469..bb1f89c3642 100644 --- a/lib/spack/spack/solver/concretize.lp +++ b/lib/spack/spack/solver/concretize.lp @@ -12,7 +12,7 @@ %----------------------------------------------------------------------------- % each node must have a single version -:- not 1 { version(Package, _) } 1, node(Package). +%:- not 1 { version(Package, _) } 1, node(Package). % each node must have a single platform, os and target :- not 1 { node_platform(Package, _) } 1, node(Package), internal_error("A node must have exactly one platform"). @@ -48,8 +48,14 @@ version_declared(Package, Version) :- version_declared(Package, Version, _). % If something is a package, it has only one version and that must be a % declared version. -1 { version(Package, Version) : version_declared(Package, Version) } 1 - :- node(Package), error("Each node must have exactly one version"). +{ version(Package, Version) : version_declared(Package, Version) } + :- node(Package).%, error("Each node must have exactly one version"). +versions_conflict(Package, Version1, Version2) + :- node(Package), + version(Package, Version1), + version(Package, Version2), + Version1 < Version2. % WLOG +no_version(Package) :- node(Package), not version(Package, _). % A virtual package may have or not a version, but never has more than one :- virtual_node(Package), 2 { version(Package, _) }. @@ -65,14 +71,22 @@ possible_version_weight(Package, Weight) % node_version_satisfies implies that exactly one of the satisfying versions % is the package's version, and vice versa. -1 { version(Package, Version) : version_satisfies(Package, Constraint, Version) } 1 +{ version(Package, Version) : version_satisfies(Package, Constraint, Version) } + :- node_version_satisfies(Package, Constraint). + %error("no version satisfies the given constraints"). +version_unsatisfiable(Package, Constraint) :- node_version_satisfies(Package, Constraint), - error("no version satisfies the given constraints"). + C = #count{ Version : version(Package, Version), version_satisfies(Package, Constraint, Version)}, + C < 1. + node_version_satisfies(Package, Constraint) :- version(Package, Version), version_satisfies(Package, Constraint, Version). #defined version_satisfies/3. #defined deprecated_version/2. +#defined version_unsatisfiable/2. +#defined versions_conflict/3. +#defined no_version/1. %----------------------------------------------------------------------------- % Spec conditions and imposed constraints @@ -165,32 +179,33 @@ node(Dependency) :- node(Package), depends_on(Package, Dependency). % dependencies) and get a two-node unconnected graph needed(Package) :- root(Package). needed(Dependency) :- needed(Package), depends_on(Package, Dependency). -:- node(Package), not needed(Package), - error("All dependencies must be reachable from root"). +unnecessary(Package) :- node(Package), not needed(Package). +% error("All dependencies must be reachable from root"). % Avoid cycles in the DAG % some combinations of conditional dependencies can result in cycles; % this ensures that we solve around them path(Parent, Child) :- depends_on(Parent, Child). path(Parent, Descendant) :- path(Parent, A), depends_on(A, Descendant). -:- path(A, B), path(B, A), error("Cyclic dependencies are not allowed"). +cyclic_dependency(A, B) :- path(A, B), path(B, A).%, error("Cyclic dependencies are not allowed"). #defined error/1. - +#defined unnecessary/1. +#defined cyclic_dependency/2. #defined dependency_type/2. #defined dependency_condition/3. %----------------------------------------------------------------------------- % Conflicts %----------------------------------------------------------------------------- -:- node(Package), +conflict_triggered(Msg) :- node(Package), conflict(Package, TriggerID, ConstraintID, Msg), condition_holds(TriggerID), condition_holds(ConstraintID), - not external(Package), % ignore conflicts for externals - error("A conflict was triggered"). + not external(Package). % ignore conflicts for externals #defined conflict/4. +#defined conflict_triggered/1. %----------------------------------------------------------------------------- % Virtual dependencies @@ -210,8 +225,17 @@ virtual_node(Virtual) % If there's a virtual node, we must select one and only one provider. % The provider must be selected among the possible providers. -1 { provider(Package, Virtual) : possible_provider(Package, Virtual) } 1 - :- virtual_node(Virtual), error("Virtual packages must be satisfied by a unique provider"). +{ provider(Package, Virtual) : possible_provider(Package, Virtual) } + :- virtual_node(Virtual).%, error("Virtual packages must be satisfied by a unique provider"). +no_provider(Virtual) + :- virtual_node(Virtual), + P = #count{ Package : provider(Package, Virtual)}, + P < 1. +multiple_providers(Virtual, P1, P2) + :- virtual_node(Virtual), + provider(P1, Virtual), + provider(P2, Virtual), + P1 < P2. % virtual roots imply virtual nodes, and that one provider is a root virtual_node(Virtual) :- virtual_root(Virtual). @@ -351,9 +375,17 @@ attr("node_compiler_version_satisfies", Package, Compiler, Version) %----------------------------------------------------------------------------- % if a package is external its version must be one of the external versions -1 { external_version(Package, Version, Weight): - version_declared(Package, Version, Weight, "external") } 1 - :- external(Package), error("External package version does not satisfy external spec"). +{ external_version(Package, Version, Weight): + version_declared(Package, Version, Weight, "external") } + :- external(Package).%, error("External package version does not satisfy external spec"). +invalid_external_spec(Package) + :- external(Package), + not external_version(Package, _, _). +invalid_external_spec(Package) + :- external(Package), + external_version(Package, Version1, Weight1), + external_version(Package, Version2, Weight2), + (Version1, Weight1) < (Version2, Weight2). % WLOG version_weight(Package, Weight) :- external_version(Package, Version, Weight). version(Package, Version) :- external_version(Package, Version, Weight). @@ -385,8 +417,8 @@ external_conditions_hold(Package, LocalIndex) :- % it cannot happen that a spec is external, but none of the external specs % conditions hold. -:- external(Package), not external_conditions_hold(Package, _), - error("External package does not satisfy external spec"). +invalid_external_spec(Package) :- external(Package), not external_conditions_hold(Package, _).%, +% error("External package does not satisfy external spec"). #defined possible_external/3. #defined external_spec_index/3. @@ -403,16 +435,18 @@ variant(Package, Variant) :- variant_condition(ID, Package, Variant), condition_holds(ID). % a variant cannot be set if it is not a variant on the package -:- variant_set(Package, Variant), - not variant(Package, Variant), - build(Package), - error("Unsatisfied conditional variants cannot be set"). +inactive_variant_set(Package, Variant) + :- variant_set(Package, Variant), + not variant(Package, Variant), + build(Package). +% error("Unsatisfied conditional variants cannot be set"). % a variant cannot take on a value if it is not a variant of the package -:- variant_value(Package, Variant, _), - not variant(Package, Variant), - build(Package), - error("Unsatisfied conditional variants cannot take on a variant value"). +inactive_variant_set(Package, Variant) + :- variant_value(Package, Variant, _), + not variant(Package, Variant), + build(Package). +% error("Unsatisfied conditional variants cannot take on a variant value"). % if a variant is sticky and not set its value is the default value variant_value(Package, Variant, Value) :- @@ -426,22 +460,25 @@ variant_value(Package, Variant, Value) :- { variant_value(Package, Variant, Value) : variant_possible_value(Package, Variant, Value) -} 1 - :- node(Package), - variant(Package, Variant), - variant_single_value(Package, Variant), - build(Package), - error("Single valued variants must have a single value"). - -% at least one variant value. -1 { - variant_value(Package, Variant, Value) - : variant_possible_value(Package, Variant, Value) } :- node(Package), variant(Package, Variant), - build(Package), - internal_error("All variants must have a value"). + build(Package). + % error("Single valued variants must have a single value"). +multiple_values_sv_variant(Package, Variant, Value1, Value2) + :- node(Package), + variant(Package, Variant), + variant_single_value(Package, Variant), + build(Package), + variant_value(Package, Variant, Value1), + variant_value(Package, Variant, Value2), + Value1 < Value2. % WLOG +no_variant_value(Package, Variant) + :- node(Package), + variant(Package, Variant), + build(Package), + C = #count{ Value : variant_value(Package, Variant, Value) }, + C < 1. % if a variant is set to anything, it is considered 'set'. variant_set(Package, Variant) :- variant_set(Package, Variant, _). @@ -449,21 +486,23 @@ variant_set(Package, Variant) :- variant_set(Package, Variant, _). % A variant cannot have a value that is not also a possible value % This only applies to packages we need to build -- concrete packages may % have been built w/different variants from older/different package versions. -:- variant_value(Package, Variant, Value), - not variant_possible_value(Package, Variant, Value), - build(Package), - error("Variant set to invalid value"). +invalid_variant_value(Package, Variant, Value) + :- variant_value(Package, Variant, Value), + not variant_possible_value(Package, Variant, Value), + build(Package). + %error("Variant set to invalid value"). % Some multi valued variants accept multiple values from disjoint sets. % Ensure that we respect that constraint and we don't pick values from more % than one set at once -:- variant_value(Package, Variant, Value1), - variant_value(Package, Variant, Value2), - variant_value_from_disjoint_sets(Package, Variant, Value1, Set1), - variant_value_from_disjoint_sets(Package, Variant, Value2, Set2), - Set1 < Set2, - build(Package), - error("Variant values selected from multiple disjoint sets"). +disjoint_variant_values(Package, Variant, Value1, Value2) + :- variant_value(Package, Variant, Value1), + variant_value(Package, Variant, Value2), + variant_value_from_disjoint_sets(Package, Variant, Value1, Set1), + variant_value_from_disjoint_sets(Package, Variant, Value2, Set2), + Set1 < Set2, + build(Package). + %error("Variant values selected from multiple disjoint sets"). % variant_set is an explicitly set variant value. If it's not 'set', % we revert to the default value. If it is set, we force the set value @@ -521,12 +560,11 @@ variant_default_value(Package, Variant, Value) :- variant_default_value_from_cli % Treat 'none' in a special way - it cannot be combined with other % values even if the variant is multi-valued -:- 2 { - variant_value(Package, Variant, Value) : variant_possible_value(Package, Variant, Value) - }, - variant_value(Package, Variant, "none"), - build(Package), - error("Variant value 'none' cannot be combined with any other value"). +variant_none_and_other(Package, Variant, Value) + :- variant_value(Package, Variant, Value), + variant_value(Package, Variant, "none"), + Value != "none", + build(Package). % patches and dev_path are special variants -- they don't have to be % declared in the package, so we just allow them to spring into existence @@ -552,6 +590,9 @@ variant_single_value(Package, "dev_path") #defined variant_default_value_from_packages_yaml/3. #defined variant_default_value_from_package_py/3. #defined variant_value_from_disjoint_sets/4. +#defined invalid_variant_value/3. +#defined multiple_values_sv_variant/4. +#defined no_variant_value/2. %----------------------------------------------------------------------------- % Platform semantics @@ -579,20 +620,26 @@ node_platform_set(Package) :- node_platform_set(Package, _). os(OS) :- os(OS, _). % one os per node -1 { node_os(Package, OS) : os(OS) } 1 :- - node(Package), error("Each node must have exactly one OS"). +{ node_os(Package, OS) : os(OS) } :- node(Package).%, error("Each node must have exactly one OS"). +no_os(Package) :- node(Package), C = #count{ OS : node_os(Package, OS)}, C < 1. +multiple_os(Package, OS1, OS2) + :- node(Package), + node_os(Package, OS1), + node_os(Package, OS2), + OS1 < OS2. % can't have a non-buildable OS on a node we need to build -:- build(Package), node_os(Package, OS), not buildable_os(OS), - error("No available OS can be built for"). +os_not_buildable(Package, OS) :- build(Package), node_os(Package, OS), not buildable_os(OS). +% error("No available OS can be built for"). % can't have dependencies on incompatible OS's -:- depends_on(Package, Dependency), - node_os(Package, PackageOS), - node_os(Dependency, DependencyOS), - not os_compatible(PackageOS, DependencyOS), - build(Package), - error("Dependencies must have compatible OS's with their dependents"). +os_incompatible(Package, Dependency, PackageOS, DependencyOS) + :- depends_on(Package, Dependency), + node_os(Package, PackageOS), + node_os(Dependency, DependencyOS), + not os_compatible(PackageOS, DependencyOS), + build(Package). + %error("Dependencies must have compatible OS's with their dependents"). % give OS choice weights according to os declarations node_os_weight(Package, Weight) @@ -624,14 +671,20 @@ node_os(Package, OS) :- node_os_set(Package, OS), node(Package). %----------------------------------------------------------------------------- % Each node has only one target chosen among the known targets -1 { node_target(Package, Target) : target(Target) } 1 :- node(Package), error("Each node must have exactly one target"). +{ node_target(Package, Target) : target(Target) } :- node(Package).%, error("Each node must have exactly one target"). +no_target(Package) :- node(Package), C = #count{Target : node_target(Package, Target)}, C < 1. +multiple_targets(Package, Target1, Target2) + :- node(Package), + node_target(Package, Target1), + node_target(Package, Target2), + Target1 < Target2. % If a node must satisfy a target constraint, enforce it -:- node_target(Package, Target), - node_target_satisfies(Package, Constraint), - not target_satisfies(Constraint, Target), - error("Node targets must satisfy node target constraints"). - +target_unsatisfiable(Package, Target, Constraint) + :- node_target(Package, Target), + node_target_satisfies(Package, Constraint), + not target_satisfies(Constraint, Target). +% error("Node targets must satisfy node target constraints"). % If a node has a target and the target satisfies a constraint, then the target % associated with the node satisfies the same constraint @@ -639,10 +692,11 @@ node_target_satisfies(Package, Constraint) :- node_target(Package, Target), target_satisfies(Constraint, Target). % If a node has a target, all of its dependencies must be compatible with that target -:- depends_on(Package, Dependency), - node_target(Package, Target), - not node_target_compatible(Dependency, Target), - error("Dependency node targets must be compatible with dependent targets"). +target_incompatible(Package, Dependency) + :- depends_on(Package, Dependency), + node_target(Package, Target), + not node_target_compatible(Dependency, Target). +% error("Dependency node targets must be compatible with dependent targets"). % Intermediate step for performance reasons % When the integrity constraint above was formulated including this logic @@ -683,12 +737,13 @@ target_weight(Target, Package, Weight) :- package_target_weight(Target, Package, Weight). % can't use targets on node if the compiler for the node doesn't support them -:- node_target(Package, Target), - not compiler_supports_target(Compiler, Version, Target), - node_compiler(Package, Compiler), - node_compiler_version(Package, Compiler, Version), - build(Package), - error("No satisfying compiler available is compatible with a satisfying target"). +compiler_target_mismatch(Package, Target, Compiler, Version) + :- node_target(Package, Target), + not compiler_supports_target(Compiler, Version, Target), + node_compiler(Package, Compiler), + node_compiler_version(Package, Compiler, Version), + build(Package). + %error("No satisfying compiler available is compatible with a satisfying target"). % if a target is set explicitly, respect it node_target(Package, Target) @@ -715,8 +770,11 @@ node_target_mismatch(Parent, Dependency) not node_target_match(Parent, Dependency). % disallow reusing concrete specs that don't have a compatible target -:- node(Package), node_target(Package, Target), not target(Target), - error("No satisfying package's target is compatible with this machine"). +invalid_target(Package, Target) + :- node(Package), + node_target(Package, Target), + not target(Target). +% error("No satisfying package's target is compatible with this machine"). #defined node_target_set/2. #defined package_target_weight/3. @@ -728,10 +786,21 @@ compiler(Compiler) :- compiler_version(Compiler, _). % There must be only one compiler set per built node. The compiler % is chosen among available versions. -1 { node_compiler_version(Package, Compiler, Version) : compiler_version(Compiler, Version) } 1 :- +{ node_compiler_version(Package, Compiler, Version) : compiler_version(Compiler, Version) } :- node(Package), - build(Package), - error("Each node must have exactly one compiler"). + build(Package). +% error("Each node must have exactly one compiler"). +no_compiler_version(Package) + :- node(Package), + build(Package), + C = #count{ Version : node_compiler_version(Package, _, Version)}, + C < 1. +multiple_compiler_versions(Package, Compiler1, Version1, Compiler2, Version2) + :- node(Package), + build(Package), + node_compiler_version(Package, Compiler1, Version1), + node_compiler_version(Package, Compiler2, Version2), + (Compiler1, Version1) < (Compiler2, Version2). % Sometimes we just need to know the compiler and not the version node_compiler(Package, Compiler) :- node_compiler_version(Package, Compiler, _). @@ -765,11 +834,13 @@ node_compiler_version(Package, Compiler, Version) :- node_compiler_version_set(P % Cannot select a compiler if it is not supported on the OS % Compilers that are explicitly marked as allowed % are excluded from this check -:- node_compiler_version(Package, Compiler, Version), node_os(Package, OS), - not compiler_supports_os(Compiler, Version, OS), - not allow_compiler(Compiler, Version), - build(Package), - error("No satisfying compiler available is compatible with a satisfying os"). +compiler_os_mismatch(Package, Compiler, Version, OS) + :- node_compiler_version(Package, Compiler, Version), + node_os(Package, OS), + not compiler_supports_os(Compiler, Version, OS), + not allow_compiler(Compiler, Version), + build(Package). +% error("No satisfying compiler available is compatible with a satisfying os"). % If a package and one of its dependencies don't have the % same compiler there's a mismatch. @@ -912,6 +983,124 @@ build_priority(Package, 0) :- node(Package), not optimize_for_reuse(). #defined installed_hash/2. +%----------------------------------------------------------------- +% Optimization to avoid errors +%----------------------------------------------------------------- +% Some errors are handled as rules instead of constraints because +% it allows us to explain why something failed. Here we optimize +% HEAVILY against the facts generated by those rules. +opt_criterion(1000, "errors"). +#minimize{ 0@1000: #true }. +#minimize{ 1000@1000,Msg: conflict_triggered(Msg) }. + +opt_criterion(1001, "errors"). +#minimize{ 0@1001: #true }. +#minimize{ 1000@1001,Package,Variant,Value1,Value2: multiple_values_sv_variant(Package, Variant, Value1, Value2) }. + +opt_criterion(1002, "errors"). +#minimize{ 0@1002: #true }. +#minimize{ 1000@1002,Package,Variant: no_variant_value(Package, Variant) }. + +opt_criterion(1003, "errors"). +#minimize{ 0@1003: #true }. +#minimize{ 1000@1003,Package,Variant,Value: invalid_variant_value(Package, Variant, Value) }. + +opt_criterion(1004, "errors"). +#minimize{ 0@1004: #true }. +#minimize{ 1000@1004,Package : no_version(Package) }. + +opt_criterion(1005, "errors"). +#minimize{ 0@1005: #true }. +#minimize{ 1000@1005,Package,Version1,Version2 : versions_conflict(Package, Version1, Version2) }. + +opt_criterion(1006, "errors"). +#minimize{ 0@1006: #true }. +#minimize{ 1000@1006,Package,Constraint : version_unsatisfiable(Package, Constraint) }. + +opt_criterion(1007, "errors"). +#minimize{ 0@1007: #true }. +#minimize{ 1000@1007,Package : unnecessary(Package) }. + +opt_criterion(1008, "errors"). +#minimize{ 0@1008: #true }. +#minimize{ 1000@1008,Package1,Package2 : cyclic_dependency(Package1, Package2) }. + +opt_criterion(1009, "errors"). +#minimize{ 0@1009: #true }. +#minimize{ 1000@1009,Virtual : no_provider(Virtual) }. + +opt_criterion(1010, "errors"). +#minimize{ 0@1010: #true }. +#minimize{ 1000@1010,Virtual,Package1,Package2 : multiple_providers(Virtual, Package1, Package2) }. + +opt_criterion(1011, "errors"). +#minimize{ 0@1011: #true }. +#minimize{ 1000@1011,Package : invalid_external_spec(Package) }. + +opt_criterion(1012, "errors"). +#minimize{ 0@1012: #true }. +#minimize{ 1000@1012,Package,Variant : inactive_variant_set(Package, Variant) }. + +opt_criterion(1013, "errors"). +#minimize{ 0@1013: #true }. +#minimize{ 1000@1013,Package,Variant,Value1,Value2 : disjoint_variant_values(Package, Variant, Value1, Value2) }. + +opt_criterion(1014, "errors"). +#minimize{ 0@1014: #true }. +#minimize{ 1000@1014,Package,Variant,Value : variant_none_and_other(Package, Variant, Value) }. + +opt_criterion(1015, "errors"). +#minimize{ 0@1015: #true }. +#minimize{ 1000@1015,Package : no_os(Package) }. + +opt_criterion(1016, "errors"). +#minimize{ 0@1016: #true }. +#minimize{ 1000@1016,Package,OS1,OS2 : multiple_os(Package, OS1, OS2) }. + +opt_criterion(1017, "errors"). +#minimize{ 0@1017: #true }. +#minimize{ 1000@1017,Package,OS : os_not_buildable(Package, OS) }. + +opt_criterion(1018, "errors"). +#minimize{ 0@1018: #true }. +#minimize{ 1000@1018,Package,Dependency,POS,DOS : os_incompatible(Package, Dependency, POS, DOS) }. + +opt_criterion(1019, "errors"). +#minimize{ 0@1019: #true }. +#minimize{ 1000@1019,Package : no_target(Package) }. + +opt_criterion(1020, "errors"). +#minimize{ 0@1020: #true }. +#minimize{ 1000@1020,Package,Target1, Target2 : multiple_targets(Package, Target1, Target2) }. + +opt_criterion(1021, "errors"). +#minimize{ 0@1021: #true }. +#minimize{ 1000@1021,Package,Target,Constraint : target_unsatisfiable(Package, Target, Constraint) }. + +opt_criterion(1022, "errors"). +#minimize{ 0@1022: #true }. +#minimize{ 1000@1022,Package,Dependency : target_incompatible(Package, Dependency) }. + +opt_criterion(1023, "errors"). +#minimize{ 0@1023: #true }. +#minimize{ 1000@1023,Package,Target,Compiler,Version : compiler_target_mismatch(Package, Target, Compiler, Version) }. + +opt_criterion(1024, "errors"). +#minimize{ 0@1024: #true }. +#minimize{ 1000@1024,Package,Target : invalid_target(Package, Target) }. + +opt_criterion(1025, "errors"). +#minimize{ 0@1025: #true }. +#minimize{ 1000@1025,Package : no_compiler_version(Package) }. + +opt_criterion(1026, "errors"). +#minimize{ 0@1026: #true }. +#minimize{ 1000@1026,Package,Compiler1,Version1,Compiler2,Version2 : multiple_compiler_versions(Package, Compiler1, Version1, Compiler2, Version2) }. + +opt_criterion(1027, "errors"). +#minimize{ 0@1027: #true }. +#minimize{ 1000@1027,Package,Compiler,Version,OS : compiler_os_mismatch(Package, Compiler, Version, OS) }. + %----------------------------------------------------------------------------- % How to optimize the spec (high to low priority) %----------------------------------------------------------------------------- diff --git a/lib/spack/spack/solver/display.lp b/lib/spack/spack/solver/display.lp index 4d950ea2ced..83127fca764 100644 --- a/lib/spack/spack/solver/display.lp +++ b/lib/spack/spack/solver/display.lp @@ -34,3 +34,35 @@ % deprecated packages #show deprecated/2. + +% error types +#show conflict_triggered/1. +#show no_version/1. +#show versions_conflict/3. +#show no_variant_value/2. +#show multiple_values_sv_variant/4. +#show invalid_variant_value/3. +#show version_unsatisfiable/2. +#show unnecessary/1. +#show cyclic_dependency/2. +#show no_provider/1. +#show multiple_providers/3. +#show invalid_external_spec/1. +#show inactive_variant_set/2. +#show disjoint_variant_values/4. +#show variant_none_and_other/3. +#show no_os/1. +#show multiple_os/3. +#show os_not_buildable/2. +#show os_incompatible/4. +#show no_target/1. +#show multiple_targets/3. +#show target_unsatisfiable/3. +#show target_incompatible/2. +#show compiler_target_mismatch/4. +#show invalid_target/2. +#show no_compiler_version/1. +#show multiple_compiler_versions/5. +#show compiler_os_mismatch/4. + +% debug diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 43941908f98..0aa7722b9d7 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -645,7 +645,7 @@ def test_conflicts_show_cores(self, conflict_spec, monkeypatch): with pytest.raises(spack.error.SpackError) as e: s.concretize() - assert "conflict_trigger(" in e.value.message + assert "conflict" in e.value.message def test_conflict_in_all_directives_true(self): s = Spec('when-directives-true') @@ -862,7 +862,8 @@ def test_conditional_variants(self, spec_str, expected, unexpected): def test_conditional_variants_fail(self, bad_spec): with pytest.raises( (spack.error.UnsatisfiableSpecError, - vt.InvalidVariantForSpecError) + vt.InvalidVariantForSpecError, + spack.error.SpackError) ): _ = Spec('conditional-variant-pkg' + bad_spec).concretized() @@ -1435,7 +1436,7 @@ def test_provider_must_meet_requirements(self): # A package can be a provider of a virtual only if the underlying # requirements are met. s = spack.spec.Spec('unsat-virtual-dependency') - with pytest.raises((RuntimeError, spack.error.UnsatisfiableSpecError)): + with pytest.raises((RuntimeError, spack.error.UnsatisfiableSpecError, spack.error.SpackError)): s.concretize() @pytest.mark.regression('23951') @@ -1539,7 +1540,7 @@ def test_conditional_values_in_variants(self, spec_str): ) s = Spec(spec_str) - with pytest.raises((RuntimeError, spack.error.UnsatisfiableSpecError)): + with pytest.raises((RuntimeError, spack.error.UnsatisfiableSpecError, spack.error.SpackError)): s.concretize() def test_conditional_values_in_conditional_variant(self):