diff --git a/lib/spack/spack/schema/concretizer.py b/lib/spack/spack/schema/concretizer.py index e628519d56f..30f73d7557d 100644 --- a/lib/spack/spack/schema/concretizer.py +++ b/lib/spack/spack/schema/concretizer.py @@ -15,6 +15,7 @@ "additionalProperties": False, "properties": { "reuse": {"type": "boolean"}, + "enable_node_namespace": {"type": "boolean"}, "targets": { "type": "object", "properties": { diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 5f387636cc0..cabaa9f2f28 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -595,13 +595,14 @@ def fact(self, head): if choice: self.assumptions.append(atom) - def solve(self, setup, specs, reuse=None, output=None, control=None): + def solve(self, setup, specs, reuse=None, namespace=False, output=None, control=None): """Set up the input and solve for dependencies of ``specs``. Arguments: setup (SpackSolverSetup): An object to set up the ASP problem. specs (list): List of ``Spec`` objects to solve for. reuse (None or list): list of concrete specs that can be reused + namespace (bool): enable node namespacing output (None or OutputConfiguration): configuration object to set the output of this solve. control (clingo.Control): configuration for the solver. If None, @@ -624,7 +625,7 @@ def solve(self, setup, specs, reuse=None, output=None, control=None): self.assumptions = [] with self.control.backend() as backend: self.backend = backend - setup.setup(self, specs, reuse=reuse) + setup.setup(self, specs, reuse=reuse, namespace=namespace) timer.phase("setup") # read in the main ASP program and display logic -- these are @@ -760,6 +761,10 @@ def __init__(self, tests=False): # whether to add installed/binary hashes to the solve self.tests = tests + # whether to namespace nodes in the solve + # set by setup() + self.namespace = None + # If False allows for input specs that are not solved self.concretize_everything = True @@ -1348,6 +1353,7 @@ def _spec_clauses( # TODO: do this with consistent suffixes. class Head(object): node = fn.node + node_namespace = fn.node_namespace virtual_node = fn.virtual_node node_platform = fn.node_platform_set node_os = fn.node_os_set @@ -1361,6 +1367,7 @@ class Head(object): class Body(object): node = fn.node + node_namespace = fn.node_namespace virtual_node = fn.virtual_node node_platform = fn.node_platform node_os = fn.node_os @@ -1377,6 +1384,9 @@ class Body(object): if spec.name: clauses.append(f.node(spec.name) if not spec.virtual else f.virtual_node(spec.name)) + if self.namespace and spec.namespace: + clauses.append(f.node_namespace(spec.name, spec.namespace)) + clauses.extend(self.spec_versions(spec)) # seed architecture at the root (we'll propagate later) @@ -1917,7 +1927,7 @@ def define_concrete_input_specs(self, specs, possible): if spec.concrete: self._facts_from_concrete_spec(spec, possible) - def setup(self, driver, specs, reuse=None): + def setup(self, driver, specs, reuse=None, namespace=False): """Generate an ASP program with relevant constraints for specs. This calls methods on the solve driver to set up the problem with @@ -1928,6 +1938,7 @@ def setup(self, driver, specs, reuse=None): driver (PyclingoDriver): driver instance of this solve specs (list): list of Specs to solve reuse (None or list): list of concrete specs that can be reused + namespace (bool): enable namespace matching in the solve """ self._condition_id_counter = itertools.count() @@ -1980,6 +1991,10 @@ def setup(self, driver, specs, reuse=None): self.gen.h1("Concrete input spec definitions") self.define_concrete_input_specs(specs, possible) + self.namespace = namespace + if namespace: + self.gen.fact(fn.enable_node_namespace()) + if reuse: self.gen.h1("Reusable specs") self.gen.fact(fn.optimize_for_reuse()) @@ -2073,6 +2088,9 @@ def node(self, pkg): if pkg not in self._specs: self._specs[pkg] = spack.spec.Spec(pkg) + def node_namespace(self, pkg, namespace): + self._specs[pkg].namespace = namespace + def _arch(self, pkg): arch = self._specs[pkg].architecture if not arch: @@ -2368,6 +2386,7 @@ def __init__(self): # These properties are settable via spack configuration, and overridable # by setting them directly as properties. self.reuse = spack.config.get("concretizer:reuse", False) + self.namespace = spack.config.get("concretizer:enable_node_namespace", False) @staticmethod def _check_input_and_extract_concrete_specs(specs): @@ -2430,7 +2449,9 @@ def solve( reusable_specs.extend(self._reusable_specs()) setup = SpackSolverSetup(tests=tests) output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=setup_only) - result, _, _ = self.driver.solve(setup, specs, reuse=reusable_specs, output=output) + result, _, _ = self.driver.solve( + setup, specs, reuse=reusable_specs, namespace=self.namespace, output=output + ) return result def solve_in_rounds( @@ -2467,7 +2488,7 @@ def solve_in_rounds( output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=False) while True: result, _, _ = self.driver.solve( - setup, input_specs, reuse=reusable_specs, output=output + setup, input_specs, reuse=reusable_specs, namespace=self.namespace, output=output ) yield result diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp index af5dffbc8c8..03d9f50ead9 100644 --- a/lib/spack/spack/solver/concretize.lp +++ b/lib/spack/spack/solver/concretize.lp @@ -210,6 +210,14 @@ attr(Name, A1, A2, A3, A4) :- impose(ID), imposed_constraint(ID, Name, A1, A2, A not imposed_constraint(Hash, "node_flag", Package, FlagType, Flag), internal_error("imposed hash without imposing all flag values"). +% cannot have multiple namespaces for a package +:- node(Package), + node_namespace(Package, Namespace1), + node_namespace(Package, Namespace2), + Namespace1 < Namespace2, + enable_node_namespace(). + +#defined enable_node_namespace/0. #defined condition/2. #defined condition_requirement/3. #defined condition_requirement/4. @@ -405,6 +413,7 @@ possible_provider_weight(Dependency, Virtual, 100, "fallback") :- provider(Depen % These allow us to easily define conditional dependency and conflict rules % without enumerating all spec attributes every time. node(Package) :- attr("node", Package). +node_namespace(Package, Namespace) :- attr("node_namespace", Package, Namespace). virtual_node(Virtual) :- attr("virtual_node", Virtual). hash(Package, Hash) :- attr("hash", Package, Hash). version(Package, Version) :- attr("version", Package, Version). @@ -427,6 +436,7 @@ node_flag_propagate(Package, FlagType) :- attr("node_flag_propagate", Package, FlagType). attr("node", Package) :- node(Package). +attr("node_namespace", Package, Namespace) :- node_namespace(Package, Namespace). attr("virtual_node", Virtual) :- virtual_node(Virtual). attr("hash", Package, Hash) :- hash(Package, Hash). attr("version", Package, Version) :- version(Package, Version). @@ -464,6 +474,7 @@ attr("node_flag_propagate", Package, FlagType) #defined node_version_satisfies/2. #defined node_compiler_version_satisfies/3. #defined root/1. +#defined node_namespace/2. %----------------------------------------------------------------------------- % External semantics diff --git a/lib/spack/spack/solver/display.lp b/lib/spack/spack/solver/display.lp index 4ba8e9e2cb5..54bbe584df7 100644 --- a/lib/spack/spack/solver/display.lp +++ b/lib/spack/spack/solver/display.lp @@ -12,6 +12,7 @@ % Spec-related functions. % Used to build the result of the solve. #show node/1. +#show node_namespace/2. #show hash/2. #show depends_on/3. #show version/2.