From 27ab53b68a3a8e067ccec9b1e93f1939cdde1e3b Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Fri, 16 Jun 2023 15:25:30 +0200 Subject: 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. --- lib/spack/spack/solver/asp.py | 152 +++-- lib/spack/spack/solver/concretize.lp | 1018 ++++++++++++++++++---------------- lib/spack/spack/spec.py | 8 +- 3 files changed, 635 insertions(+), 543 deletions(-) (limited to 'lib') 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) -- cgit v1.2.3-60-g2f50