Compare commits
39 Commits
fix-linux-
...
my_branch
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f70d2c7ccb | ||
![]() |
62d18d9af7 | ||
![]() |
8824040eda | ||
![]() |
d744b83584 | ||
![]() |
14601b59ef | ||
![]() |
8d7360106f | ||
![]() |
881f184eab | ||
![]() |
63bec85a24 | ||
![]() |
159975a561 | ||
![]() |
25ba7d071a | ||
![]() |
54b79d5661 | ||
![]() |
235f93c241 | ||
![]() |
a73c5ffb0b | ||
![]() |
ce53ce284b | ||
![]() |
ae68318475 | ||
![]() |
bd63c19b94 | ||
![]() |
3f3bcacd16 | ||
![]() |
d349d4ab0b | ||
![]() |
e61b3c96f6 | ||
![]() |
bcbb0a3b85 | ||
![]() |
3c556ab318 | ||
![]() |
563ae5188e | ||
![]() |
f4b3561f71 | ||
![]() |
0748a1b290 | ||
![]() |
0bfece0c5e | ||
![]() |
6e9b16279a | ||
![]() |
cf71baff30 | ||
![]() |
b9831acb44 | ||
![]() |
5f2edb1860 | ||
![]() |
8b6809cf66 | ||
![]() |
a93a4274aa | ||
![]() |
59bb42ee7f | ||
![]() |
11d382bff1 | ||
![]() |
57889ec446 | ||
![]() |
66bda49c44 | ||
![]() |
94a2ab5359 | ||
![]() |
6d0044f703 | ||
![]() |
3fa447a84a | ||
![]() |
d501ce0c7e |
@@ -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()
|
||||
|
@@ -10,9 +10,9 @@
|
||||
|
||||
import llnl.util.tty as tty
|
||||
|
||||
#: whether we should write stack traces or short error messages
|
||||
#: at what level we should write stack traces or short error messages
|
||||
#: this is module-scoped because it needs to be set very early
|
||||
debug = False
|
||||
debug = 0
|
||||
|
||||
|
||||
class SpackError(Exception):
|
||||
|
@@ -375,13 +375,6 @@ def make_argument_parser(**kwargs):
|
||||
# stat names in groups of 7, for nice wrapping.
|
||||
stat_lines = list(zip(*(iter(stat_names),) * 7))
|
||||
|
||||
# help message for --show-cores
|
||||
show_cores_help = 'provide additional information on concretization failures\n'
|
||||
show_cores_help += 'off (default): show only the violated rule\n'
|
||||
show_cores_help += 'full: show raw unsat cores from clingo\n'
|
||||
show_cores_help += 'minimized: show subset-minimal unsat cores '
|
||||
show_cores_help += '(Warning: this may take hours for some specs)'
|
||||
|
||||
parser.add_argument(
|
||||
'-h', '--help',
|
||||
dest='help', action='store_const', const='short', default=None,
|
||||
@@ -405,9 +398,6 @@ def make_argument_parser(**kwargs):
|
||||
'-d', '--debug', action='count', default=0,
|
||||
help="write out debug messages "
|
||||
"(more d's for more verbosity: -d, -dd, -ddd, etc.)")
|
||||
parser.add_argument(
|
||||
'--show-cores', choices=["off", "full", "minimized"], default="off",
|
||||
help=show_cores_help)
|
||||
parser.add_argument(
|
||||
'--timestamp', action='store_true',
|
||||
help="Add a timestamp to tty output")
|
||||
@@ -490,18 +480,11 @@ def setup_main_options(args):
|
||||
# errors raised by spack.config.
|
||||
|
||||
if args.debug:
|
||||
spack.error.debug = True
|
||||
spack.error.debug = args.debug
|
||||
spack.util.debug.register_interrupt_handler()
|
||||
spack.config.set('config:debug', True, scope='command_line')
|
||||
spack.util.environment.tracing_enabled = True
|
||||
|
||||
if args.show_cores != "off":
|
||||
# minimize_cores defaults to true, turn it off if we're showing full core
|
||||
# but don't want to wait to minimize it.
|
||||
spack.solver.asp.full_cores = True
|
||||
if args.show_cores == 'full':
|
||||
spack.solver.asp.minimize_cores = False
|
||||
|
||||
if args.timestamp:
|
||||
tty.set_timestamp(True)
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
import itertools
|
||||
import os
|
||||
import pprint
|
||||
import re
|
||||
import types
|
||||
import warnings
|
||||
|
||||
@@ -55,14 +56,6 @@
|
||||
parse_files = None
|
||||
|
||||
|
||||
#: whether we should write ASP unsat cores quickly in debug mode when the cores
|
||||
#: may be very large or take the time (sometimes hours) to minimize them
|
||||
minimize_cores = True
|
||||
|
||||
#: whether we should include all facts in the unsat cores or only error messages
|
||||
full_cores = False
|
||||
|
||||
|
||||
# backward compatibility functions for clingo ASTs
|
||||
def ast_getter(*names):
|
||||
def getter(node):
|
||||
@@ -114,7 +107,7 @@ 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 = []
|
||||
|
||||
@@ -141,7 +134,10 @@ def build_criteria_names(costs, tuples):
|
||||
# sort the criteria by priority
|
||||
priorities_names = sorted(priorities_names, reverse=True)
|
||||
|
||||
assert len(priorities_names) == len(costs), "Wrong number of optimization criteria!"
|
||||
# We only have opt-criterion values for non-error types
|
||||
# error type criteria are excluded (they come first)
|
||||
error_criteria = len(costs) - len(priorities_names)
|
||||
costs = costs[error_criteria:]
|
||||
|
||||
# split list into three parts: build criteria, fixed criteria, non-build criteria
|
||||
num_criteria = len(priorities_names)
|
||||
@@ -154,12 +150,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
|
||||
|
||||
@@ -331,9 +327,6 @@ def format_core(self, core):
|
||||
core_symbols = []
|
||||
for atom in core:
|
||||
sym = symbols[atom]
|
||||
if sym.name in ("rule", "error"):
|
||||
# these are special symbols we use to get messages in the core
|
||||
sym = sym.arguments[0].string
|
||||
core_symbols.append(sym)
|
||||
|
||||
return sorted(str(symbol) for symbol in core_symbols)
|
||||
@@ -392,7 +385,7 @@ def raise_if_unsat(self):
|
||||
"""
|
||||
Raise an appropriate error if the result is unsatisfiable.
|
||||
|
||||
The error is a UnsatisfiableSpecError, and includes the minimized cores
|
||||
The error is an InternalConcretizerError, and includes the minimized cores
|
||||
resulting from the solve, formatted to be human readable.
|
||||
"""
|
||||
if self.satisfiable:
|
||||
@@ -402,12 +395,8 @@ def raise_if_unsat(self):
|
||||
if len(constraints) == 1:
|
||||
constraints = constraints[0]
|
||||
|
||||
if minimize_cores:
|
||||
conflicts = self.format_minimal_cores()
|
||||
else:
|
||||
conflicts = self.format_cores()
|
||||
|
||||
raise UnsatisfiableSpecError(constraints, conflicts=conflicts)
|
||||
conflicts = self.format_minimal_cores()
|
||||
raise InternalConcretizerError(constraints, conflicts=conflicts)
|
||||
|
||||
@property
|
||||
def specs(self):
|
||||
@@ -507,13 +496,11 @@ def h2(self, name):
|
||||
def newline(self):
|
||||
self.out.write('\n')
|
||||
|
||||
def fact(self, head, assumption=False):
|
||||
def fact(self, head):
|
||||
"""ASP fact (a rule without a body).
|
||||
|
||||
Arguments:
|
||||
head (AspFunction): ASP function to generate as fact
|
||||
assumption (bool): If True and using cores, use this fact as a
|
||||
choice point in ASP and include it in unsatisfiable cores
|
||||
"""
|
||||
symbol = head.symbol() if hasattr(head, 'symbol') else head
|
||||
|
||||
@@ -521,10 +508,9 @@ def fact(self, head, assumption=False):
|
||||
|
||||
atom = self.backend.add_atom(symbol)
|
||||
|
||||
# with `--show-cores=full or --show-cores=minimized, make all facts
|
||||
# choices/assumptions, otherwise only if assumption=True
|
||||
choice = self.cores and (full_cores or assumption)
|
||||
|
||||
# Only functions relevant for constructing bug reports for bad error messages
|
||||
# are assumptions, and only when using cores.
|
||||
choice = self.cores and symbol.name == 'internal_error'
|
||||
self.backend.add_rule([atom], [], choice=choice)
|
||||
if choice:
|
||||
self.assumptions.append(atom)
|
||||
@@ -582,9 +568,10 @@ def visit(node):
|
||||
for term in node.body:
|
||||
if ast_type(term) == ASTType.Literal:
|
||||
if ast_type(term.atom) == ASTType.SymbolicAtom:
|
||||
if ast_sym(term.atom).name == "error":
|
||||
name = ast_sym(term.atom).name
|
||||
if name == 'internal_error':
|
||||
arg = ast_sym(ast_sym(term.atom).arguments[0])
|
||||
self.fact(fn.error(arg.string), assumption=True)
|
||||
self.fact(AspFunction(name)(arg.string))
|
||||
|
||||
path = os.path.join(parent_dir, 'concretize.lp')
|
||||
parse_files([path], visit)
|
||||
@@ -737,7 +724,7 @@ def spec_versions(self, spec):
|
||||
|
||||
# record all version constraints for later
|
||||
self.version_constraints.add((spec.name, spec.versions))
|
||||
return [fn.version_satisfies(spec.name, spec.versions)]
|
||||
return [fn.node_version_satisfies(spec.name, spec.versions)]
|
||||
|
||||
def target_ranges(self, spec, single_target_fn):
|
||||
target = spec.architecture.target
|
||||
@@ -750,13 +737,24 @@ def target_ranges(self, spec, single_target_fn):
|
||||
return [fn.node_target_satisfies(spec.name, target)]
|
||||
|
||||
def conflict_rules(self, pkg):
|
||||
default_msg = "{0} '{1}' conflicts with '{2}'"
|
||||
no_constraint_msg = "{0} conflicts with '{1}'"
|
||||
for trigger, constraints in pkg.conflicts.items():
|
||||
trigger_id = self.condition(spack.spec.Spec(trigger), name=pkg.name)
|
||||
self.gen.fact(fn.conflict_trigger(trigger_id))
|
||||
trigger_msg = "conflict trigger %s" % str(trigger)
|
||||
trigger_id = self.condition(
|
||||
spack.spec.Spec(trigger), name=pkg.name, msg=trigger_msg)
|
||||
|
||||
for constraint, _ in constraints:
|
||||
constraint_id = self.condition(constraint, name=pkg.name)
|
||||
self.gen.fact(fn.conflict(pkg.name, trigger_id, constraint_id))
|
||||
for constraint, conflict_msg in constraints:
|
||||
if conflict_msg is None:
|
||||
if constraint == spack.spec.Spec():
|
||||
conflict_msg = no_constraint_msg.format(pkg.name, trigger)
|
||||
else:
|
||||
conflict_msg = default_msg.format(pkg.name, trigger, constraint)
|
||||
constraint_msg = "conflict constraint %s" % str(constraint)
|
||||
constraint_id = self.condition(
|
||||
constraint, name=pkg.name, msg=constraint_msg)
|
||||
self.gen.fact(
|
||||
fn.conflict(pkg.name, trigger_id, constraint_id, conflict_msg))
|
||||
self.gen.newline()
|
||||
|
||||
def available_compilers(self):
|
||||
@@ -840,9 +838,18 @@ def pkg_rules(self, pkg, tests):
|
||||
for name, entry in sorted(pkg.variants.items()):
|
||||
variant, when = entry
|
||||
|
||||
for w in when:
|
||||
cond_id = self.condition(w, name=pkg.name)
|
||||
self.gen.fact(fn.variant_condition(cond_id, pkg.name, name))
|
||||
if spack.spec.Spec() in when:
|
||||
# unconditional variant
|
||||
self.gen.fact(fn.variant(pkg.name, name))
|
||||
else:
|
||||
# conditional variant
|
||||
for w in when:
|
||||
msg = "%s has variant %s" % (pkg.name, name)
|
||||
if str(w):
|
||||
msg += " when %s" % w
|
||||
|
||||
cond_id = self.condition(w, name=pkg.name, msg=msg)
|
||||
self.gen.fact(fn.variant_condition(cond_id, pkg.name, name))
|
||||
|
||||
single_value = not variant.multi
|
||||
if single_value:
|
||||
@@ -885,7 +892,9 @@ def pkg_rules(self, pkg, tests):
|
||||
imposed = spack.spec.Spec(value.when)
|
||||
imposed.name = pkg.name
|
||||
self.condition(
|
||||
required_spec=required, imposed_spec=imposed, name=pkg.name
|
||||
required_spec=required, imposed_spec=imposed, name=pkg.name,
|
||||
msg="%s variant %s value %s when %s" % (
|
||||
pkg.name, name, value, when)
|
||||
)
|
||||
|
||||
if variant.sticky:
|
||||
@@ -913,7 +922,7 @@ def pkg_rules(self, pkg, tests):
|
||||
)
|
||||
)
|
||||
|
||||
def condition(self, required_spec, imposed_spec=None, name=None):
|
||||
def condition(self, required_spec, imposed_spec=None, name=None, msg=None):
|
||||
"""Generate facts for a dependency or virtual provider condition.
|
||||
|
||||
Arguments:
|
||||
@@ -922,7 +931,7 @@ def condition(self, required_spec, imposed_spec=None, name=None):
|
||||
are imposed when this condition is triggered
|
||||
name (str or None): name for `required_spec` (required if
|
||||
required_spec is anonymous, ignored if not)
|
||||
|
||||
msg (str or None): description of the condition
|
||||
Returns:
|
||||
int: id of the condition created by this function
|
||||
"""
|
||||
@@ -931,7 +940,7 @@ def condition(self, required_spec, imposed_spec=None, name=None):
|
||||
assert named_cond.name, "must provide name for anonymous condtions!"
|
||||
|
||||
condition_id = next(self._condition_id_counter)
|
||||
self.gen.fact(fn.condition(condition_id))
|
||||
self.gen.fact(fn.condition(condition_id, msg))
|
||||
|
||||
# requirements trigger the condition
|
||||
requirements = self.spec_clauses(
|
||||
@@ -963,7 +972,8 @@ def package_provider_rules(self, pkg):
|
||||
|
||||
for provided, whens in pkg.provided.items():
|
||||
for when in whens:
|
||||
condition_id = self.condition(when, provided, pkg.name)
|
||||
msg = '%s provides %s when %s' % (pkg.name, provided, when)
|
||||
condition_id = self.condition(when, provided, pkg.name, msg)
|
||||
self.gen.fact(fn.provider_condition(
|
||||
condition_id, when.name, provided.name
|
||||
))
|
||||
@@ -987,7 +997,11 @@ def package_dependencies_rules(self, pkg):
|
||||
if not deptypes:
|
||||
continue
|
||||
|
||||
condition_id = self.condition(cond, dep.spec, pkg.name)
|
||||
msg = '%s depends on %s' % (pkg.name, dep.spec.name)
|
||||
if cond != spack.spec.Spec():
|
||||
msg += ' when %s' % cond
|
||||
|
||||
condition_id = self.condition(cond, dep.spec, pkg.name, msg)
|
||||
self.gen.fact(fn.dependency_condition(
|
||||
condition_id, pkg.name, dep.spec.name
|
||||
))
|
||||
@@ -1067,7 +1081,8 @@ def external_packages(self):
|
||||
|
||||
# Declare external conditions with a local index into packages.yaml
|
||||
for local_idx, spec in enumerate(external_specs):
|
||||
condition_id = self.condition(spec)
|
||||
msg = '%s available as external when satisfying %s' % (spec.name, spec)
|
||||
condition_id = self.condition(spec, msg=msg)
|
||||
self.gen.fact(
|
||||
fn.possible_external(condition_id, pkg_name, local_idx)
|
||||
)
|
||||
@@ -1920,6 +1935,17 @@ def node_os(self, pkg, os):
|
||||
def node_target(self, pkg, target):
|
||||
self._arch(pkg).target = target
|
||||
|
||||
def error(self, priority, msg, *args):
|
||||
msg = msg.format(*args)
|
||||
|
||||
# For variant formatting, we sometimes have to construct specs
|
||||
# to format values properly. Find/replace all occurances of
|
||||
# Spec(...) with the string representation of the spec mentioned
|
||||
specs_to_construct = re.findall(r'Spec\(([^)]*)\)', msg)
|
||||
for spec_str in specs_to_construct:
|
||||
msg = msg.replace('Spec(%s)' % spec_str, str(spack.spec.Spec(spec_str)))
|
||||
raise UnsatisfiableSpecError(msg)
|
||||
|
||||
def variant_value(self, pkg, name, value):
|
||||
# FIXME: is there a way not to special case 'dev_path' everywhere?
|
||||
if name == 'dev_path':
|
||||
@@ -2042,15 +2068,27 @@ def deprecated(self, pkg, version):
|
||||
msg = 'using "{0}@{1}" which is a deprecated version'
|
||||
tty.warn(msg.format(pkg, version))
|
||||
|
||||
@staticmethod
|
||||
def sort_fn(function_tuple):
|
||||
name = function_tuple[0]
|
||||
if name == 'error':
|
||||
priority = function_tuple[1][0]
|
||||
return (-4, priority)
|
||||
elif name == 'hash':
|
||||
return (-3, 0)
|
||||
elif name == 'node':
|
||||
return (-2, 0)
|
||||
elif name == 'node_compiler':
|
||||
return (-1, 0)
|
||||
else:
|
||||
return (0, 0)
|
||||
|
||||
def build_specs(self, function_tuples):
|
||||
# Functions don't seem to be in particular order in output. Sort
|
||||
# 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: {
|
||||
"hash": -3,
|
||||
"node": -2,
|
||||
"node_compiler": -1,
|
||||
}.get(f[0], 0))
|
||||
self.function_tuples = function_tuples
|
||||
self.function_tuples.sort(key=self.sort_fn)
|
||||
|
||||
self._specs = {}
|
||||
for name, args in function_tuples:
|
||||
@@ -2058,7 +2096,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))
|
||||
@@ -2068,16 +2105,18 @@ def build_specs(self, function_tuples):
|
||||
assert action and callable(action)
|
||||
|
||||
# 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):
|
||||
continue
|
||||
# solving but don't construct anything. Do not ignore error
|
||||
# predicates on virtual packages.
|
||||
if name != 'error':
|
||||
pkg = args[0]
|
||||
if spack.repo.path.is_virtual(pkg):
|
||||
continue
|
||||
|
||||
# if we've already gotten a concrete spec for this pkg,
|
||||
# do not bother calling actions on it.
|
||||
spec = self._specs.get(pkg)
|
||||
if spec and spec.concrete:
|
||||
continue
|
||||
# if we've already gotten a concrete spec for this pkg,
|
||||
# do not bother calling actions on it.
|
||||
spec = self._specs.get(pkg)
|
||||
if spec and spec.concrete:
|
||||
continue
|
||||
|
||||
action(*args)
|
||||
|
||||
@@ -2201,25 +2240,27 @@ def solve(
|
||||
|
||||
|
||||
class UnsatisfiableSpecError(spack.error.UnsatisfiableSpecError):
|
||||
"""
|
||||
Subclass for new constructor signature for new concretizer
|
||||
"""
|
||||
def __init__(self, msg):
|
||||
super(spack.error.UnsatisfiableSpecError, self).__init__(msg)
|
||||
self.provided = None
|
||||
self.required = None
|
||||
self.constraint_type = None
|
||||
|
||||
|
||||
class InternalConcretizerError(spack.error.UnsatisfiableSpecError):
|
||||
"""
|
||||
Subclass for new constructor signature for new concretizer
|
||||
"""
|
||||
def __init__(self, provided, conflicts):
|
||||
indented = [' %s\n' % conflict for conflict in conflicts]
|
||||
conflict_msg = ''.join(indented)
|
||||
issue = 'conflicts' if full_cores else 'errors'
|
||||
msg = '%s is unsatisfiable, %s are:\n%s' % (provided, issue, conflict_msg)
|
||||
|
||||
newline_indent = '\n '
|
||||
if not full_cores:
|
||||
msg += newline_indent + 'To see full clingo unsat cores, '
|
||||
msg += 're-run with `spack --show-cores=full`'
|
||||
if not minimize_cores or not full_cores:
|
||||
# not solver.minimalize_cores and not solver.full_cores impossible
|
||||
msg += newline_indent + 'For full, subset-minimal unsat cores, '
|
||||
msg += 're-run with `spack --show-cores=minimized'
|
||||
msg += newline_indent
|
||||
msg += 'Warning: This may take (up to) hours for some specs'
|
||||
error_msg = ''.join(indented)
|
||||
msg = 'Spack concretizer internal error. Please submit a bug report'
|
||||
msg += '\n Please include the command, environment if applicable,'
|
||||
msg += '\n and the following error message.'
|
||||
msg = '\n %s is unsatisfiable, errors are:\n%s' % (provided, error_msg)
|
||||
|
||||
super(spack.error.UnsatisfiableSpecError, self).__init__(msg)
|
||||
|
||||
|
@@ -7,22 +7,6 @@
|
||||
% This logic program implements Spack's concretizer
|
||||
%=============================================================================
|
||||
|
||||
%-----------------------------------------------------------------------------
|
||||
% Generic constraints on nodes
|
||||
%-----------------------------------------------------------------------------
|
||||
|
||||
% each node must have a single version
|
||||
:- 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), error("A node must have exactly one platform").
|
||||
:- not 1 { node_os(Package, _) } 1, node(Package).
|
||||
:- not 1 { node_target(Package, _) } 1, node(Package).
|
||||
|
||||
% each node has a single compiler associated with it
|
||||
:- not 1 { node_compiler(Package, _) } 1, node(Package).
|
||||
:- not 1 { node_compiler_version(Package, _, _) } 1, node(Package).
|
||||
|
||||
%-----------------------------------------------------------------------------
|
||||
% Version semantics
|
||||
%-----------------------------------------------------------------------------
|
||||
@@ -35,7 +19,7 @@ version_declared(Package, Version, Weight) :- version_declared(Package, Version,
|
||||
:- version_declared(Package, Version, Weight, Origin1),
|
||||
version_declared(Package, Version, Weight, Origin2),
|
||||
Origin1 < Origin2,
|
||||
error("Internal error: two versions with identical weights").
|
||||
internal_error("Two versions with identical weights").
|
||||
|
||||
% We cannot use a version declared for an installed package if we end up building it
|
||||
:- version_declared(Package, Version, Weight, "installed"),
|
||||
@@ -48,11 +32,27 @@ 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").
|
||||
% We allow clingo to choose any version(s), and infer an error if there
|
||||
% is not precisely one version chosen. Error facts are heavily optimized
|
||||
% against to ensure they cannot be inferred when a non-error solution is
|
||||
% possible
|
||||
{ version(Package, Version) : version_declared(Package, Version) }
|
||||
:- node(Package).
|
||||
error(2, "No version for '{0}' satisfies '@{1}' and '@{2}'", Package, Version1, Version2)
|
||||
:- node(Package),
|
||||
version(Package, Version1),
|
||||
version(Package, Version2),
|
||||
Version1 < Version2. % see[1]
|
||||
|
||||
% A virtual package may have or not a version, but never has more than one
|
||||
:- virtual_node(Package), 2 { version(Package, _) }.
|
||||
error(2, "No versions available for package '{0}'", Package)
|
||||
:- node(Package), not version(Package, _).
|
||||
|
||||
% A virtual package may or may not have a version, but never has more than one
|
||||
error(2, "No version for '{0}' satisfies '@{1}' and '@{2}'", Virtual, Version1, Version2)
|
||||
:- virtual_node(Virtual),
|
||||
version(Virtual, Version1),
|
||||
version(Virtual, Version2),
|
||||
Version1 < Version2. % see[1]
|
||||
|
||||
% If we select a deprecated version, mark the package as deprecated
|
||||
deprecated(Package, Version) :- version(Package, Version), deprecated_version(Package, Version).
|
||||
@@ -61,14 +61,27 @@ possible_version_weight(Package, Weight)
|
||||
:- version(Package, Version),
|
||||
version_declared(Package, Version, Weight).
|
||||
|
||||
1 { version_weight(Package, Weight) : possible_version_weight(Package, Weight) } 1 :- node(Package), error("Internal error: Package version must have a unique weight").
|
||||
version_weight(Package, Weight)
|
||||
:- version(Package, Version),
|
||||
node(Package),
|
||||
Weight = #min{W : version_declared(Package, Version, W)}.
|
||||
|
||||
% version_satisfies implies that exactly one of the satisfying versions
|
||||
% 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_satisfies(Package, Constraint),
|
||||
error("no version satisfies the given constraints").
|
||||
version_satisfies(Package, Constraint)
|
||||
% While this choice rule appears redundant with the initial choice rule for
|
||||
% versions, virtual nodes with version constraints require this rule to be
|
||||
% able to choose versions
|
||||
{ version(Package, Version) : version_satisfies(Package, Constraint, Version) }
|
||||
:- node_version_satisfies(Package, Constraint).
|
||||
|
||||
% More specific error message if the version cannot satisfy some constraint
|
||||
% Otherwise covered by `no_version_error` and `versions_conflict_error`.
|
||||
error(1, "No valid version for '{0}' satisfies '@{1}'", Package, Constraint)
|
||||
:- node_version_satisfies(Package, Constraint),
|
||||
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.
|
||||
@@ -87,7 +100,7 @@ version_satisfies(Package, Constraint)
|
||||
% conditions are specified with `condition_requirement` and hold when
|
||||
% corresponding spec attributes hold.
|
||||
condition_holds(ID) :-
|
||||
condition(ID);
|
||||
condition(ID, _);
|
||||
attr(Name, A1) : condition_requirement(ID, Name, A1);
|
||||
attr(Name, A1, A2) : condition_requirement(ID, Name, A1, A2);
|
||||
attr(Name, A1, A2, A3) : condition_requirement(ID, Name, A1, A2, A3).
|
||||
@@ -106,7 +119,7 @@ attr(Name, A1, A2, A3) :- impose(ID), imposed_constraint(ID, Name, A1, A2, A3).
|
||||
variant_value(Package, Variant, Value),
|
||||
not imposed_constraint(Hash, "variant_value", Package, Variant, Value).
|
||||
|
||||
#defined condition/1.
|
||||
#defined condition/2.
|
||||
#defined condition_requirement/3.
|
||||
#defined condition_requirement/4.
|
||||
#defined condition_requirement/5.
|
||||
@@ -133,9 +146,7 @@ depends_on(Package, Dependency) :- depends_on(Package, Dependency, _).
|
||||
dependency_holds(Package, Dependency, Type) :-
|
||||
dependency_condition(ID, Package, Dependency),
|
||||
dependency_type(ID, Type),
|
||||
condition_holds(ID),
|
||||
build(Package),
|
||||
not external(Package).
|
||||
condition_holds(ID).
|
||||
|
||||
% We cut off dependencies of externals (as we don't really know them).
|
||||
% Don't impose constraints on dependencies that don't exist.
|
||||
@@ -161,17 +172,18 @@ 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").
|
||||
error(1, "'{0}' is not a valid dependency for any package in the DAG", Package)
|
||||
:- node(Package),
|
||||
not needed(Package).
|
||||
|
||||
% 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").
|
||||
|
||||
#defined error/1.
|
||||
error(2, "Cyclic dependency detected between '{0}' and '{1}'\n Consider changing variants to avoid the cycle", A, B)
|
||||
:- path(A, B),
|
||||
path(B, A).
|
||||
|
||||
#defined dependency_type/2.
|
||||
#defined dependency_condition/3.
|
||||
@@ -179,14 +191,13 @@ path(Parent, Descendant) :- path(Parent, A), depends_on(A, Descendant).
|
||||
%-----------------------------------------------------------------------------
|
||||
% Conflicts
|
||||
%-----------------------------------------------------------------------------
|
||||
:- node(Package),
|
||||
conflict(Package, TriggerID, ConstraintID),
|
||||
error(0, 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/3.
|
||||
#defined conflict/4.
|
||||
|
||||
%-----------------------------------------------------------------------------
|
||||
% Virtual dependencies
|
||||
@@ -206,8 +217,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(2, "Cannot find valid provider for virtual {0}", Virtual)
|
||||
:- virtual_node(Virtual),
|
||||
P = #count{ Package : provider(Package, Virtual)},
|
||||
P < 1.
|
||||
error(2, "Spec cannot include multiple providers for virtual '{0}'\n Requested '{1}' and '{2}'", 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).
|
||||
@@ -232,7 +252,7 @@ virtual_condition_holds(Provider, Virtual) :-
|
||||
% A package cannot be the actual provider for a virtual if it does not
|
||||
% fulfill the conditions to provide that virtual
|
||||
:- provider(Package, Virtual), not virtual_condition_holds(Package, Virtual),
|
||||
error("Internal error: virtual when provides not respected").
|
||||
internal_error("Virtual when provides not respected").
|
||||
|
||||
#defined possible_provider/2.
|
||||
|
||||
@@ -245,7 +265,7 @@ virtual_condition_holds(Provider, Virtual) :-
|
||||
% we select the weight, among the possible ones, that minimizes the overall objective function.
|
||||
1 { provider_weight(Dependency, Virtual, Weight, Reason) :
|
||||
possible_provider_weight(Dependency, Virtual, Weight, Reason) } 1
|
||||
:- provider(Dependency, Virtual), error("Internal error: package provider weights must be unique").
|
||||
:- provider(Dependency, Virtual), internal_error("Package provider weights must be unique").
|
||||
|
||||
% Get rid or the reason for enabling the possible weight (useful for debugging)
|
||||
provider_weight(Dependency, Virtual, Weight) :- provider_weight(Dependency, Virtual, Weight, _).
|
||||
@@ -291,7 +311,7 @@ node(Package) :- attr("node", Package).
|
||||
virtual_node(Virtual) :- attr("virtual_node", Virtual).
|
||||
hash(Package, Hash) :- attr("hash", Package, Hash).
|
||||
version(Package, Version) :- attr("version", Package, Version).
|
||||
version_satisfies(Package, Constraint) :- attr("version_satisfies", Package, Constraint).
|
||||
node_version_satisfies(Package, Constraint) :- attr("node_version_satisfies", Package, Constraint).
|
||||
node_platform(Package, Platform) :- attr("node_platform", Package, Platform).
|
||||
node_os(Package, OS) :- attr("node_os", Package, OS).
|
||||
node_target(Package, Target) :- attr("node_target", Package, Target).
|
||||
@@ -310,7 +330,7 @@ attr("node", Package) :- node(Package).
|
||||
attr("virtual_node", Virtual) :- virtual_node(Virtual).
|
||||
attr("hash", Package, Hash) :- hash(Package, Hash).
|
||||
attr("version", Package, Version) :- version(Package, Version).
|
||||
attr("version_satisfies", Package, Constraint) :- version_satisfies(Package, Constraint).
|
||||
attr("node_version_satisfies", Package, Constraint) :- node_version_satisfies(Package, Constraint).
|
||||
attr("node_platform", Package, Platform) :- node_platform(Package, Platform).
|
||||
attr("node_os", Package, OS) :- node_os(Package, OS).
|
||||
attr("node_target", Package, Target) :- node_target(Package, Target).
|
||||
@@ -338,7 +358,7 @@ attr("node_compiler_version_satisfies", Package, Compiler, Version)
|
||||
#defined external_only/1.
|
||||
#defined pkg_provider_preference/4.
|
||||
#defined default_provider_preference/3.
|
||||
#defined version_satisfies/2.
|
||||
#defined node_version_satisfies/2.
|
||||
#defined node_compiler_version_satisfies/3.
|
||||
#defined root/1.
|
||||
|
||||
@@ -347,9 +367,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(2, "Attempted to use external for '{0}' which does not satisfy any configured external spec", Package)
|
||||
:- external(Package),
|
||||
not external_version(Package, _, _).
|
||||
error(2, "Attempted to use external for '{0}' which does not satisfy any configured external spec", Package)
|
||||
:- external(Package),
|
||||
external_version(Package, Version1, Weight1),
|
||||
external_version(Package, Version2, Weight2),
|
||||
(Version1, Weight1) < (Version2, Weight2). % see[1]
|
||||
|
||||
version_weight(Package, Weight) :- external_version(Package, Version, Weight).
|
||||
version(Package, Version) :- external_version(Package, Version, Weight).
|
||||
@@ -369,7 +397,7 @@ external(Package) :- external_spec_selected(Package, _).
|
||||
version_weight(Package, Weight),
|
||||
version_declared(Package, Version, Weight, "external"),
|
||||
not external(Package),
|
||||
error("Internal error: external weight used for internal spec").
|
||||
internal_error("External weight used for internal spec").
|
||||
|
||||
% determine if an external spec has been selected
|
||||
external_spec_selected(Package, LocalIndex) :-
|
||||
@@ -381,8 +409,9 @@ 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").
|
||||
error(2, "Attempted to use external for '{0}' which does not satisfy any configured external spec", Package)
|
||||
:- external(Package),
|
||||
not external_conditions_hold(Package, _).
|
||||
|
||||
#defined possible_external/3.
|
||||
#defined external_spec_index/3.
|
||||
@@ -399,16 +428,16 @@ 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").
|
||||
error(2, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Package, Variant)
|
||||
:- variant_set(Package, Variant),
|
||||
not variant(Package, Variant),
|
||||
build(Package).
|
||||
|
||||
% 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").
|
||||
error(2, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Package, Variant)
|
||||
:- variant_value(Package, Variant, _),
|
||||
not variant(Package, Variant),
|
||||
build(Package).
|
||||
|
||||
% if a variant is sticky and not set its value is the default value
|
||||
variant_value(Package, Variant, Value) :-
|
||||
@@ -418,27 +447,30 @@ variant_value(Package, Variant, Value) :-
|
||||
variant_default_value(Package, Variant, Value),
|
||||
build(Package).
|
||||
|
||||
% one variant value for single-valued variants.
|
||||
1 {
|
||||
% at most one variant value for single-valued variants.
|
||||
{
|
||||
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 for multi-valued variants.
|
||||
1 {
|
||||
variant_value(Package, Variant, Value)
|
||||
: variant_possible_value(Package, Variant, Value)
|
||||
}
|
||||
:- node(Package),
|
||||
variant(Package, Variant),
|
||||
not variant_single_value(Package, Variant),
|
||||
build(Package),
|
||||
error("Internal error: All variants must have a value").
|
||||
build(Package).
|
||||
|
||||
|
||||
error(2, "'{0}' required multiple values for single-valued variant '{1}'\n Requested 'Spec({1}={2})' and 'Spec({1}={3})'", 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. % see[1]
|
||||
error(2, "No valid value for variant '{1}' of package '{0}'", 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, _).
|
||||
@@ -446,21 +478,21 @@ 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").
|
||||
error(1, "'Spec({1}={2})' is not a valid value for '{0}' variant '{1}'", Package, Variant, Value)
|
||||
:- variant_value(Package, Variant, Value),
|
||||
not variant_possible_value(Package, Variant, Value),
|
||||
build(Package).
|
||||
|
||||
% 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").
|
||||
error(2, "{0} variant '{1}' cannot have values '{2}' and '{3}' as they come from disjoing value sets", 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, % see[1]
|
||||
build(Package).
|
||||
|
||||
% 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
|
||||
@@ -518,12 +550,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").
|
||||
error(2, "{0} variant '{1}' cannot have values '{2}' and 'none'", 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
|
||||
@@ -567,6 +598,18 @@ node_platform(Package, Platform)
|
||||
% platform is set if set to anything
|
||||
node_platform_set(Package) :- node_platform_set(Package, _).
|
||||
|
||||
% each node must have a single platform
|
||||
error(2, "No valid platform found for {0}", Package)
|
||||
:- node(Package),
|
||||
C = #count{ Platform : node_platform(Package, Platform)},
|
||||
C < 1.
|
||||
|
||||
error(2, "Cannot concretize {0} with multiple platforms\n Requested 'platform={1}' and 'platform={2}'", Package, Platform1, Platform2)
|
||||
:- node(Package),
|
||||
node_platform(Package, Platform1),
|
||||
node_platform(Package, Platform2),
|
||||
Platform1 < Platform2. % see[1]
|
||||
|
||||
#defined node_platform_set/2. % avoid warnings
|
||||
|
||||
%-----------------------------------------------------------------------------
|
||||
@@ -576,20 +619,32 @@ 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(2, "Cannot find valid operating system for '{0}'", Package)
|
||||
:- node(Package),
|
||||
C = #count{ OS : node_os(Package, OS)},
|
||||
C < 1.
|
||||
|
||||
error(2, "Cannot concretize {0} with multiple operating systems\n Requested 'os={1}' and 'os={2}'", Package, OS1, OS2)
|
||||
:- node(Package),
|
||||
node_os(Package, OS1),
|
||||
node_os(Package, OS2),
|
||||
OS1 < OS2. %see [1]
|
||||
|
||||
% 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").
|
||||
error(2, "Cannot concretize '{0} os={1}'. Operating system '{1}' is not buildable", Package, OS)
|
||||
:- build(Package),
|
||||
node_os(Package, OS),
|
||||
not buildable_os(OS).
|
||||
|
||||
% 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").
|
||||
error(2, "{0} and dependency {1} have incompatible operating systems 'os={2}' and 'os={3}'", Package, Dependency, PackageOS, DependencyOS)
|
||||
:- depends_on(Package, Dependency),
|
||||
node_os(Package, PackageOS),
|
||||
node_os(Dependency, DependencyOS),
|
||||
not os_compatible(PackageOS, DependencyOS),
|
||||
build(Package).
|
||||
|
||||
% give OS choice weights according to os declarations
|
||||
node_os_weight(Package, Weight)
|
||||
@@ -621,14 +676,24 @@ 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(2, "Cannot find valid target for '{0}'", Package)
|
||||
:- node(Package),
|
||||
C = #count{Target : node_target(Package, Target)},
|
||||
C < 1.
|
||||
|
||||
error(2, "Cannot concretize '{0}' with multiple targets\n Requested 'target={1}' and 'target={2}'", Package, Target1, Target2)
|
||||
:- node(Package),
|
||||
node_target(Package, Target1),
|
||||
node_target(Package, Target2),
|
||||
Target1 < Target2. % see[1]
|
||||
|
||||
% 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").
|
||||
|
||||
error(1, "'{0} target={1}' cannot satisfy constraint 'target={2}'", Package, Target, Constraint)
|
||||
:- node_target(Package, Target),
|
||||
node_target_satisfies(Package, Constraint),
|
||||
not target_satisfies(Constraint, Target).
|
||||
|
||||
% If a node has a target and the target satisfies a constraint, then the target
|
||||
% associated with the node satisfies the same constraint
|
||||
@@ -636,10 +701,10 @@ 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").
|
||||
error(2, "Cannot find compatible targets for {0} and {1}", Package, Dependency)
|
||||
:- depends_on(Package, Dependency),
|
||||
node_target(Package, Target),
|
||||
not node_target_compatible(Dependency, Target).
|
||||
|
||||
% Intermediate step for performance reasons
|
||||
% When the integrity constraint above was formulated including this logic
|
||||
@@ -680,12 +745,12 @@ 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").
|
||||
error(2, "{0} compiler '{2}@{3}' incompatible with 'target={1}'", 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).
|
||||
|
||||
% if a target is set explicitly, respect it
|
||||
node_target(Package, Target)
|
||||
@@ -712,8 +777,10 @@ 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").
|
||||
error(2, "'{0} target={1}' is not compatible with this machine", Package, Target)
|
||||
:- node(Package),
|
||||
node_target(Package, Target),
|
||||
not target(Target).
|
||||
|
||||
#defined node_target_set/2.
|
||||
#defined package_target_weight/3.
|
||||
@@ -725,10 +792,19 @@ 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(2, "No valid compiler version found for '{0}'", Package)
|
||||
:- node(Package),
|
||||
C = #count{ Version : node_compiler_version(Package, _, Version)},
|
||||
C < 1.
|
||||
error(2, "'{0}' compiler constraints '%{1}@{2}' and '%{3}@{4}' are incompatible", Package, Compiler1, Version1, Compiler2, Version2)
|
||||
:- node(Package),
|
||||
node_compiler_version(Package, Compiler1, Version1),
|
||||
node_compiler_version(Package, Compiler2, Version2),
|
||||
(Compiler1, Version1) < (Compiler2, Version2). % see[1]
|
||||
|
||||
% Sometimes we just need to know the compiler and not the version
|
||||
node_compiler(Package, Compiler) :- node_compiler_version(Package, Compiler, _).
|
||||
@@ -737,14 +813,22 @@ node_compiler(Package, Compiler) :- node_compiler_version(Package, Compiler, _).
|
||||
:- node_compiler(Package, Compiler1),
|
||||
node_compiler_version(Package, Compiler2, _),
|
||||
Compiler1 != Compiler2,
|
||||
error("Internal error: mismatch between selected compiler and compiler version").
|
||||
internal_error("Mismatch between selected compiler and compiler version").
|
||||
|
||||
% If the compiler of a node cannot be satisfied, raise
|
||||
error(1, "No valid compiler for {0} satisfies '%{1}'", Package, Compiler)
|
||||
:- node(Package),
|
||||
node_compiler_version_satisfies(Package, Compiler, ":"),
|
||||
C = #count{ Version : node_compiler_version(Package, Compiler, Version), compiler_version_satisfies(Compiler, ":", Version) },
|
||||
C < 1.
|
||||
|
||||
% If the compiler of a node must satisfy a constraint, then its version
|
||||
% must be chosen among the ones that satisfy said constraint
|
||||
1 { node_compiler_version(Package, Compiler, Version)
|
||||
: compiler_version_satisfies(Compiler, Constraint, Version) } 1 :-
|
||||
node_compiler_version_satisfies(Package, Compiler, Constraint),
|
||||
error("Internal error: node compiler version mismatch").
|
||||
error(2, "No valid version for '{0}' compiler '{1}' satisfies '@{2}'", Package, Compiler, Constraint)
|
||||
:- node(Package),
|
||||
node_compiler_version_satisfies(Package, Compiler, Constraint),
|
||||
C = #count{ Version : node_compiler_version(Package, Compiler, Version), compiler_version_satisfies(Compiler, Constraint, Version) },
|
||||
C < 1.
|
||||
|
||||
% If the node is associated with a compiler and the compiler satisfy a constraint, then
|
||||
% the compiler associated with the node satisfy the same constraint
|
||||
@@ -762,11 +846,12 @@ 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").
|
||||
error(2, "{0} compiler '%{1}@{2}' incompatible with 'os={3}'", 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).
|
||||
|
||||
% If a package and one of its dependencies don't have the
|
||||
% same compiler there's a mismatch.
|
||||
@@ -859,7 +944,7 @@ no_flags(Package, FlagType)
|
||||
%-----------------------------------------------------------------------------
|
||||
% the solver is free to choose at most one installed hash for each package
|
||||
{ hash(Package, Hash) : installed_hash(Package, Hash) } 1
|
||||
:- node(Package), error("Internal error: package must resolve to at most one hash").
|
||||
:- node(Package), internal_error("Package must resolve to at most one hash").
|
||||
|
||||
% you can't choose an installed hash for a dev spec
|
||||
:- hash(Package, Hash), variant_value(Package, "dev_path", _).
|
||||
@@ -909,6 +994,23 @@ 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.
|
||||
#minimize{ 0@1000: #true}.
|
||||
#minimize{ 0@1001: #true}.
|
||||
#minimize{ 0@1002: #true}.
|
||||
|
||||
#minimize{ 1000@1000+Priority,Msg: error(Priority, Msg) }.
|
||||
#minimize{ 1000@1000+Priority,Msg,Arg1: error(Priority, Msg, Arg1) }.
|
||||
#minimize{ 1000@1000+Priority,Msg,Arg1,Arg2: error(Priority, Msg, Arg1, Arg2) }.
|
||||
#minimize{ 1000@1000+Priority,Msg,Arg1,Arg2,Arg3: error(Priority, Msg, Arg1, Arg2, Arg3) }.
|
||||
#minimize{ 1000@1000+Priority,Msg,Arg1,Arg2,Arg3,Arg4: error(Priority, Msg, Arg1, Arg2, Arg3, Arg4) }.
|
||||
#minimize{ 1000@1000+Priority,Msg,Arg1,Arg2,Arg3,Arg4,Arg5: error(Priority, Msg, Arg1, Arg2, Arg3, Arg4, Arg5) }.
|
||||
|
||||
%-----------------------------------------------------------------------------
|
||||
% How to optimize the spec (high to low priority)
|
||||
%-----------------------------------------------------------------------------
|
||||
@@ -1088,3 +1190,11 @@ opt_criterion(1, "non-preferred targets").
|
||||
#heuristic variant_value(Package, Variant, Value) : variant_default_value(Package, Variant, Value), node(Package). [10, true]
|
||||
#heuristic provider(Package, Virtual) : possible_provider_weight(Package, Virtual, 0, _), virtual_node(Virtual). [10, true]
|
||||
#heuristic node(Package) : possible_provider_weight(Package, Virtual, 0, _), virtual_node(Virtual). [10, true]
|
||||
|
||||
%-----------
|
||||
% Notes
|
||||
%-----------
|
||||
|
||||
% [1] Clingo ensures a total ordering among all atoms. We rely on that total ordering
|
||||
% to reduce symmetry in the solution by checking `<` instead of `!=` in symmetric
|
||||
% cases. These choices are made without loss of generality.
|
||||
|
@@ -34,3 +34,13 @@
|
||||
|
||||
% deprecated packages
|
||||
#show deprecated/2.
|
||||
|
||||
% error types
|
||||
#show error/2.
|
||||
#show error/3.
|
||||
#show error/4.
|
||||
#show error/5.
|
||||
#show error/6.
|
||||
#show error/7.
|
||||
|
||||
% debug
|
||||
|
@@ -532,7 +532,7 @@ def test_cdash_report_concretization_error(tmpdir, mock_fetch, install_mockery,
|
||||
# new or the old concretizer
|
||||
expected_messages = (
|
||||
'Conflicts in concretized spec',
|
||||
'A conflict was triggered',
|
||||
'conflicts with',
|
||||
)
|
||||
assert any(x in content for x in expected_messages)
|
||||
|
||||
|
@@ -638,14 +638,11 @@ def test_conflicts_show_cores(self, conflict_spec, monkeypatch):
|
||||
if spack.config.get('config:concretizer') == 'original':
|
||||
pytest.skip('Testing debug statements specific to new concretizer')
|
||||
|
||||
monkeypatch.setattr(spack.solver.asp, 'full_cores', True)
|
||||
monkeypatch.setattr(spack.solver.asp, 'minimize_cores', False)
|
||||
|
||||
s = Spec(conflict_spec)
|
||||
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')
|
||||
|
@@ -335,7 +335,7 @@ _spacktivate() {
|
||||
_spack() {
|
||||
if $list_options
|
||||
then
|
||||
SPACK_COMPREPLY="-h --help -H --all-help --color -c --config -C --config-scope -d --debug --show-cores --timestamp --pdb -e --env -D --env-dir -E --no-env --use-env-repo -k --insecure -l --enable-locks -L --disable-locks -m --mock -b --bootstrap -p --profile --sorted-profile --lines -v --verbose --stacktrace -V --version --print-shell-vars"
|
||||
SPACK_COMPREPLY="-h --help -H --all-help --color -c --config -C --config-scope -d --debug --timestamp --pdb -e --env -D --env-dir -E --no-env --use-env-repo -k --insecure -l --enable-locks -L --disable-locks -m --mock -b --bootstrap -p --profile --sorted-profile --lines -v --verbose --stacktrace -V --version --print-shell-vars"
|
||||
else
|
||||
SPACK_COMPREPLY="activate add analyze arch audit blame bootstrap build-env buildcache cd checksum ci clean clone commands compiler compilers concretize config containerize create deactivate debug dependencies dependents deprecate dev-build develop diff docs edit env extensions external fetch find gc gpg graph help info install license list load location log-parse maintainers make-installer mark mirror module monitor patch pkg providers pydoc python reindex remove rm repo resource restage solve spec stage style tags test test-env tutorial undevelop uninstall unit-test unload url verify versions view"
|
||||
fi
|
||||
|
Reference in New Issue
Block a user