summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMassimiliano Culpo <massimiliano.culpo@gmail.com>2023-06-16 15:25:30 +0200
committerTodd Gamblin <tgamblin@llnl.gov>2023-08-15 15:54:37 -0700
commit27ab53b68a3a8e067ccec9b1e93f1939cdde1e3b (patch)
tree750ed6eb442af0baa1d98a9550027ea2df7f927d /lib
parent907a80ca71b4690867a95392e3a314be8cdbca1c (diff)
downloadspack-27ab53b68a3a8e067ccec9b1e93f1939cdde1e3b.tar.gz
spack-27ab53b68a3a8e067ccec9b1e93f1939cdde1e3b.tar.bz2
spack-27ab53b68a3a8e067ccec9b1e93f1939cdde1e3b.tar.xz
spack-27ab53b68a3a8e067ccec9b1e93f1939cdde1e3b.zip
Rework the encoding to introduce node(ID, Package) nested facts
So far the encoding has a single ID per package, i.e. all the facts will be node(0, Package). This will prepare the stage for extending this logic and having multiple nodes from the same package in a DAG.
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/solver/asp.py152
-rw-r--r--lib/spack/spack/solver/concretize.lp1018
-rw-r--r--lib/spack/spack/spec.py8
3 files changed, 635 insertions, 543 deletions
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
index fe01be1bcf..91c0b1680a 100644
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -515,15 +515,17 @@ class Result:
best = min(self.answers)
opt, _, answer = best
for input_spec in self.abstract_specs:
- key = input_spec.name
+ node = SpecBuilder.root_node(pkg=input_spec.name)
if input_spec.virtual:
- providers = [spec.name for spec in answer.values() if spec.package.provides(key)]
- key = providers[0]
- candidate = answer.get(key)
+ providers = [
+ spec.name for spec in answer.values() if spec.package.provides(input_spec.name)
+ ]
+ node = SpecBuilder.root_node(pkg=providers[0])
+ candidate = answer.get(node)
if candidate and candidate.satisfies(input_spec):
- self._concrete_specs.append(answer[key])
- self._concrete_specs_by_input[input_spec] = answer[key]
+ self._concrete_specs.append(answer[node])
+ self._concrete_specs_by_input[input_spec] = answer[node]
else:
self._unsolved_specs.append(input_spec)
@@ -2426,6 +2428,18 @@ class SpecBuilder:
)
)
+ node_regex = re.compile(r"node\(\d,\"(.*)\"\)")
+
+ @staticmethod
+ def root_node(*, pkg: str) -> str:
+ """Given a package name, returns the string representation of the root node in
+ the ASP encoding.
+
+ Args:
+ pkg: name of a package
+ """
+ return f'node(0,"{pkg}")'
+
def __init__(self, specs, hash_lookup=None):
self._specs = {}
self._result = None
@@ -2438,100 +2452,121 @@ class SpecBuilder:
# from this dictionary during reconstruction
self._hash_lookup = hash_lookup or {}
- def hash(self, pkg, h):
- if pkg not in self._specs:
- self._specs[pkg] = self._hash_lookup[h]
- self._hash_specs.append(pkg)
+ @staticmethod
+ def extract_pkg(node: str) -> str:
+ """Extracts the package name from a node fact, and returns it.
+
+ Args:
+ node: node from which the package name is to be extracted
+ """
+ m = SpecBuilder.node_regex.match(node)
+ if m is None:
+ raise spack.error.SpackError(f"cannot extract package information from '{node}'")
+
+ return m.group(1)
+
+ def hash(self, node, h):
+ if node not in self._specs:
+ self._specs[node] = self._hash_lookup[h]
+ self._hash_specs.append(node)
- def node(self, pkg):
- if pkg not in self._specs:
- self._specs[pkg] = spack.spec.Spec(pkg)
+ def node(self, node):
+ pkg = self.extract_pkg(node)
+ if node not in self._specs:
+ self._specs[node] = spack.spec.Spec(pkg)
- def _arch(self, pkg):
- arch = self._specs[pkg].architecture
+ def _arch(self, node):
+ arch = self._specs[node].architecture
if not arch:
arch = spack.spec.ArchSpec()
- self._specs[pkg].architecture = arch
+ self._specs[node].architecture = arch
return arch
- def node_platform(self, pkg, platform):
- self._arch(pkg).platform = platform
+ def node_platform(self, node, platform):
+ self._arch(node).platform = platform
- def node_os(self, pkg, os):
- self._arch(pkg).os = os
+ def node_os(self, node, os):
+ self._arch(node).os = os
- def node_target(self, pkg, target):
- self._arch(pkg).target = target
+ def node_target(self, node, target):
+ self._arch(node).target = target
- def variant_value(self, pkg, name, value):
+ def variant_value(self, node, name, value):
# FIXME: is there a way not to special case 'dev_path' everywhere?
if name == "dev_path":
- self._specs[pkg].variants.setdefault(
+ self._specs[node].variants.setdefault(
name, spack.variant.SingleValuedVariant(name, value)
)
return
if name == "patches":
- self._specs[pkg].variants.setdefault(
+ self._specs[node].variants.setdefault(
name, spack.variant.MultiValuedVariant(name, value)
)
return
- self._specs[pkg].update_variant_validate(name, value)
+ self._specs[node].update_variant_validate(name, value)
- def version(self, pkg, version):
- self._specs[pkg].versions = vn.VersionList([vn.Version(version)])
+ def version(self, node, version):
+ self._specs[node].versions = vn.VersionList([vn.Version(version)])
- def node_compiler_version(self, pkg, compiler, version):
- self._specs[pkg].compiler = spack.spec.CompilerSpec(compiler)
- self._specs[pkg].compiler.versions = vn.VersionList([vn.Version(version)])
+ def node_compiler_version(self, node, compiler, version):
+ self._specs[node].compiler = spack.spec.CompilerSpec(compiler)
+ self._specs[node].compiler.versions = vn.VersionList([vn.Version(version)])
- def node_flag_compiler_default(self, pkg):
- self._flag_compiler_defaults.add(pkg)
+ def node_flag_compiler_default(self, node):
+ self._flag_compiler_defaults.add(node)
- def node_flag(self, pkg, flag_type, flag):
- self._specs[pkg].compiler_flags.add_flag(flag_type, flag, False)
+ def node_flag(self, node, flag_type, flag):
+ self._specs[node].compiler_flags.add_flag(flag_type, flag, False)
- def node_flag_source(self, pkg, flag_type, source):
- self._flag_sources[(pkg, flag_type)].add(source)
+ def node_flag_source(self, node, flag_type, source):
+ self._flag_sources[(node, flag_type)].add(source)
- def no_flags(self, pkg, flag_type):
- self._specs[pkg].compiler_flags[flag_type] = []
+ def no_flags(self, node, flag_type):
+ self._specs[node].compiler_flags[flag_type] = []
- def external_spec_selected(self, pkg, idx):
+ def external_spec_selected(self, node, idx):
"""This means that the external spec and index idx
has been selected for this package.
"""
+
packages_yaml = spack.config.get("packages")
packages_yaml = _normalize_packages_yaml(packages_yaml)
+ pkg = self.extract_pkg(node)
spec_info = packages_yaml[pkg]["externals"][int(idx)]
- self._specs[pkg].external_path = spec_info.get("prefix", None)
- self._specs[pkg].external_modules = spack.spec.Spec._format_module_list(
+ self._specs[node].external_path = spec_info.get("prefix", None)
+ self._specs[node].external_modules = spack.spec.Spec._format_module_list(
spec_info.get("modules", None)
)
- self._specs[pkg].extra_attributes = spec_info.get("extra_attributes", {})
+ self._specs[node].extra_attributes = spec_info.get("extra_attributes", {})
# If this is an extension, update the dependencies to include the extendee
- package = self._specs[pkg].package_class(self._specs[pkg])
+ package = self._specs[node].package_class(self._specs[node])
extendee_spec = package.extendee_spec
+
if extendee_spec:
- package.update_external_dependencies(self._specs.get(extendee_spec.name, None))
+ extendee_node = SpecBuilder.root_node(pkg=extendee_spec.name)
+ package.update_external_dependencies(self._specs.get(extendee_node, None))
- def depends_on(self, pkg, dep, type):
- dependencies = self._specs[pkg].edges_to_dependencies(name=dep)
+ def depends_on(self, parent_node, dependency_node, type):
+ dependencies = self._specs[parent_node].edges_to_dependencies(name=dependency_node)
# TODO: assertion to be removed when cross-compilation is handled correctly
msg = "Current solver does not handle multiple dependency edges of the same name"
assert len(dependencies) < 2, msg
if not dependencies:
- self._specs[pkg].add_dependency_edge(self._specs[dep], deptypes=(type,), virtuals=())
+ self._specs[parent_node].add_dependency_edge(
+ self._specs[dependency_node], deptypes=(type,), virtuals=()
+ )
else:
# TODO: This assumes that each solve unifies dependencies
dependencies[0].update_deptypes(deptypes=(type,))
- def virtual_on_edge(self, pkg, provider, virtual):
- dependencies = self._specs[pkg].edges_to_dependencies(name=provider)
+ def virtual_on_edge(self, parent_node, provider_node, virtual):
+ provider = self.extract_pkg(provider_node)
+ dependencies = self._specs[parent_node].edges_to_dependencies(name=provider)
assert len(dependencies) == 1
dependencies[0].update_virtuals((virtual,))
@@ -2562,17 +2597,22 @@ class SpecBuilder:
# order is determined by the DAG. A spec's flags come after any of its ancestors
# on the compile line
- source_key = (spec.name, flag_type)
+ node = SpecBuilder.root_node(pkg=spec.name)
+ source_key = (node, flag_type)
if source_key in self._flag_sources:
- order = [s.name for s in spec.traverse(order="post", direction="parents")]
+ order = [
+ SpecBuilder.root_node(pkg=s.name)
+ for s in spec.traverse(order="post", direction="parents")
+ ]
sorted_sources = sorted(
self._flag_sources[source_key], key=lambda s: order.index(s)
)
# add flags from each source, lowest to highest precedence
- for name in sorted_sources:
+ for node in sorted_sources:
all_src_flags = list()
- per_pkg_sources = [self._specs[name]]
+ per_pkg_sources = [self._specs[node]]
+ name = self.extract_pkg(node)
if name in cmd_specs:
per_pkg_sources.append(cmd_specs[name])
for source in per_pkg_sources:
@@ -2645,14 +2685,14 @@ class SpecBuilder:
# solving but don't construct anything. Do not ignore error
# predicates on virtual packages.
if name != "error":
- pkg = args[0]
+ pkg = self.extract_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 except for node_flag_source,
# since node_flag_source is tracking information not in the spec itself
- spec = self._specs.get(pkg)
+ spec = self._specs.get(args[0])
if spec and spec.concrete:
if name != "node_flag_source":
continue
diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp
index fee377f209..aa2dbecdb4 100644
--- a/lib/spack/spack/solver/concretize.lp
+++ b/lib/spack/spack/solver/concretize.lp
@@ -11,6 +11,8 @@
% Map literal input specs to facts that drive the solve
%-----------------------------------------------------------------------------
+#const root_node_id = 0.
+
% Give clingo the choice to solve an input spec or not
{ literal_solved(ID) } :- literal(ID).
literal_not_solved(ID) :- not literal_solved(ID), literal(ID).
@@ -27,11 +29,17 @@ opt_criterion(300, "number of input specs not concretized").
#minimize{ 0@300: #true }.
#minimize { 1@300,ID : literal_not_solved(ID) }.
-% Map constraint on the literal ID to the correct PSID
-attr(Name, A1) :- literal(LiteralID, Name, A1), literal_solved(LiteralID).
-attr(Name, A1, A2) :- literal(LiteralID, Name, A1, A2), literal_solved(LiteralID).
-attr(Name, A1, A2, A3) :- literal(LiteralID, Name, A1, A2, A3), literal_solved(LiteralID).
-attr(Name, A1, A2, A3, A4) :- literal(LiteralID, Name, A1, A2, A3, A4), literal_solved(LiteralID).
+% Map constraint on the literal ID to facts on the node
+attr(Name, node(root_node_id, A1)) :- literal(LiteralID, Name, A1), literal_solved(LiteralID).
+attr(Name, node(root_node_id, A1), A2) :- literal(LiteralID, Name, A1, A2), literal_solved(LiteralID).
+attr(Name, node(root_node_id, A1), A2, A3) :- literal(LiteralID, Name, A1, A2, A3), literal_solved(LiteralID), not literal_special_case(LiteralID, Name, A1, A2, A3).
+attr(Name, node(root_node_id, A1), A2, A3, A4) :- literal(LiteralID, Name, A1, A2, A3, A4), literal_solved(LiteralID).
+
+% Special cases where nodes occur in arguments other than A1
+literal_special_case(LiteralID, Name, A1, A2, A3) :- literal(LiteralID, Name, A1, A2, A3), Name == "node_flag_source".
+literal_special_case(LiteralID, Name, A1, A2, A3) :- literal(LiteralID, Name, A1, A2, A3), Name == "depends_on".
+attr("node_flag_source", node(root_node_id, A1), A2, node(root_node_id, A3)) :- literal(LiteralID, "node_flag_source", A1, A2, A3), literal_solved(LiteralID).
+attr("depends_on", node(root_node_id, A1), node(root_node_id, A2), A3) :- literal(LiteralID, "depends_on", A1, A2, A3), literal_solved(LiteralID).
#defined concretize_everything/0.
#defined literal/1.
@@ -48,15 +56,15 @@ attr_single_value("node_target").
% Error when no attribute is selected
error(100, no_value_error, Attribute, Package)
- :- attr("node", Package),
+ :- attr("node", node(ID, Package)),
attr_single_value(Attribute),
- not attr(Attribute, Package, _).
+ not attr(Attribute, node(ID, Package), _).
% Error when multiple attr need to be selected
error(100, multiple_values_error, Attribute, Package)
- :- attr("node", Package),
+ :- attr("node", node(ID, Package)),
attr_single_value(Attribute),
- 2 { attr(Attribute, Package, Version) }.
+ 2 { attr(Attribute, node(ID, Package), Value) }.
%-----------------------------------------------------------------------------
% Version semantics
@@ -74,9 +82,9 @@ facts(Package, version_declared(Version, Weight)) :- facts(Package, version_decl
% We cannot use a version declared for an installed package if we end up building it
:- facts(Package, version_declared(Version, Weight, "installed")),
- attr("version", Package, Version),
- version_weight(Package, Weight),
- not attr("hash", Package, _),
+ attr("version", node(ID, Package), Version),
+ version_weight(node(ID, Package), Weight),
+ not attr("hash", node(ID, Package), _),
internal_error("Reuse version weight used for built package").
% versions are declared w/priority -- declared with priority implies declared
@@ -88,72 +96,73 @@ facts(Package, version_declared(Version)) :- facts(Package, version_declared(Ver
% 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
-{ attr("version", Package, Version) : facts(Package, version_declared(Version)) }
- :- attr("node", Package).
+{ attr("version", node(ID, Package), Version) : facts(Package, version_declared(Version)) }
+ :- attr("node", node(ID, Package)).
% A virtual package may or may not have a version, but never has more than one
error(100, "Cannot select a single version for virtual '{0}'", Virtual)
- :- attr("virtual_node", Virtual),
- 2 { attr("version", Virtual, Version) }.
+ :- attr("virtual_node", node(ID, Virtual)),
+ 2 { attr("version", node(ID, Virtual), Version) }.
% If we select a deprecated version, mark the package as deprecated
-attr("deprecated", Package, Version) :-
- attr("version", Package, Version),
+attr("deprecated", node(ID, Package), Version) :-
+ attr("version", node(ID, Package), Version),
facts(Package, deprecated_version(Version)).
-possible_version_weight(Package, Weight)
- :- attr("version", Package, Version),
+possible_version_weight(node(ID, Package), Weight)
+ :- attr("version", node(ID, Package), Version),
facts(Package, version_declared(Version, Weight)).
% we can't use the weight for an external version if we don't use the
% corresponding external spec.
-:- attr("version", Package, Version),
- version_weight(Package, Weight),
+:- attr("version", node(ID, Package), Version),
+ version_weight(node(ID, Package), Weight),
facts(Package, version_declared(Version, Weight, "external")),
- not external(Package),
+ not external(node(ID, Package)),
internal_error("External weight used for built package").
% we can't use a weight from an installed spec if we are building it
% and vice-versa
-:- attr("version", Package, Version),
- version_weight(Package, Weight),
+:- attr("version", node(ID, Package), Version),
+ version_weight(node(ID, Package), Weight),
facts(Package, version_declared(Version, Weight, "installed")),
- build(Package),
+ build(node(ID, Package)),
internal_error("Reuse version weight used for build package").
-:- attr("version", Package, Version),
- version_weight(Package, Weight),
+:- attr("version", node(ID, Package), Version),
+ version_weight(node(ID, Package), Weight),
not facts(Package, version_declared(Version, Weight, "installed")),
- not build(Package),
+ not build(node(ID, Package)),
internal_error("Build version weight used for reused package").
-1 { version_weight(Package, Weight) : facts(Package, version_declared(Version, Weight)) } 1
- :- attr("version", Package, Version),
- attr("node", Package).
+1 { version_weight(node(ID, Package), Weight) : facts(Package, version_declared(Version, Weight)) } 1
+ :- attr("version", node(ID, Package), Version),
+ attr("node", node(ID, Package)).
% node_version_satisfies implies that exactly one of the satisfying versions
% is the package's version, and vice versa.
% 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
-{ attr("version", Package, Version) : facts(Package, version_satisfies(Constraint, Version)) }
- :- attr("node_version_satisfies", Package, Constraint).
+{ attr("version", node(ID, Package), Version) : facts(Package, version_satisfies(Constraint, Version)) }
+ :- attr("node_version_satisfies", node(ID, Package), Constraint).
% If there is at least a version that satisfy the constraint, impose a lower
% bound on the choice rule to avoid false positives with the error below
-1 { attr("version", Package, Version) : facts(Package, version_satisfies(Constraint, Version)) }
- :- attr("node_version_satisfies", Package, Constraint),
+1 { attr("version", node(ID, Package), Version) : facts(Package, version_satisfies(Constraint, Version)) }
+ :- attr("node_version_satisfies", node(ID, Package), Constraint),
facts(Package, version_satisfies(Constraint, _)).
% More specific error message if the version cannot satisfy some constraint
% Otherwise covered by `no_version_error` and `versions_conflict_error`.
error(10, "Cannot satisfy '{0}@{1}'", Package, Constraint)
- :- attr("node_version_satisfies", Package, Constraint),
- attr("version", Package, Version),
+ :- attr("node_version_satisfies", node(ID, Package), Constraint),
+ attr("version", node(ID, Package), Version),
not facts(Package, version_satisfies(Constraint, Version)).
-attr("node_version_satisfies", Package, Constraint)
- :- attr("version", Package, Version), facts(Package, version_satisfies(Constraint, Version)).
+attr("node_version_satisfies", node(ID, Package), Constraint)
+ :- attr("version", node(ID, Package), Version),
+ facts(Package, version_satisfies(Constraint, Version)).
#defined version_satisfies/3.
#defined deprecated_version/2.
@@ -170,32 +179,46 @@ attr("node_version_satisfies", Package, Constraint)
%-----------------------------------------------------------------------------
% conditions are specified with `condition_requirement` and hold when
% corresponding spec attributes hold.
-condition_holds(ID, Package) :-
+condition_holds(ID, node(root_node_id, Package)) :-
facts(Package, 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);
- attr(Name, A1, A2, A3, A4) : condition_requirement(ID, Name, A1, A2, A3, A4).
-
-% condition_holds(ID, Package) implies all imposed_constraints, unless do_not_impose(ID, Package)
+ attr(Name, node(root_node_id, A1)) : condition_requirement(ID, Name, A1);
+ attr(Name, node(root_node_id, A1), A2) : condition_requirement(ID, Name, A1, A2);
+ attr(Name, node(root_node_id, A1), A2, A3) : condition_requirement(ID, Name, A1, A2, A3), Name != "node_flag_source";
+ attr(Name, node(root_node_id, A1), A2, A3, A4) : condition_requirement(ID, Name, A1, A2, A3, A4);
+ % Special cases
+ % FIXME (node transformation): is node_flag_source needed?
+ attr("node_flag_source", node(NodeID, Package), A2, node(ID, A3)) : condition_requirement(ID, "node_flag_source", Package, A2, A3).
+
+% condition_holds(ID, node(ID, Package)) implies all imposed_constraints, unless do_not_impose(ID, node(ID, Package))
% is derived. This allows imposed constraints to be canceled in special cases.
-impose(ID, Package) :- condition_holds(ID, Package), not do_not_impose(ID, Package).
+impose(ID, PackageNode) :- condition_holds(ID, PackageNode), not do_not_impose(ID, PackageNode).
+
+% Conditions that hold impose may impose constraints on other specs
+attr(Name, node(root_node_id, A1)) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, Name, A1).
+attr(Name, node(root_node_id, A1), A2) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, Name, A1, A2).
+attr(Name, node(root_node_id, A1), A2, A3) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, Name, A1, A2, A3), not special_case(ID, Name, A1, A2, A3).
+attr(Name, node(root_node_id, A1), A2, A3, A4) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, Name, A1, A2, A3, A4).
+
-% conditions that hold impose constraints on other specs
-attr(Name, A1) :- impose(ID, Package), imposed_constraint(ID, Name, A1).
-attr(Name, A1, A2) :- impose(ID, Package), imposed_constraint(ID, Name, A1, A2).
-attr(Name, A1, A2, A3) :- impose(ID, Package), imposed_constraint(ID, Name, A1, A2, A3).
-attr(Name, A1, A2, A3, A4) :- impose(ID, Package), imposed_constraint(ID, Name, A1, A2, A3, A4).
+% Special cases
+special_case(ID, Name, A1, A2, A3) :- imposed_constraint(ID, Name, A1, A2, A3), Name == "node_flag_source".
+special_case(ID, Name, A1, A2, A3) :- imposed_constraint(ID, Name, A1, A2, A3), Name == "depends_on".
+
+% FIXME (node transformation): is node_flag_source needed?
+attr("node_flag_source", node(0, Package), A2, node(0, A3)) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, "node_flag_source", Package, A2, A3).
+attr("depends_on", node(0, Package), node(0, A2), A3) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, "depends_on", Package, A2, A3).
% we cannot have additional variant values when we are working with concrete specs
-:- attr("node", Package), attr("hash", Package, Hash),
- attr("variant_value", Package, Variant, Value),
+:- attr("node", node(ID, Package)),
+ attr("hash", node(ID, Package), Hash),
+ attr("variant_value", node(ID, Package), Variant, Value),
not imposed_constraint(Hash, "variant_value", Package, Variant, Value),
internal_error("imposed hash without imposing all variant values").
% we cannot have additional flag values when we are working with concrete specs
-:- attr("node", Package), attr("hash", Package, Hash),
- attr("node_flag", Package, FlagType, Flag),
+:- attr("node", node(ID, Package)),
+ attr("hash", node(ID, Package), Hash),
+ attr("node_flag", node(ID, Package), FlagType, Flag),
not imposed_constraint(Hash, "node_flag", Package, FlagType, Flag),
internal_error("imposed hash without imposing all flag values").
@@ -213,52 +236,55 @@ attr(Name, A1, A2, A3, A4) :- impose(ID, Package), imposed_constraint(ID, Name,
% Concrete specs
%-----------------------------------------------------------------------------
% if a package is assigned a hash, it's concrete.
-concrete(Package) :- attr("hash", Package, _), attr("node", Package).
+concrete(PackageNode) :- attr("hash", PackageNode, _), attr("node", PackageNode).
%-----------------------------------------------------------------------------
% Dependency semantics
%-----------------------------------------------------------------------------
% Dependencies of any type imply that one package "depends on" another
-depends_on(Package, Dependency) :- attr("depends_on", Package, Dependency, _).
+depends_on(PackageNode, DependencyNode) :- attr("depends_on", PackageNode, DependencyNode, _).
% a dependency holds if its condition holds and if it is not external or
% concrete. We chop off dependencies for externals, and dependencies of
% concrete specs don't need to be resolved -- they arise from the concrete
% specs themselves.
-dependency_holds(Package, Dependency, Type) :-
+dependency_holds(node(NodeID, Package), Dependency, Type) :-
facts(Package, dependency_condition(ID, Dependency)),
dependency_type(ID, Type),
- build(Package),
- not external(Package),
- condition_holds(ID, Package).
+ build(node(NodeID, Package)),
+ not external(node(NodeID, Package)),
+ condition_holds(ID, node(NodeID, Package)).
% We cut off dependencies of externals (as we don't really know them).
% Don't impose constraints on dependencies that don't exist.
-do_not_impose(ID, Package) :-
- not dependency_holds(Package, Dependency, _),
+do_not_impose(ID, node(NodeID, Package)) :-
+ not dependency_holds(node(NodeID, Package), Dependency, _),
+ attr("node", node(NodeID, Package)),
facts(Package, dependency_condition(ID, Dependency)).
% declared dependencies are real if they're not virtual AND
% the package is not an external.
% They're only triggered if the associated dependnecy condition holds.
-attr("depends_on", Package, Dependency, Type)
- :- dependency_holds(Package, Dependency, Type),
+
+% TODO (node transformation): here we infer node id 0
+attr("depends_on", node(NodeID, Package), node(0, Dependency), Type)
+ :- dependency_holds(node(NodeID, Package), Dependency, Type),
not virtual(Dependency).
% every root must be a node
-attr("node", Package) :- attr("root", Package).
+attr("node", PackageNode) :- attr("root", PackageNode).
% dependencies imply new nodes
-attr("node", Dependency) :- attr("node", Package), depends_on(Package, Dependency).
+attr("node", DependencyNode) :- attr("node", PackageNode), depends_on(PackageNode, DependencyNode).
% all nodes in the graph must be reachable from some root
% this ensures a user can't say `zlib ^libiconv` (neither of which have any
% dependencies) and get a two-node unconnected graph
-needed(Package) :- attr("root", Package).
-needed(Dependency) :- needed(Package), depends_on(Package, Dependency).
+needed(PackageNode) :- attr("root", PackageNode).
+needed(DependencyNode) :- needed(PackageNode), depends_on(PackageNode, DependencyNode).
error(10, "'{0}' is not a valid dependency for any package in the DAG", Package)
- :- attr("node", Package),
- not needed(Package).
+ :- attr("node", node(ID, Package)),
+ not needed(node(ID, Package)).
% Avoid cycles in the DAG
% some combinations of conditional dependencies can result in cycles;
@@ -274,12 +300,13 @@ error(100, "Cyclic dependency detected between '{0}' and '{1}' (consider changin
%-----------------------------------------------------------------------------
% Conflicts
%-----------------------------------------------------------------------------
-error(1, Msg) :- attr("node", Package),
- facts(Package, conflict(TriggerID, ConstraintID, Msg)),
- condition_holds(TriggerID, Package),
- condition_holds(ConstraintID, Package),
- not external(Package), % ignore conflicts for externals
- not attr("hash", Package, _). % ignore conflicts for installed packages
+error(1, Msg)
+ :- attr("node", node(ID, Package)),
+ facts(Package, conflict(TriggerID, ConstraintID, Msg)),
+ condition_holds(TriggerID, node(ID, Package)),
+ condition_holds(ConstraintID, node(ID, Package)),
+ not external(node(ID, Package)), % ignore conflicts for externals
+ not attr("hash", node(ID, Package), _). % ignore conflicts for installed packages
%-----------------------------------------------------------------------------
% Virtual dependencies
@@ -287,57 +314,62 @@ error(1, Msg) :- attr("node", Package),
% if a package depends on a virtual, it's not external and we have a
% provider for that virtual then it depends on the provider
-attr("depends_on", Package, Provider, Type)
- :- dependency_holds(Package, Virtual, Type),
- provider(Provider, Virtual),
- not external(Package).
+attr("depends_on", PackageNode, ProviderNode, Type)
+ :- dependency_holds(PackageNode, Virtual, Type),
+ provider(ProviderNode, node(0, Virtual)),
+ not external(PackageNode).
-attr("virtual_on_edge", Package, Provider, Virtual)
- :- dependency_holds(Package, Virtual, Type),
- provider(Provider, Virtual),
- not external(Package).
+attr("virtual_on_edge", PackageNode, ProviderNode, Virtual)
+ :- dependency_holds(PackageNode, Virtual, Type),
+ provider(ProviderNode, node(0, Virtual)),
+ not external(PackageNode).
% dependencies on virtuals also imply that the virtual is a virtual node
-attr("virtual_node", Virtual)
- :- dependency_holds(Package, Virtual, Type),
- virtual(Virtual), not external(Package).
+attr("virtual_node", node(0, Virtual))
+ :- dependency_holds(PackageNode, Virtual, Type),
+ virtual(Virtual), not external(PackageNode).
% If there's a virtual node, we must select one and only one provider.
% The provider must be selected among the possible providers.
-{ provider(Package, Virtual) : facts(Package, possible_provider(Virtual)) }
- :- attr("virtual_node", Virtual).
-error(100, "Cannot find valid provider for virtual {0}", Virtual)
- :- attr("virtual_node", Virtual),
- not provider(_, Virtual).
+% FIXME (node transformation): here we use root_node_id
+{ provider(node(0, Package), node(VirtualID, Virtual))
+ : facts(Package, possible_provider(Virtual)) }
+ :- attr("virtual_node", node(VirtualID, Virtual)).
+
+error(100, "Cannot find valid provider for virtual {0}", VirtualNode)
+ :- attr("virtual_node", VirtualNode),
+ not provider(_, VirtualNode).
-error(100, "Cannot select a single provider for virtual '{0}'", Virtual)
- :- attr("virtual_node", Virtual),
- 2 { provider(P, Virtual) }.
+error(100, "Cannot select a single provider for virtual '{0}'", VirtualNode)
+ :- attr("virtual_node", VirtualNode),
+ 2 { provider(P, VirtualNode) }.
% virtual roots imply virtual nodes, and that one provider is a root
-attr("virtual_node", Virtual) :- attr("virtual_root", Virtual).
+attr("virtual_node", VirtualNode) :- attr("virtual_root", VirtualNode).
% If we asked for a virtual root and we have a provider for that,
% then the provider is the root package.
-attr("root", Package) :- attr("virtual_root", Virtual), provider(Package, Virtual).
+attr("root", PackageNode) :- attr("virtual_root", VirtualNode), provider(PackageNode, VirtualNode).
% If we asked for a root package and that root provides a virtual,
% the root is a provider for that virtual. This rule is mostly relevant
% for environments that are concretized together (e.g. where we
% asks to install "mpich" and "hdf5+mpi" and we want "mpich" to
% be the mpi provider)
-provider(Package, Virtual) :- attr("node", Package), virtual_condition_holds(Package, Virtual).
+provider(PackageNode, node(0, Virtual)) :- attr("node", PackageNode), virtual_condition_holds(PackageNode, Virtual).
% The provider provides the virtual if some provider condition holds.
-virtual_condition_holds(Provider, Virtual) :-
+virtual_condition_holds(node(ProviderID, Provider), Virtual) :-virtual_condition_holds(ID, node(ProviderID, Provider), Virtual).
+virtual_condition_holds(ID, node(ProviderID, Provider), Virtual) :-
facts(Provider, provider_condition(ID, Virtual)),
- condition_holds(ID, Provider),
+ condition_holds(ID, node(ProviderID, Provider)),
virtual(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),
+:- provider(PackageNode, node(0, Virtual)),
+ not virtual_condition_holds(PackageNode, Virtual),
internal_error("Virtual when provides not respected").
%-----------------------------------------------------------------------------
@@ -347,33 +379,33 @@ virtual_condition_holds(Provider, Virtual) :-
% A provider may have different possible weights depending on whether it's an external
% or not, or on preferences expressed in packages.yaml etc. This rule ensures that
% 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), internal_error("Package provider weights must be unique").
+1 { provider_weight(DependencyNode, VirtualNode, Weight, Reason) :
+ possible_provider_weight(DependencyNode, VirtualNode, Weight, Reason) } 1
+ :- provider(DependencyNode, VirtualNode), 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, _).
+provider_weight(DependencyNode, VirtualNode, Weight) :- provider_weight(DependencyNode, VirtualNode, Weight, _).
% A provider that is an external can use a weight of 0
-possible_provider_weight(Dependency, Virtual, 0, "external")
- :- provider(Dependency, Virtual),
- external(Dependency).
+possible_provider_weight(DependencyNode, VirtualNode, 0, "external")
+ :- provider(DependencyNode, VirtualNode),
+ external(DependencyNode).
% A provider mentioned in packages.yaml can use a weight
% according to its priority in the list of providers
-possible_provider_weight(Dependency, Virtual, Weight, "packages_yaml")
- :- provider(Dependency, Virtual),
- depends_on(Package, Dependency),
+possible_provider_weight(node(DependencyID, Dependency), node(0, Virtual), Weight, "packages_yaml")
+ :- provider(node(DependencyID, Dependency), node(0, Virtual)),
+ depends_on(node(ID, Package), node(DependencyID, Dependency)),
facts(Package, provider_preference(Virtual, Dependency, Weight)).
% A provider mentioned in the default configuration can use a weight
% according to its priority in the list of providers
-possible_provider_weight(Dependency, Virtual, Weight, "default")
- :- provider(Dependency, Virtual),
+possible_provider_weight(node(DependencyID, Dependency), node(0, Virtual), Weight, "default")
+ :- provider(node(DependencyID, Dependency), node(0, Virtual)),
default_provider_preference(Virtual, Dependency, Weight).
% Any provider can use 100 as a weight, which is very high and discourage its use
-possible_provider_weight(Dependency, Virtual, 100, "fallback") :- provider(Dependency, Virtual).
+possible_provider_weight(node(DependencyID, Dependency), VirtualNode, 100, "fallback") :- provider(node(DependencyID, Dependency), VirtualNode).
% do not warn if generated program contains none of these.
#defined virtual/1.
@@ -387,127 +419,131 @@ possible_provider_weight(Dependency, Virtual, 100, "fallback") :- provider(Depen
%-----------------------------------------------------------------------------
% if a package is external its version must be one of the external versions
-{ external_version(Package, Version, Weight):
+{ external_version(node(ID, Package), Version, Weight):
facts(Package, version_declared(Version, Weight, "external")) }
- :- external(Package).
+ :- external(node(ID, Package)).
+
error(100, "Attempted to use external for '{0}' which does not satisfy any configured external spec", Package)
- :- external(Package),
- not external_version(Package, _, _).
+ :- external(node(ID, Package)),
+ not external_version(node(ID, Package), _, _).
error(100, "Attempted to use external for '{0}' which does not satisfy any configured external spec", Package)
- :- external(Package),
- 2 { external_version(Package, Version, Weight) }.
+ :- external(node(ID, Package)),
+ 2 { external_version(node(ID, Package), Version, Weight) }.
-version_weight(Package, Weight) :- external_version(Package, Version, Weight).
-attr("version", Package, Version) :- external_version(Package, Version, Weight).
+version_weight(PackageNode, Weight) :- external_version(PackageNode, Version, Weight).
+attr("version", PackageNode, Version) :- external_version(PackageNode, Version, Weight).
% if a package is not buildable, only externals or hashed specs are allowed
-external(Package) :- buildable_false(Package),
- attr("node", Package),
- not attr("hash", Package, _).
+external(node(ID, Package))
+ :- buildable_false(Package),
+ attr("node", node(ID, Package)),
+ not attr("hash", node(ID, Package), _).
% a package is a real_node if it is not external
-real_node(Package) :- attr("node", Package), not external(Package).
+real_node(PackageNode) :- attr("node", PackageNode), not external(PackageNode).
% a package is external if we are using an external spec for it
-external(Package) :- attr("external_spec_selected", Package, _).
+external(PackageNode) :- attr("external_spec_selected", PackageNode, _).
% we can't use the weight for an external version if we don't use the
% corresponding external spec.
-:- attr("version", Package, Version),
- version_weight(Package, Weight),
+:- attr("version", node(ID, Package), Version),
+ version_weight(node(ID, Package), Weight),
facts(Package, version_declared(Version, Weight, "external")),
- not external(Package),
+ not external(node(ID, Package)),
internal_error("External weight used for internal spec").
% determine if an external spec has been selected
-attr("external_spec_selected", Package, LocalIndex) :-
- external_conditions_hold(Package, LocalIndex),
- attr("node", Package),
- not attr("hash", Package, _).
+attr("external_spec_selected", node(ID, Package), LocalIndex) :-
+ external_conditions_hold(node(ID, Package), LocalIndex),
+ attr("node", node(ID, Package)),
+ not attr("hash", node(ID, Package), _).
-external_conditions_hold(Package, LocalIndex) :-
- facts(Package, possible_external(ID, LocalIndex)), condition_holds(ID, Package).
+external_conditions_hold(node(PackageID, Package), LocalIndex) :-
+ facts(Package, possible_external(ID, LocalIndex)), condition_holds(ID, node(PackageID, Package)).
% it cannot happen that a spec is external, but none of the external specs
% conditions hold.
error(100, "Attempted to use external for '{0}' which does not satisfy any configured external spec", Package)
- :- external(Package),
- not external_conditions_hold(Package, _).
+ :- external(node(ID, Package)),
+ not external_conditions_hold(node(ID, Package), _).
%-----------------------------------------------------------------------------
% Config required semantics
%-----------------------------------------------------------------------------
-package_in_dag(Package) :- attr("node", Package).
-package_in_dag(Package) :- attr("virtual_node", Package).
+package_in_dag(Node) :- attr("node", Node).
+package_in_dag(Node) :- attr("virtual_node", Node).
-activate_requirement(Package, X) :-
- package_in_dag(Package),
+activate_requirement(node(ID, Package), X) :-
+ package_in_dag(node(ID, Package)),
requirement_group(Package, X),
not requirement_conditional(Package, X, _).
-activate_requirement(Package, X) :-
- package_in_dag(Package),
+activate_requirement(node(ID, Package), X) :-
+ package_in_dag(node(ID, Package)),
requirement_group(Package, X),
- condition_holds(Y, Package),
+ condition_holds(Y, node(ID, Package)),
requirement_conditional(Package, X, Y).
-requirement_group_satisfied(Package, X) :-
- 1 { condition_holds(Y, Package) : requirement_group_member(Y, Package, X) } 1,
+requirement_group_satisfied(node(ID, Package), X) :-
+ 1 { condition_holds(Y, node(ID, Package)) : requirement_group_member(Y, Package, X) } 1,
requirement_policy(Package, X, "one_of"),
- activate_requirement(Package, X),
+ activate_requirement(node(ID, Package), X),
requirement_group(Package, X).
-requirement_weight(Package, Group, W) :-
- condition_holds(Y, Package),
+requirement_weight(node(ID, Package), Group, W) :-
+ condition_holds(Y, node(ID, Package)),
requirement_has_weight(Y, W),
requirement_group_member(Y, Package, Group),
requirement_policy(Package, Group, "one_of"),
- requirement_group_satisfied(Package, Group).
+ requirement_group_satisfied(node(ID, Package), Group).
-requirement_group_satisfied(Package, X) :-
- 1 { condition_holds(Y, Package) : requirement_group_member(Y, Package, X) } ,
+requirement_group_satisfied(node(ID, Package), X) :-
+ 1 { condition_holds(Y, node(ID, Package)) : requirement_group_member(Y, Package, X) } ,
requirement_policy(Package, X, "any_of"),
- activate_requirement(Package, X),
+ activate_requirement(node(ID, Package), X),
requirement_group(Package, X).
% TODO: the following two choice rules allow the solver to add compiler
% flags if their only source is from a requirement. This is overly-specific
% and should use a more-generic approach like in https://github.com/spack/spack/pull/37180
-{ attr("node_flag", A1, A2, A3) } :-
+
+% FIXME (node transformation): check the following two rules for node(ID, A1)
+{ attr("node_flag", node(ID, A1), A2, A3) } :-
requirement_group_member(Y, Package, X),
- activate_requirement(Package, X),
+ activate_requirement(node(ID, Package), X),
imposed_constraint(Y,"node_flag_set", A1, A2, A3).
-{ attr("node_flag_source", A1, A2, A3) } :-
+{ attr("node_flag_source", node(ID, A1), A2, node(ID, A3)) } :-
requirement_group_member(Y, Package, X),
- activate_requirement(Package, X),
+ activate_requirement(node(ID, Package), X),
imposed_constraint(Y,"node_flag_source", A1, A2, A3).
-requirement_weight(Package, Group, W) :-
+requirement_weight(node(ID, Package), Group, W) :-
W = #min {
- Z : requirement_has_weight(Y, Z), condition_holds(Y, Package), requirement_group_member(Y, Package, Group);
+ Z : requirement_has_weight(Y, Z), condition_holds(Y, node(ID, Package)), requirement_group_member(Y, Package, Group);
% We need this to avoid an annoying warning during the solve
% concretize.lp:1151:5-11: info: tuple ignored:
% #sup@73
10000
},
requirement_policy(Package, Group, "any_of"),
- requirement_group_satisfied(Package, Group).
+ requirement_group_satisfied(node(ID, Package), Group).
error(100, "cannot satisfy a requirement for package '{0}'.", Package) :-
- activate_requirement(Package, X),
+ activate_requirement(node(ID, Package), X),
requirement_group(Package, X),
not requirement_message(Package, X, _),
- not requirement_group_satisfied(Package, X).
+ not requirement_group_satisfied(node(ID, Package), X).
error(10, Message) :-
- activate_requirement(Package, X),
+ activate_requirement(node(ID, Package), X),
requirement_group(Package, X),
requirement_message(Package, X, Message),
- not requirement_group_satisfied(Package, X).
+ not requirement_group_satisfied(node(ID, Package), X).
#defined requirement_group/2.
@@ -522,135 +558,137 @@ error(10, Message) :-
%-----------------------------------------------------------------------------
% a variant is a variant of a package if it is a variant under some condition
% and that condition holds
-node_has_variant(Package, variant(Variant)) :-
+node_has_variant(node(NodeID, Package), variant(Variant)) :-
facts(Package, conditional_variant(ID, Variant)),
- condition_holds(ID, Package).
-
-node_has_variant(Package, variant(Variant)) :- facts(Package, variant(Variant)).
-
-
-attr("variant_propagate", Package, Variant, Value, Source) :-
- attr("node", Package),
- depends_on(Parent, Package),
- attr("variant_propagate", Parent, Variant, Value, Source),
- not attr("variant_set", Package, Variant).
-
-attr("variant_value", Package, Variant, Value) :-
- attr("node", Package),
- node_has_variant(Package, variant(Variant)),
- attr("variant_propagate", Package, Variant, Value, _),
+ condition_holds(ID, node(NodeID, Package)).
+
+node_has_variant(node(ID, Package), variant(Variant)) :-
+ facts(Package, variant(Variant)),
+ attr("node", node(ID, Package)).
+
+attr("variant_propagate", PackageNode, Variant, Value, Source) :-
+ attr("node", PackageNode),
+ depends_on(ParentNode, PackageNode),
+ attr("variant_propagate", ParentNode, Variant, Value, Source),
+ not attr("variant_set", PackageNode, Variant).
+
+attr("variant_value", node(ID, Package), Variant, Value) :-
+ attr("node", node(ID, Package)),
+ node_has_variant(node(ID, Package), variant(Variant)),
+ attr("variant_propagate", node(ID, Package), Variant, Value, _),
facts(Package, variant_possible_value(Variant, Value)).
-error(100, "{0} and {1} cannot both propagate variant '{2}' to package {3} with values '{4}' and '{5}'", Source1, Source2, Variant, Package, Value1, Value2) :-
- attr("variant_propagate", Package, Variant, Value1, Source1),
- attr("variant_propagate", Package, Variant, Value2, Source2),
- node_has_variant(Package, variant(Variant)),
+error(100, "{0} and {1} cannot both propagate variant '{2}' to package {3} with values '{4}' and '{5}'", Source1, Source2, Variant, PackageNode, Value1, Value2) :-
+ attr("variant_propagate", PackageNode, Variant, Value1, Source1),
+ attr("variant_propagate", PackageNode, Variant, Value2, Source2),
+ node_has_variant(PackageNode, variant(Variant)),
Value1 < Value2.
% a variant cannot be set if it is not a variant on the package
-error(100, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Variant, Package)
- :- attr("variant_set", Package, Variant),
- not node_has_variant(Package, variant(Variant)),
- build(Package).
+error(100, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Variant, PackageNode)
+ :- attr("variant_set", PackageNode, Variant),
+ not node_has_variant(PackageNode, variant(Variant)),
+ build(PackageNode).
% a variant cannot take on a value if it is not a variant of the package
-error(100, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Variant, Package)
- :- attr("variant_value", Package, Variant, _),
- not node_has_variant(Package, variant(Variant)),
- build(Package).
+error(100, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Variant, PackageNode)
+ :- attr("variant_value", PackageNode, Variant, _),
+ not node_has_variant(PackageNode, variant(Variant)),
+ build(PackageNode).
% if a variant is sticky and not set its value is the default value
-attr("variant_value", Package, Variant, Value) :-
- node_has_variant(Package, variant(Variant)),
- not attr("variant_set", Package, Variant),
+attr("variant_value", node(ID, Package), Variant, Value) :-
+ node_has_variant(node(ID, Package), variant(Variant)),
+ not attr("variant_set", node(ID, Package), Variant),
facts(Package, variant_sticky(Variant)),
variant_default_value(Package, Variant, Value),
- build(Package).
+ build(node(ID, Package)).
% at most one variant value for single-valued variants.
{
- attr("variant_value", Package, Variant, Value)
+ attr("variant_value", node(ID, Package), Variant, Value)
: facts(Package, variant_possible_value(Variant, Value))
}
- :- attr("node", Package),
- node_has_variant(Package, variant(Variant)),
- build(Package).
+ :- attr("node", node(ID, Package)),
+ node_has_variant(node(ID, Package), variant(Variant)),
+ build(node(ID, Package)).
error(100, "'{0}' required multiple values for single-valued variant '{1}'", Package, Variant)
- :- attr("node", Package),
- node_has_variant(Package, variant(Variant)),
+ :- attr("node", node(ID, Package)),
+ node_has_variant(node(ID, Package), variant(Variant)),
facts(Package, variant_single_value(Variant)),
- build(Package),
- 2 { attr("variant_value", Package, Variant, Value) }.
+ build(node(ID, Package)),
+ 2 { attr("variant_value", node(ID, Package), Variant, Value) }.
-error(100, "No valid value for variant '{1}' of package '{0}'", Package, Variant)
- :- attr("node", Package),
- node_has_variant(Package, variant(Variant)),
- build(Package),
- not attr("variant_value", Package, Variant, _).
+error(100, "No valid value for variant '{1}' of package '{0}'", PackageNode, Variant)
+ :- attr("node", PackageNode),
+ node_has_variant(PackageNode, variant(Variant)),
+ build(PackageNode),
+ not attr("variant_value", PackageNode, Variant, _).
% if a variant is set to anything, it is considered 'set'.
-attr("variant_set", Package, Variant) :- attr("variant_set", Package, Variant, _).
+attr("variant_set", PackageNode, Variant) :- attr("variant_set", PackageNode, 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.
error(10, "'Spec({1}={2})' is not a valid value for '{0}' variant '{1}'", Package, Variant, Value)
- :- attr("variant_value", Package, Variant, Value),
+ :- attr("variant_value", node(ID, Package), Variant, Value),
not facts(Package, variant_possible_value(Variant, Value)),
- build(Package).
+ build(node(ID, 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
error(100, "{0} variant '{1}' cannot have values '{2}' and '{3}' as they come from disjoint value sets", Package, Variant, Value1, Value2)
- :- attr("variant_value", Package, Variant, Value1),
- attr("variant_value", Package, Variant, Value2),
+ :- attr("variant_value", node(ID, Package), Variant, Value1),
+ attr("variant_value", node(ID, Package), Variant, Value2),
facts(Package, variant_value_from_disjoint_sets(Variant, Value1, Set1)),
facts(Package, variant_value_from_disjoint_sets(Variant, Value2, Set2)),
Set1 < Set2, % see[1]
- build(Package).
+ build(node(ID, 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
-attr("variant_value", Package, Variant, Value)
- :- attr("node", Package),
- node_has_variant(Package, variant(Variant)),
- attr("variant_set", Package, Variant, Value).
+attr("variant_value", PackageNode, Variant, Value)
+ :- attr("node", PackageNode),
+ node_has_variant(PackageNode, variant(Variant)),
+ attr("variant_set", PackageNode, Variant, Value).
% The rules below allow us to prefer default values for variants
% whenever possible. If a variant is set in a spec, or if it is
% specified in an external, we score it as if it was a default value.
-variant_not_default(Package, Variant, Value)
- :- attr("variant_value", Package, Variant, Value),
+variant_not_default(node(ID, Package), Variant, Value)
+ :- attr("variant_value", node(ID, Package), Variant, Value),
not variant_default_value(Package, Variant, Value),
% variants set explicitly on the CLI don't count as non-default
- not attr("variant_set", Package, Variant, Value),
+ not attr("variant_set", node(ID, Package), Variant, Value),
% variant values forced by propagation don't count as non-default
- not attr("variant_propagate", Package, Variant, Value, _),
+ not attr("variant_propagate", node(ID, Package), Variant, Value, _),
% variants set on externals that we could use don't count as non-default
% this makes spack prefer to use an external over rebuilding with the
% default configuration
- not external_with_variant_set(Package, Variant, Value),
- attr("node", Package).
+ not external_with_variant_set(node(ID, Package), Variant, Value),
+ attr("node", node(ID, Package)).
% A default variant value that is not used
-variant_default_not_used(Package, Variant, Value)
+variant_default_not_used(node(ID, Package), Variant, Value)
:- variant_default_value(Package, Variant, Value),
- node_has_variant(Package, variant(Variant)),
- not attr("variant_value", Package, Variant, Value),
- attr("node", Package).
+ node_has_variant(node(ID, Package), variant(Variant)),
+ not attr("variant_value", node(ID, Package), Variant, Value),
+ attr("node", node(ID, Package)).
% The variant is set in an external spec
-external_with_variant_set(Package, Variant, Value)
- :- attr("variant_value", Package, Variant, Value),
+external_with_variant_set(node(NodeID, Package), Variant, Value)
+ :- attr("variant_value", node(NodeID, Package), Variant, Value),
condition_requirement(ID, "variant_value", Package, Variant, Value),
facts(Package, possible_external(ID, _)),
- external(Package),
- attr("node", Package).
+ external(node(NodeID, Package)),
+ attr("node", node(NodeID, Package)).
+% FIXME (node transformation): check how to define the default value
% The default value for a variant in a package is what is prescribed:
%
% 1. On the command line
@@ -661,22 +699,22 @@ external_with_variant_set(Package, Variant, Value)
variant_default_value(Package, Variant, Value)
:- facts(Package, variant_default_value_from_package_py(Variant, Value)),
not variant_default_value_from_packages_yaml(Package, Variant, _),
- not attr("variant_default_value_from_cli", Package, Variant, _).
+ not attr("variant_default_value_from_cli", node(0, Package), Variant, _).
variant_default_value(Package, Variant, Value)
:- variant_default_value_from_packages_yaml(Package, Variant, Value),
- not attr("variant_default_value_from_cli", Package, Variant, _).
+ not attr("variant_default_value_from_cli", node(0, Package), Variant, _).
variant_default_value(Package, Variant, Value) :-
- attr("variant_default_value_from_cli", Package, Variant, Value).
+ attr("variant_default_value_from_cli", node(0, Package), Variant, Value).
% Treat 'none' in a special way - it cannot be combined with other
% values even if the variant is multi-valued
-error(100, "{0} variant '{1}' cannot have values '{2}' and 'none'", Package, Variant, Value)
- :- attr("variant_value", Package, Variant, Value),
- attr("variant_value", Package, Variant, "none"),
+error(100, "{0} variant '{1}' cannot have values '{2}' and 'none'", PackageNode, Variant, Value)
+ :- attr("variant_value", PackageNode, Variant, Value),
+ attr("variant_value", PackageNode, Variant, "none"),
Value != "none",
- build(Package).
+ build(PackageNode).
% 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
@@ -684,11 +722,11 @@ error(100, "{0} variant '{1}' cannot have values '{2}' and 'none'", Package, Var
auto_variant("dev_path").
auto_variant("patches").
-node_has_variant(Package, variant(Variant))
- :- attr("variant_set", Package, Variant, _), auto_variant(Variant).
+node_has_variant(PackageNode, variant(Variant))
+ :- attr("variant_set", PackageNode, Variant, _), auto_variant(Variant).
facts(Package, variant_single_value("dev_path"))
- :- attr("variant_set", Package, "dev_path", _).
+ :- attr("variant_set", node(ID, Package), "dev_path", _).
% suppress warnings about this atom being unset. It's only set if some
% spec or some package sets it, and without this, clingo will give
@@ -703,17 +741,17 @@ facts(Package, variant_single_value("dev_path"))
% if no platform is set, fall back to the default
:- attr("node_platform", _, Platform), not allowed_platform(Platform).
-attr("node_platform", Package, Platform)
- :- attr("node", Package),
- not attr("node_platform_set", Package),
+attr("node_platform", PackageNode, Platform)
+ :- attr("node", PackageNode),
+ not attr("node_platform_set", PackageNode),
node_platform_default(Platform).
% setting platform on a node is a hard constraint
-attr("node_platform", Package, Platform)
- :- attr("node", Package), attr("node_platform_set", Package, Platform).
+attr("node_platform", PackageNode, Platform)
+ :- attr("node", PackageNode), attr("node_platform_set", PackageNode, Platform).
% platform is set if set to anything
-attr("node_platform_set", Package) :- attr("node_platform_set", Package, _).
+attr("node_platform_set", PackageNode) :- attr("node_platform_set", PackageNode, _).
%-----------------------------------------------------------------------------
% OS semantics
@@ -722,33 +760,37 @@ attr("node_platform_set", Package) :- attr("node_platform_set", Package, _).
os(OS) :- os(OS, _).
% one os per node
-{ attr("node_os", Package, OS) : os(OS) } :- attr("node", Package).
+{ attr("node_os", PackageNode, OS) : os(OS) } :- attr("node", PackageNode).
% can't have a non-buildable OS on a node we need to build
-error(100, "Cannot select '{0} os={1}' (operating system '{1}' is not buildable)", Package, OS)
- :- build(Package),
- attr("node_os", Package, OS),
+error(100, "Cannot select '{0} os={1}' (operating system '{1}' is not buildable)", PackageNode, OS)
+ :- build(PackageNode),
+ attr("node_os", PackageNode, OS),
not buildable_os(OS).
% can't have dependencies on incompatible OS's
-error(100, "{0} and dependency {1} have incompatible operating systems 'os={2}' and 'os={3}'", Package, Dependency, PackageOS, DependencyOS)
- :- depends_on(Package, Dependency),
- attr("node_os", Package, PackageOS),
- attr("node_os", Dependency, DependencyOS),
- not os_compatible(PackageOS, DependencyOS),
- build(Package).
+error(100, "{0} and dependency {1} have incompatible operating systems 'os={2}' and 'os={3}'", PackageNode, DependencyNode, PackageNodeOS, DependencyOS)
+ :- depends_on(PackageNode, DependencyNode),
+ attr("node_os", PackageNode, PackageNodeOS),
+ attr("node_os", DependencyNode, DependencyOS),
+ not os_compatible(PackageNodeOS, DependencyOS),
+ build(PackageNode).
% give OS choice weights according to os declarations
-node_os_weight(Package, Weight)
- :- attr("node", Package),
- attr("node_os", Package, OS),
+node_os_weight(PackageNode, Weight)
+ :- attr("node", PackageNode),
+ attr("node_os", PackageNode, OS),
os(OS, Weight).
% match semantics for OS's
-node_os_match(Package, Dependency) :-
- depends_on(Package, Dependency), attr("node_os", Package, OS), attr("node_os", Dependency, OS).
-node_os_mismatch(Package, Dependency) :-
- depends_on(Package, Dependency), not node_os_match(Package, Dependency).
+node_os_match(PackageNode, DependencyNode) :-
+ depends_on(PackageNode, DependencyNode),
+ attr("node_os", PackageNode, OS),
+ attr("node_os", DependencyNode, OS).
+
+node_os_mismatch(PackageNode, DependencyNode) :-
+ depends_on(PackageNode, DependencyNode),
+ not node_os_match(PackageNode, DependencyNode).
% every OS is compatible with itself. We can use `os_compatible` to declare
os_compatible(OS, OS) :- os(OS).
@@ -764,7 +806,7 @@ os_compatible(OS1, OS3) :- os_compatible(OS1, OS2), os_compatible(OS2, OS3).
internal_error("Reused OS incompatible with build OS").
% If an OS is set explicitly respect the value
-attr("node_os", Package, OS) :- attr("node_os_set", Package, OS), attr("node", Package).
+attr("node_os", PackageNode, OS) :- attr("node_os_set", PackageNode, OS), attr("node", PackageNode).
#defined os_compatible/2.
@@ -773,132 +815,132 @@ attr("node_os", Package, OS) :- attr("node_os_set", Package, OS), attr("node", P
%-----------------------------------------------------------------------------
% Each node has only one target chosen among the known targets
-{ attr("node_target", Package, Target) : target(Target) } :- attr("node", Package).
+{ attr("node_target", PackageNode, Target) : target(Target) } :- attr("node", PackageNode).
% If a node must satisfy a target constraint, enforce it
-error(10, "'{0} target={1}' cannot satisfy constraint 'target={2}'", Package, Target, Constraint)
- :- attr("node_target", Package, Target),
- attr("node_target_satisfies", Package, Constraint),
+error(10, "'{0} target={1}' cannot satisfy constraint 'target={2}'", PackageNode, Target, Constraint)
+ :- attr("node_target", PackageNode, Target),
+ attr("node_target_satisfies", PackageNode, 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
-attr("node_target_satisfies", Package, Constraint)
- :- attr("node_target", Package, Target), target_satisfies(Constraint, Target).
+attr("node_target_satisfies", PackageNode, Constraint)
+ :- attr("node_target", PackageNode, Target), target_satisfies(Constraint, Target).
% If a node has a target, all of its dependencies must be compatible with that target
-error(100, "Cannot find compatible targets for {0} and {1}", Package, Dependency)
- :- depends_on(Package, Dependency),
- attr("node_target", Package, Target),
- not node_target_compatible(Dependency, Target).
+error(100, "Cannot find compatible targets for {0} and {1}", PackageNode, DependencyNode)
+ :- depends_on(PackageNode, DependencyNode),
+ attr("node_target", PackageNode, Target),
+ not node_target_compatible(DependencyNode, Target).
% Intermediate step for performance reasons
% When the integrity constraint above was formulated including this logic
% we suffered a substantial performance penalty
-node_target_compatible(Package, Target)
- :- attr("node_target", Package, MyTarget),
+node_target_compatible(PackageNode, Target)
+ :- attr("node_target", PackageNode, MyTarget),
target_compatible(Target, MyTarget).
#defined target_satisfies/2.
% can't use targets on node if the compiler for the node doesn't support them
-error(100, "{0} compiler '{2}@{3}' incompatible with 'target={1}'", Package, Target, Compiler, Version)
- :- attr("node_target", Package, Target),
- node_compiler(Package, CompilerID),
+error(100, "{0} compiler '{2}@{3}' incompatible with 'target={1}'", PackageNode, Target, Compiler, Version)
+ :- attr("node_target", PackageNode, Target),
+ node_compiler(PackageNode, CompilerID),
not compiler_supports_target(CompilerID, Target),
compiler_name(CompilerID, Compiler),
compiler_version(CompilerID, Version),
- build(Package).
+ build(PackageNode).
% if a target is set explicitly, respect it
-attr("node_target", Package, Target)
- :- attr("node", Package), attr("node_target_set", Package, Target).
+attr("node_target", PackageNode, Target)
+ :- attr("node", PackageNode), attr("node_target_set", PackageNode, Target).
% each node has the weight of its assigned target
-node_target_weight(Package, Weight)
- :- attr("node", Package),
- attr("node_target", Package, Target),
+node_target_weight(node(ID, Package), Weight)
+ :- attr("node", node(ID, Package)),
+ attr("node_target", node(ID, Package), Target),
facts(Package, target_weight(Target, Weight)).
% compatibility rules for targets among nodes
-node_target_match(Parent, Dependency)
- :- depends_on(Parent, Dependency),
- attr("node_target", Parent, Target),
- attr("node_target", Dependency, Target).
+node_target_match(ParentNode, DependencyNode)
+ :- depends_on(ParentNode, DependencyNode),
+ attr("node_target", ParentNode, Target),
+ attr("node_target", DependencyNode, Target).
-node_target_mismatch(Parent, Dependency)
- :- depends_on(Parent, Dependency),
- not node_target_match(Parent, Dependency).
+node_target_mismatch(ParentNode, DependencyNode)
+ :- depends_on(ParentNode, DependencyNode),
+ not node_target_match(ParentNode, DependencyNode).
% disallow reusing concrete specs that don't have a compatible target
-error(100, "'{0} target={1}' is not compatible with this machine", Package, Target)
- :- attr("node", Package),
- attr("node_target", Package, Target),
+error(100, "'{0} target={1}' is not compatible with this machine", PackageNode, Target)
+ :- attr("node", PackageNode),
+ attr("node_target", PackageNode, Target),
not target(Target).
%-----------------------------------------------------------------------------
% Compiler semantics
%-----------------------------------------------------------------------------
% There must be only one compiler set per built node.
-{ node_compiler(Package, CompilerID) : compiler_id(CompilerID) } :-
- attr("node", Package),
- build(Package).
+{ node_compiler(PackageNode, CompilerID) : compiler_id(CompilerID) } :-
+ attr("node", PackageNode),
+ build(PackageNode).
% Infer the compiler that matches a reused node
-node_compiler(Package, CompilerID)
- :- attr("node_compiler_version", Package, CompilerName, CompilerVersion),
- attr("node", Package),
+node_compiler(PackageNode, CompilerID)
+ :- attr("node_compiler_version", PackageNode, CompilerName, CompilerVersion),
+ attr("node", PackageNode),
compiler_name(CompilerID, CompilerName),
compiler_version(CompilerID, CompilerVersion),
- concrete(Package).
+ concrete(PackageNode).
% Expand the internal attribute into "attr("node_compiler_version")
-attr("node_compiler_version", Package, CompilerName, CompilerVersion)
- :- node_compiler(Package, CompilerID),
+attr("node_compiler_version", PackageNode, CompilerName, CompilerVersion)
+ :- node_compiler(PackageNode, CompilerID),
compiler_name(CompilerID, CompilerName),
compiler_version(CompilerID, CompilerVersion),
- build(Package).
+ build(PackageNode).
-attr("node_compiler", Package, CompilerName)
- :- attr("node_compiler_version", Package, CompilerName, CompilerVersion).
+attr("node_compiler", PackageNode, CompilerName)
+ :- attr("node_compiler_version", PackageNode, CompilerName, CompilerVersion).
-error(100, "No valid compiler version found for '{0}'", Package)
- :- attr("node", Package),
- not node_compiler(Package, _).
+error(100, "No valid compiler version found for '{0}'", PackageNode)
+ :- attr("node", PackageNode),
+ not node_compiler(PackageNode, _).
% We can't have a compiler be enforced and select the version from another compiler
-error(100, "Cannot select a single compiler for package {0}", Package)
- :- attr("node", Package),
- 2 { attr("node_compiler_version", Package, C, V) }.
+error(100, "Cannot select a single compiler for package {0}", PackageNode)
+ :- attr("node", PackageNode),
+ 2 { attr("node_compiler_version", PackageNode, C, V) }.
-error(100, "Cannot concretize {0} with two compilers {1} and {2}@{3}", Package, Compiler1, Compiler2, Version)
- :- attr("node_compiler", Package, Compiler1),
- attr("node_compiler_version", Package, Compiler2, Version),
+error(100, "Cannot concretize {0} with two compilers {1} and {2}@{3}", PackageNode, Compiler1, Compiler2, Version)
+ :- attr("node_compiler", PackageNode, Compiler1),
+ attr("node_compiler_version", PackageNode, Compiler2, Version),
Compiler1 != Compiler2.
% If the compiler of a node cannot be satisfied, raise
-error(10, "No valid compiler for {0} satisfies '%{1}'", Package, Compiler)
- :- attr("node", Package),
- attr("node_compiler_version_satisfies", Package, Compiler, ":"),
+error(10, "No valid compiler for {0} satisfies '%{1}'", PackageNode, Compiler)
+ :- attr("node", PackageNode),
+ attr("node_compiler_version_satisfies", PackageNode, Compiler, ":"),
not compiler_version_satisfies(Compiler, ":", _).
% If the compiler of a node must satisfy a constraint, then its version
% must be chosen among the ones that satisfy said constraint
-error(100, "No valid version for '{0}' compiler '{1}' satisfies '@{2}'", Package, Compiler, Constraint)
- :- attr("node", Package),
- attr("node_compiler_version_satisfies", Package, Compiler, Constraint),
+error(100, "No valid version for '{0}' compiler '{1}' satisfies '@{2}'", PackageNode, Compiler, Constraint)
+ :- attr("node", PackageNode),
+ attr("node_compiler_version_satisfies", PackageNode, Compiler, Constraint),
not compiler_version_satisfies(Compiler, Constraint, _).
-error(100, "No valid version for '{0}' compiler '{1}' satisfies '@{2}'", Package, Compiler, Constraint)
- :- attr("node", Package),
- attr("node_compiler_version_satisfies", Package, Compiler, Constraint),
+error(100, "No valid version for '{0}' compiler '{1}' satisfies '@{2}'", PackageNode, Compiler, Constraint)
+ :- attr("node", PackageNode),
+ attr("node_compiler_version_satisfies", PackageNode, Compiler, Constraint),
not compiler_version_satisfies(Compiler, Constraint, ID),
- node_compiler(Package, ID).
+ node_compiler(PackageNode, ID).
% 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
-attr("node_compiler_version_satisfies", Package, Compiler, Constraint)
- :- node_compiler(Package, CompilerID),
+attr("node_compiler_version_satisfies", PackageNode, Compiler, Constraint)
+ :- node_compiler(PackageNode, CompilerID),
compiler_name(CompilerID, Compiler),
compiler_version_satisfies(Compiler, Constraint, CompilerID).
@@ -906,58 +948,58 @@ attr("node_compiler_version_satisfies", Package, Compiler, Constraint)
% If the compiler version was set from the command line,
% respect it verbatim
-:- attr("node_compiler_version_set", Package, Compiler, Version),
- not attr("node_compiler_version", Package, Compiler, Version).
+:- attr("node_compiler_version_set", PackageNode, Compiler, Version),
+ not attr("node_compiler_version", PackageNode, Compiler, Version).
-:- attr("node_compiler_set", Package, Compiler),
- not attr("node_compiler_version", Package, Compiler, _).
+:- attr("node_compiler_set", PackageNode, Compiler),
+ not attr("node_compiler_version", PackageNode, Compiler, _).
% Cannot select a compiler if it is not supported on the OS
% Compilers that are explicitly marked as allowed
% are excluded from this check
-error(100, "{0} compiler '%{1}@{2}' incompatible with 'os={3}'", Package, Compiler, Version, OS)
- :- attr("node_os", Package, OS),
- node_compiler(Package, CompilerID),
+error(100, "{0} compiler '%{1}@{2}' incompatible with 'os={3}'", PackageNode, Compiler, Version, OS)
+ :- attr("node_os", PackageNode, OS),
+ node_compiler(PackageNode, CompilerID),
compiler_name(CompilerID, Compiler),
compiler_version(CompilerID, Version),
not compiler_os(CompilerID, OS),
not allow_compiler(Compiler, Version),
- build(Package).
+ build(PackageNode).
% If a package and one of its dependencies don't have the
% same compiler there's a mismatch.
-compiler_match(Package, Dependency)
- :- depends_on(Package, Dependency),
- node_compiler(Package, CompilerID),
- node_compiler(Dependency, CompilerID).
+compiler_match(PackageNode, DependencyNode)
+ :- depends_on(PackageNode, DependencyNode),
+ node_compiler(PackageNode, CompilerID),
+ node_compiler(DependencyNode, CompilerID).
-compiler_mismatch(Package, Dependency)
- :- depends_on(Package, Dependency),
- not attr("node_compiler_set", Dependency, _),
- not compiler_match(Package, Dependency).
+compiler_mismatch(PackageNode, DependencyNode)
+ :- depends_on(PackageNode, DependencyNode),
+ not attr("node_compiler_set", DependencyNode, _),
+ not compiler_match(PackageNode, DependencyNode).
-compiler_mismatch_required(Package, Dependency)
- :- depends_on(Package, Dependency),
- attr("node_compiler_set", Dependency, _),
- not compiler_match(Package, Dependency).
+compiler_mismatch_required(PackageNode, DependencyNode)
+ :- depends_on(PackageNode, DependencyNode),
+ attr("node_compiler_set", DependencyNode, _),
+ not compiler_match(PackageNode, DependencyNode).
#defined compiler_os/3.
#defined allow_compiler/2.
% compilers weighted by preference according to packages.yaml
-compiler_weight(Package, Weight)
- :- node_compiler(Package, CompilerID),
+compiler_weight(node(ID, Package), Weight)
+ :- node_compiler(node(ID, Package), CompilerID),
compiler_name(CompilerID, Compiler),
compiler_version(CompilerID, V),
facts(Package, node_compiler_preference(Compiler, V, Weight)).
-compiler_weight(Package, Weight)
- :- node_compiler(Package, CompilerID),
+compiler_weight(node(ID, Package), Weight)
+ :- node_compiler(node(ID, Package), CompilerID),
compiler_name(CompilerID, Compiler),
compiler_version(CompilerID, V),
not facts(Package, node_compiler_preference(Compiler, V, _)),
default_compiler_preference(CompilerID, Weight).
-compiler_weight(Package, 100)
- :- node_compiler(Package, CompilerID),
+compiler_weight(node(ID, Package), 100)
+ :- node_compiler(node(ID, Package), CompilerID),
compiler_name(CompilerID, Compiler),
compiler_version(CompilerID, V),
not facts(Package, node_compiler_preference(Compiler, V, _)),
@@ -965,8 +1007,8 @@ compiler_weight(Package, 100)
% For the time being, be strict and reuse only if the compiler match one we have on the system
error(100, "Compiler {1}@{2} requested for {0} cannot be found. Set install_missing_compilers:true if intended.", Package, Compiler, Version)
- :- attr("node_compiler_version", Package, Compiler, Version),
- not node_compiler(Package, _).
+ :- attr("node_compiler_version", node(ID, Package), Compiler, Version),
+ not node_compiler(node(ID, Package), _).
#defined node_compiler_preference/4.
#defined default_compiler_preference/3.
@@ -976,21 +1018,23 @@ error(100, "Compiler {1}@{2} requested for {0} cannot be found. Set install_miss
%-----------------------------------------------------------------------------
% propagate flags when compiler match
-can_inherit_flags(Package, Dependency, FlagType)
- :- depends_on(Package, Dependency),
- node_compiler(Package, CompilerID),
- node_compiler(Dependency, CompilerID),
- not attr("node_flag_set", Dependency, FlagType, _),
- compiler_id(CompilerID),
- flag_type(FlagType).
-
-node_flag_inherited(Dependency, FlagType, Flag)
- :- attr("node_flag_set", Package, FlagType, Flag), can_inherit_flags(Package, Dependency, FlagType),
- attr("node_flag_propagate", Package, FlagType).
+can_inherit_flags(PackageNode, DependencyNode, FlagType)
+ :- depends_on(PackageNode, DependencyNode),
+ node_compiler(PackageNode, CompilerID),
+ node_compiler(DependencyNode, CompilerID),
+ not attr("node_flag_set", DependencyNode, FlagType, _),
+ compiler_id(CompilerID),
+ flag_type(FlagType).
+
+node_flag_inherited(DependencyNode, FlagType, Flag)
+ :- attr("node_flag_set", PackageNode, FlagType, Flag),
+ can_inherit_flags(PackageNode, DependencyNode, FlagType),
+ attr("node_flag_propagate", PackageNode, FlagType).
+
% Ensure propagation
-:- node_flag_inherited(Package, FlagType, Flag),
- can_inherit_flags(Package, Dependency, FlagType),
- attr("node_flag_propagate", Package, FlagType).
+:- node_flag_inherited(PackageNode, FlagType, Flag),
+ can_inherit_flags(PackageNode, DependencyNode, FlagType),
+ attr("node_flag_propagate", PackageNode, FlagType).
error(100, "{0} and {1} cannot both propagate compiler flags '{2}' to {3}", Source1, Source2, Package, FlagType) :-
depends_on(Source1, Package),
@@ -1002,37 +1046,41 @@ error(100, "{0} and {1} cannot both propagate compiler flags '{2}' to {3}", Sour
Source1 < Source2.
% remember where flags came from
-attr("node_flag_source", Package, FlagType, Package) :- attr("node_flag_set", Package, FlagType, _).
-attr("node_flag_source", Dependency, FlagType, Q)
- :- attr("node_flag_source", Package, FlagType, Q), node_flag_inherited(Dependency, FlagType, _),
- attr("node_flag_propagate", Package, FlagType).
+attr("node_flag_source", PackageNode, FlagType, PackageNode)
+ :- attr("node_flag_set", PackageNode, FlagType, _).
+
+attr("node_flag_source", DependencyNode, FlagType, Q)
+ :- attr("node_flag_source", PackageNode, FlagType, Q),
+ node_flag_inherited(DependencyNode, FlagType, _),
+ attr("node_flag_propagate", PackageNode, FlagType).
% compiler flags from compilers.yaml are put on nodes if compiler matches
-attr("node_flag", Package, FlagType, Flag)
- :- compiler_flag(CompilerID, FlagType, Flag),
- node_compiler(Package, CompilerID),
- flag_type(FlagType),
- compiler_id(CompilerID),
- compiler_name(CompilerID, CompilerName),
- compiler_version(CompilerID, Version).
-
-attr("node_flag_compiler_default", Package)
- :- not attr("node_flag_set", Package, FlagType, _),
- compiler_flag(CompilerID, FlagType, Flag),
- node_compiler(Package, CompilerID),
- flag_type(FlagType),
- compiler_id(CompilerID),
- compiler_name(CompilerID, CompilerName),
- compiler_version(CompilerID, Version).
+attr("node_flag", PackageNode, FlagType, Flag)
+ :- compiler_flag(CompilerID, FlagType, Flag),
+ node_compiler(PackageNode, CompilerID),
+ flag_type(FlagType),
+ compiler_id(CompilerID),
+ compiler_name(CompilerID, CompilerName),
+ compiler_version(CompilerID, Version).
+
+attr("node_flag_compiler_default", PackageNode)
+ :- not attr("node_flag_set", PackageNode, FlagType, _),
+ compiler_flag(CompilerID, FlagType, Flag),
+ node_compiler(PackageNode, CompilerID),
+ flag_type(FlagType),
+ compiler_id(CompilerID),
+ compiler_name(CompilerID, CompilerName),
+ compiler_version(CompilerID, Version).
% if a flag is set to something or inherited, it's included
-attr("node_flag", Package, FlagType, Flag) :- attr("node_flag_set", Package, FlagType, Flag).
-attr("node_flag", Package, FlagType, Flag)
- :- node_flag_inherited(Package, FlagType, Flag).
+attr("node_flag", PackageNode, FlagType, Flag) :- attr("node_flag_set", PackageNode, FlagType, Flag).
+attr("node_flag", PackageNode, FlagType, Flag) :- node_flag_inherited(PackageNode, FlagType, Flag).
% if no node flags are set for a type, there are no flags.
-attr("no_flags", Package, FlagType)
- :- not attr("node_flag", Package, FlagType, _), attr("node", Package), flag_type(FlagType).
+attr("no_flags", PackageNode, FlagType)
+ :- not attr("node_flag", PackageNode, FlagType, _),
+ attr("node", PackageNode),
+ flag_type(FlagType).
#defined compiler_flag/3.
@@ -1041,22 +1089,22 @@ attr("no_flags", Package, FlagType)
% Installed packages
%-----------------------------------------------------------------------------
% the solver is free to choose at most one installed hash for each package
-{ attr("hash", Package, Hash) : installed_hash(Package, Hash) } 1
- :- attr("node", Package), internal_error("Package must resolve to at most one hash").
+{ attr("hash", node(ID, Package), Hash) : installed_hash(Package, Hash) } 1
+ :- attr("node", node(ID, Package)), internal_error("Package must resolve to at most one hash").
% you can't choose an installed hash for a dev spec
-:- attr("hash", Package, Hash), attr("variant_value", Package, "dev_path", _).
+:- attr("hash", PackageNode, Hash), attr("variant_value", PackageNode, "dev_path", _).
% You can't install a hash, if it is not installed
-:- attr("hash", Package, Hash), not installed_hash(Package, Hash).
+:- attr("hash", node(ID, Package), Hash), not installed_hash(Package, Hash).
% This should be redundant given the constraint above
-:- attr("node", Package), 2 { attr("hash", Package, Hash) }.
+:- attr("node", PackageNode), 2 { attr("hash", PackageNode, Hash) }.
% if a hash is selected, we impose all the constraints that implies
-impose(Hash, Package) :- attr("hash", Package, Hash).
+impose(Hash, PackageNode) :- attr("hash", PackageNode, Hash).
% if we haven't selected a hash for a package, we'll be building it
-build(Package) :- not attr("hash", Package, _), attr("node", Package).
+build(PackageNode) :- not attr("hash", PackageNode, _), attr("node", PackageNode).
% Minimizing builds is tricky. We want a minimizing criterion
@@ -1073,11 +1121,11 @@ build(Package) :- not attr("hash", Package, _), attr("node", Package).
% 200+ Shifted priorities for build nodes; correspond to priorities 0 - 99.
% 100 - 199 Unshifted priorities. Currently only includes minimizing #builds.
% 0 - 99 Priorities for non-built nodes.
-build_priority(Package, 200) :- build(Package), attr("node", Package), optimize_for_reuse().
-build_priority(Package, 0) :- not build(Package), attr("node", Package), optimize_for_reuse().
+build_priority(PackageNode, 200) :- build(PackageNode), attr("node", PackageNode), optimize_for_reuse().
+build_priority(PackageNode, 0) :- not build(PackageNode), attr("node", PackageNode), optimize_for_reuse().
% don't adjust build priorities if reuse is not enabled
-build_priority(Package, 0) :- attr("node", Package), not optimize_for_reuse().
+build_priority(PackageNode, 0) :- attr("node", PackageNode), not optimize_for_reuse().
% don't assign versions from installed packages unless reuse is enabled
% NOTE: that "installed" means the declared version was only included because
@@ -1090,8 +1138,8 @@ build_priority(Package, 0) :- attr("node", Package), not optimize_for_reuse().
% currently *won't* force versions for `bar`'s build dependencies -- `--fresh`
% will instead build the latest bar. When we actually include transitive
% build deps in the solve, consider using them as a preference to resolve this.
-:- attr("version", Package, Version),
- version_weight(Package, Weight),
+:- attr("version", node(ID, Package), Version),
+ version_weight(node(ID, Package), Weight),
facts(Package, version_declared(Version, Weight, "installed")),
not optimize_for_reuse().
@@ -1121,7 +1169,7 @@ build_priority(Package, 0) :- attr("node", Package), not optimize_for_reuse().
% Try hard to reuse installed packages (i.e., minimize the number built)
opt_criterion(100, "number of packages to build (vs. reuse)").
#minimize { 0@100: #true }.
-#minimize { 1@100,Package : build(Package), optimize_for_reuse() }.
+#minimize { 1@100,PackageNode : build(PackageNode), optimize_for_reuse() }.
#defined optimize_for_reuse/0.
% A condition group specifies one or more specs that must be satisfied.
@@ -1131,9 +1179,9 @@ opt_criterion(75, "requirement weight").
#minimize{ 0@275: #true }.
#minimize{ 0@75: #true }.
#minimize {
- Weight@75+Priority,Package,Group
- : requirement_weight(Package, Group, Weight),
- build_priority(Package, Priority)
+ Weight@75+Priority,PackageNode,Group
+ : requirement_weight(PackageNode, Group, Weight),
+ build_priority(PackageNode, Priority)
}.
% Minimize the number of deprecated versions being used
@@ -1141,9 +1189,9 @@ opt_criterion(73, "deprecated versions used").
#minimize{ 0@273: #true }.
#minimize{ 0@73: #true }.
#minimize{
- 1@73+Priority,Package
- : attr("deprecated", Package, _),
- build_priority(Package, Priority)
+ 1@73+Priority,PackageNode
+ : attr("deprecated", PackageNode, _),
+ build_priority(PackageNode, Priority)
}.
% Minimize the:
@@ -1155,38 +1203,39 @@ opt_criterion(70, "version weight").
#minimize{ 0@70: #true }.
#minimize {
Weight@70+Priority
- : attr("root", Package), version_weight(Package, Weight),
- build_priority(Package, Priority)
+ : attr("root", PackageNode),
+ version_weight(PackageNode, Weight),
+ build_priority(PackageNode, Priority)
}.
opt_criterion(65, "number of non-default variants (roots)").
#minimize{ 0@265: #true }.
#minimize{ 0@65: #true }.
#minimize {
- 1@65+Priority,Package,Variant,Value
- : variant_not_default(Package, Variant, Value),
- attr("root", Package),
- build_priority(Package, Priority)
+ 1@65+Priority,PackageNode,Variant,Value
+ : variant_not_default(PackageNode, Variant, Value),
+ attr("root", PackageNode),
+ build_priority(PackageNode, Priority)
}.
opt_criterion(60, "preferred providers for roots").
#minimize{ 0@260: #true }.
#minimize{ 0@60: #true }.
#minimize{
- Weight@60+Priority,Provider,Virtual
- : provider_weight(Provider, Virtual, Weight),
- attr("root", Provider),
- build_priority(Provider, Priority)
+ Weight@60+Priority,ProviderNode,Virtual
+ : provider_weight(ProviderNode, Virtual, Weight),
+ attr("root", ProviderNode),
+ build_priority(ProviderNode, Priority)
}.
opt_criterion(55, "default values of variants not being used (roots)").
#minimize{ 0@255: #true }.
#minimize{ 0@55: #true }.
#minimize{
- 1@55+Priority,Package,Variant,Value
- : variant_default_not_used(Package, Variant, Value),
- attr("root", Package),
- build_priority(Package, Priority)
+ 1@55+Priority,PackageNode,Variant,Value
+ : variant_default_not_used(PackageNode, Variant, Value),
+ attr("root", PackageNode),
+ build_priority(PackageNode, Priority)
}.
% Try to use default variants or variants that have been set
@@ -1194,10 +1243,10 @@ opt_criterion(50, "number of non-default variants (non-roots)").
#minimize{ 0@250: #true }.
#minimize{ 0@50: #true }.
#minimize {
- 1@50+Priority,Package,Variant,Value
- : variant_not_default(Package, Variant, Value),
- not attr("root", Package),
- build_priority(Package, Priority)
+ 1@50+Priority,PackageNode,Variant,Value
+ : variant_not_default(PackageNode, Variant, Value),
+ not attr("root", PackageNode),
+ build_priority(PackageNode, Priority)
}.
% Minimize the weights of the providers, i.e. use as much as
@@ -1206,9 +1255,10 @@ opt_criterion(45, "preferred providers (non-roots)").
#minimize{ 0@245: #true }.
#minimize{ 0@45: #true }.
#minimize{
- Weight@45+Priority,Provider,Virtual
- : provider_weight(Provider, Virtual, Weight), not attr("root", Provider),
- build_priority(Provider, Priority)
+ Weight@45+Priority,ProviderNode,Virtual
+ : provider_weight(ProviderNode, Virtual, Weight),
+ not attr("root", ProviderNode),
+ build_priority(ProviderNode, Priority)
}.
% Try to minimize the number of compiler mismatches in the DAG.
@@ -1216,18 +1266,18 @@ opt_criterion(40, "compiler mismatches that are not from CLI").
#minimize{ 0@240: #true }.
#minimize{ 0@40: #true }.
#minimize{
- 1@40+Priority,Package,Dependency
- : compiler_mismatch(Package, Dependency),
- build_priority(Package, Priority)
+ 1@40+Priority,PackageNode,DependencyNode
+ : compiler_mismatch(PackageNode, DependencyNode),
+ build_priority(PackageNode, Priority)
}.
opt_criterion(39, "compiler mismatches that are not from CLI").
#minimize{ 0@239: #true }.
#minimize{ 0@39: #true }.
#minimize{
- 1@39+Priority,Package,Dependency
- : compiler_mismatch_required(Package, Dependency),
- build_priority(Package, Priority)
+ 1@39+Priority,PackageNode,DependencyNode
+ : compiler_mismatch_required(PackageNode, DependencyNode),
+ build_priority(PackageNode, Priority)
}.
% Try to minimize the number of compiler mismatches in the DAG.
@@ -1235,18 +1285,18 @@ opt_criterion(35, "OS mismatches").
#minimize{ 0@235: #true }.
#minimize{ 0@35: #true }.
#minimize{
- 1@35+Priority,Package,Dependency
- : node_os_mismatch(Package, Dependency),
- build_priority(Package, Priority)
+ 1@35+Priority,PackageNode,DependencyNode
+ : node_os_mismatch(PackageNode, DependencyNode),
+ build_priority(PackageNode, Priority)
}.
opt_criterion(30, "non-preferred OS's").
#minimize{ 0@230: #true }.
#minimize{ 0@30: #true }.
#minimize{
- Weight@30+Priority,Package
- : node_os_weight(Package, Weight),
- build_priority(Package, Priority)
+ Weight@30+Priority,PackageNode
+ : node_os_weight(PackageNode, Weight),
+ build_priority(PackageNode, Priority)
}.
% Choose more recent versions for nodes
@@ -1254,9 +1304,9 @@ opt_criterion(25, "version badness").
#minimize{ 0@225: #true }.
#minimize{ 0@25: #true }.
#minimize{
- Weight@25+Priority,Package
- : version_weight(Package, Weight),
- build_priority(Package, Priority)
+ Weight@25+Priority,PackageNode
+ : version_weight(PackageNode, Weight),
+ build_priority(PackageNode, Priority)
}.
% Try to use all the default values of variants
@@ -1264,10 +1314,10 @@ opt_criterion(20, "default values of variants not being used (non-roots)").
#minimize{ 0@220: #true }.
#minimize{ 0@20: #true }.
#minimize{
- 1@20+Priority,Package,Variant,Value
- : variant_default_not_used(Package, Variant, Value),
- not attr("root", Package),
- build_priority(Package, Priority)
+ 1@20+Priority,PackageNode,Variant,Value
+ : variant_default_not_used(PackageNode, Variant, Value),
+ not attr("root", PackageNode),
+ build_priority(PackageNode, Priority)
}.
% Try to use preferred compilers
@@ -1275,9 +1325,9 @@ opt_criterion(15, "non-preferred compilers").
#minimize{ 0@215: #true }.
#minimize{ 0@15: #true }.
#minimize{
- Weight@15+Priority,Package
- : compiler_weight(Package, Weight),
- build_priority(Package, Priority)
+ Weight@15+Priority,PackageNode
+ : compiler_weight(PackageNode, Weight),
+ build_priority(PackageNode, Priority)
}.
% Minimize the number of mismatches for targets in the DAG, try
@@ -1286,18 +1336,18 @@ opt_criterion(10, "target mismatches").
#minimize{ 0@210: #true }.
#minimize{ 0@10: #true }.
#minimize{
- 1@10+Priority,Package,Dependency
- : node_target_mismatch(Package, Dependency),
- build_priority(Package, Priority)
+ 1@10+Priority,PackageNode,Dependency
+ : node_target_mismatch(PackageNode, Dependency),
+ build_priority(PackageNode, Priority)
}.
opt_criterion(5, "non-preferred targets").
#minimize{ 0@205: #true }.
#minimize{ 0@5: #true }.
#minimize{
- Weight@5+Priority,Package
- : node_target_weight(Package, Weight),
- build_priority(Package, Priority)
+ Weight@5+Priority,PackageNode
+ : node_target_weight(PackageNode, Weight),
+ build_priority(PackageNode, Priority)
}.
%-----------------
@@ -1305,28 +1355,28 @@ opt_criterion(5, "non-preferred targets").
%-----------------
#heuristic literal_solved(ID) : literal(ID). [1, sign]
#heuristic literal_solved(ID) : literal(ID). [50, init]
-#heuristic attr("hash", Package, Hash) : attr("root", Package). [45, init]
+#heuristic attr("hash", PackageNode, Hash) : attr("root", PackageNode). [45, init]
-#heuristic attr("version", Package, Version) : facts(Package, version_declared(Version, 0)), attr("root", Package). [40, true]
-#heuristic version_weight(Package, 0) : facts(Package, version_declared(Version, 0)), attr("root", Package). [40, true]
-#heuristic attr("variant_value", Package, Variant, Value) : variant_default_value(Package, Variant, Value), attr("root", Package). [40, true]
-#heuristic attr("node_target", Package, Target) : facts(Package, target_weight(Target, 0)), attr("root", Package). [40, true]
-#heuristic node_target_weight(Package, 0) : attr("root", Package). [40, true]
-#heuristic node_compiler(Package, CompilerID) : default_compiler_preference(ID, 0), compiler_id(ID), attr("root", Package). [40, true]
+#heuristic attr("version", node(0, Package), Version) : facts(Package, version_declared(Version, 0)), attr("root", node(0, Package)). [40, true]
+#heuristic version_weight(node(0, Package), 0) : facts(Package, version_declared(Version, 0)), attr("root", node(0, Package)). [40, true]
+#heuristic attr("variant_value", node(0, Package), Variant, Value) : variant_default_value(Package, Variant, Value), attr("root", node(0, Package)). [40, true]
+#heuristic attr("node_target", node(0, Package), Target) : facts(Package, target_weight(Target, 0)), attr("root", node(0, Package)). [40, true]
+#heuristic node_target_weight(node(0, Package), 0) : attr("root", node(0, Package)). [40, true]
+#heuristic node_compiler(node(0, Package), CompilerID) : default_compiler_preference(ID, 0), compiler_id(ID), attr("root", node(0, Package)). [40, true]
-#heuristic provider(Package, Virtual) : possible_provider_weight(Package, Virtual, 0, _), attr("virtual_node", Virtual). [30, true]
+#heuristic provider(PackageNode, VirtualNode) : possible_provider_weight(PackageNode, VirtualNode, 0, _), attr("virtual_node", VirtualNode). [30, true]
#heuristic provider_weight(Package, Virtual, 0, R) : possible_provider_weight(Package, Virtual, 0, R), attr("virtual_node", Virtual). [30, true]
#heuristic attr("node", Package) : possible_provider_weight(Package, Virtual, 0, _), attr("virtual_node", Virtual). [30, true]
-#heuristic attr("version", Package, Version) : facts(Package, version_declared(Version, 0)), attr("node", Package). [20, true]
-#heuristic version_weight(Package, 0) : facts(Package, version_declared(Version, 0)), attr("node", Package). [20, true]
+#heuristic attr("version", node(ID, Package), Version) : facts(Package, version_declared(Version, 0)), attr("node", node(ID, Package)). [20, true]
+#heuristic version_weight(node(ID, Package), 0) : facts(Package, version_declared(Version, 0)), attr("node", node(ID, Package)). [20, true]
-#heuristic attr("node_target", Package, Target) : facts(Package, target_weight(Target, 0)), attr("node", Package). [20, true]
+#heuristic attr("node_target", node(ID, Package), Target) : facts(Package, target_weight(Target, 0)), attr("node", node(ID, Package)). [20, true]
#heuristic node_target_weight(Package, 0) : attr("node", Package). [20, true]
-#heuristic node_compiler(Package, CompilerID) : default_compiler_preference(ID, 0), compiler_id(ID), attr("node", Package). [15, true]
+#heuristic node_compiler(node(ID, Package), ID) : default_compiler_preference(ID, 0), compiler_id(ID), attr("node", node(NodeID, Package)). [15, true]
-#heuristic attr("variant_value", Package, Variant, Value) : variant_default_value(Package, Variant, Value), attr("node", Package). [10, true]
-#heuristic attr("node_os", Package, OS) : buildable_os(OS). [10, true]
+#heuristic attr("variant_value", node(ID, Package), Variant, Value) : variant_default_value(Package, Variant, Value), attr("node", node(ID, Package)). [10, true]
+#heuristic attr("node_os", PackageNode, OS) : buildable_os(OS). [10, true]
%-----------
% Notes
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index af534e3f84..561cd7baba 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -2983,9 +2983,10 @@ class Spec:
providers = [spec.name for spec in answer.values() if spec.package.provides(name)]
name = providers[0]
- assert name in answer
+ node = spack.solver.asp.SpecBuilder.root_node(pkg=name)
+ assert node in answer, f"cannot find {name} in the list of specs {','.join(answer.keys())}"
- concretized = answer[name]
+ concretized = answer[node]
self._dup(concretized)
def concretize(self, tests=False):
@@ -3519,7 +3520,8 @@ class Spec:
for value in values:
if self.variants.get(variant_name):
msg = (
- "Cannot append a value to a single-valued " "variant with an already set value"
+ f"cannot append the new value '{value}' to the single-valued "
+ f"variant '{self.variants[variant_name]}'"
)
assert pkg_variant.multi, msg
self.variants[variant_name].append(value)