diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/directives.py | 22 | ||||
-rw-r--r-- | lib/spack/spack/package_base.py | 1 | ||||
-rw-r--r-- | lib/spack/spack/solver/asp.py | 64 | ||||
-rw-r--r-- | lib/spack/spack/solver/concretize.lp | 8 | ||||
-rw-r--r-- | lib/spack/spack/solver/counter.py | 13 |
5 files changed, 93 insertions, 15 deletions
diff --git a/lib/spack/spack/directives.py b/lib/spack/spack/directives.py index 0d4834ed13..b1f5526d22 100644 --- a/lib/spack/spack/directives.py +++ b/lib/spack/spack/directives.py @@ -94,6 +94,9 @@ Patcher = Callable[[Union["spack.package_base.PackageBase", Dependency]], None] PatchesType = Optional[Union[Patcher, str, List[Union[Patcher, str]]]] +SUPPORTED_LANGUAGES = ("fortran", "cxx") + + def _make_when_spec(value: WhenType) -> Optional["spack.spec.Spec"]: """Create a ``Spec`` that indicates when a directive should be applied. @@ -585,6 +588,9 @@ def depends_on( @see The section "Dependency specs" in the Spack Packaging Guide. """ + if spack.spec.Spec(spec).name in SUPPORTED_LANGUAGES: + assert type == "build", "languages must be of 'build' type" + return _language(lang_spec_str=spec, when=when) def _execute_depends_on(pkg: "spack.package_base.PackageBase"): _depends_on(pkg, spec, when=when, type=type, patches=patches) @@ -967,7 +973,6 @@ def license( checked_by: string or list of strings indicating which github user checked the license (if any). when: A spec specifying when the license applies. - when: A spec specifying when the license applies. """ return lambda pkg: _execute_license(pkg, license_identifier, when) @@ -1014,6 +1019,21 @@ def requires(*requirement_specs: str, policy="one_of", when=None, msg=None): return _execute_requires +@directive("languages") +def _language(lang_spec_str: str, *, when: Optional[Union[str, bool]] = None): + """Temporary implementation of language virtuals, until compilers are proper dependencies.""" + + def _execute_languages(pkg: "spack.package_base.PackageBase"): + when_spec = _make_when_spec(when) + if not when_spec: + return + + languages = pkg.languages.setdefault(when_spec, set()) + languages.add(lang_spec_str) + + return _execute_languages + + class DirectiveError(spack.error.SpackError): """This is raised when something is wrong with a package directive.""" diff --git a/lib/spack/spack/package_base.py b/lib/spack/spack/package_base.py index 2f8e0ae003..89b45ff65f 100644 --- a/lib/spack/spack/package_base.py +++ b/lib/spack/spack/package_base.py @@ -567,6 +567,7 @@ class PackageBase(WindowsRPath, PackageViewMixin, metaclass=PackageMeta): provided_together: Dict["spack.spec.Spec", List[Set[str]]] patches: Dict["spack.spec.Spec", List["spack.patch.Patch"]] variants: Dict[str, Tuple["spack.variant.Variant", "spack.spec.Spec"]] + languages: Dict["spack.spec.Spec", Set[str]] #: By default, packages are not virtual #: Virtual packages override this attribute diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 2cf4b5cbf4..b0ee0d0297 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -1040,6 +1040,16 @@ class SpackSolverSetup: ) self.gen.newline() + def package_languages(self, pkg): + for when_spec, languages in pkg.languages.items(): + condition_msg = f"{pkg.name} needs the {', '.join(sorted(languages))} language" + if when_spec != spack.spec.Spec(): + condition_msg += f" when {when_spec}" + condition_id = self.condition(when_spec, name=pkg.name, msg=condition_msg) + for language in sorted(languages): + self.gen.fact(fn.pkg_fact(pkg.name, fn.language(condition_id, language))) + self.gen.newline() + def compiler_facts(self): """Facts about available compilers.""" @@ -1089,6 +1099,9 @@ class SpackSolverSetup: self.pkg_version_rules(pkg) self.gen.newline() + # languages + self.package_languages(pkg) + # variants self.variant_rules(pkg) @@ -2294,8 +2307,6 @@ class SpackSolverSetup: self.possible_virtuals = node_counter.possible_virtuals() self.pkgs = node_counter.possible_dependencies() - self.pkgs.update(spack.repo.PATH.packages_with_tags("runtime")) - # Fail if we already know an unreachable node is requested for spec in specs: missing_deps = [ @@ -2309,7 +2320,6 @@ class SpackSolverSetup: self.explicitly_required_namespaces[node.name] = node.namespace self.gen = ProblemInstanceBuilder() - if not allow_deprecated: self.gen.fact(fn.deprecated_versions_not_allowed()) @@ -2439,14 +2449,14 @@ class SpackSolverSetup: """Define the constraints to be imposed on the runtimes""" recorder = RuntimePropertyRecorder(self) for compiler in self.possible_compilers: - if compiler.name != "gcc": - continue + compiler_with_different_cls_names = {"oneapi": "intel-oneapi-compilers"} + compiler_cls_name = compiler_with_different_cls_names.get(compiler.name, compiler.name) try: - compiler_cls = spack.repo.PATH.get_pkg_class(compiler.name) + compiler_cls = spack.repo.PATH.get_pkg_class(compiler_cls_name) except spack.repo.UnknownPackageError: continue if hasattr(compiler_cls, "runtime_constraints"): - compiler_cls.runtime_constraints(compiler=compiler, pkg=recorder) + compiler_cls.runtime_constraints(spec=compiler.spec, pkg=recorder) recorder.consume_facts() @@ -2858,13 +2868,24 @@ class RuntimePropertyRecorder: """Resets the current state.""" self.current_package = None - def depends_on(self, dependency_str: str, *, when: str, type: str, description: str) -> None: + def depends_on( + self, + dependency_str: str, + *, + when: str, + type: str, + description: str, + languages: Optional[List[str]] = None, + ) -> None: """Injects conditional dependencies on packages. + Conditional dependencies can be either "real" packages or virtual dependencies. + Args: dependency_str: the dependency spec to inject when: anonymous condition to be met on a package to have the dependency type: dependency type + languages: languages needed by the package for the dependency to be considered description: human-readable description of the rule for adding the dependency """ # TODO: The API for this function is not final, and is still subject to change. At @@ -2890,26 +2911,45 @@ class RuntimePropertyRecorder: f" not external({node_variable}),\n" f" not runtime(Package)" ).replace(f'"{placeholder}"', f"{node_variable}") + if languages: + body_str += ",\n" + for language in languages: + body_str += f' attr("language", {node_variable}, "{language}")' + head_clauses = self._setup.spec_clauses(dependency_spec, body=False) runtime_pkg = dependency_spec.name + + is_virtual = head_clauses[0].args[0] == "virtual_node" main_rule = ( f"% {description}\n" f'1 {{ attr("depends_on", {node_variable}, node(0..X-1, "{runtime_pkg}"), "{type}") :' - f' max_dupes("gcc-runtime", X)}} 1:-\n' + f' max_dupes("{runtime_pkg}", X)}} 1:-\n' f"{body_str}.\n\n" ) + if is_virtual: + main_rule = ( + f"% {description}\n" + f'attr("dependency_holds", {node_variable}, "{runtime_pkg}", "{type}") :-\n' + f"{body_str}.\n\n" + ) + self.rules.append(main_rule) for clause in head_clauses: if clause.args[0] == "node": continue runtime_node = f'node(RuntimeID, "{runtime_pkg}")' head_str = str(clause).replace(f'"{runtime_pkg}"', runtime_node) - rule = ( - f"{head_str} :-\n" + depends_on_constraint = ( f' attr("depends_on", {node_variable}, {runtime_node}, "{type}"),\n' - f"{body_str}.\n\n" ) + if is_virtual: + depends_on_constraint = ( + f' attr("depends_on", {node_variable}, ProviderNode, "{type}"),\n' + f" provider(ProviderNode, {runtime_node}),\n" + ) + + rule = f"{head_str} :-\n" f"{depends_on_constraint}" f"{body_str}.\n\n" self.rules.append(rule) self.reset() diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp index a186290824..00be3e94db 100644 --- a/lib/spack/spack/solver/concretize.lp +++ b/lib/spack/spack/solver/concretize.lp @@ -159,6 +159,14 @@ error(100, multiple_values_error, Attribute, Package) 2 { attr(Attribute, node(ID, Package), Value) }. %----------------------------------------------------------------------------- +% Languages used +%----------------------------------------------------------------------------- + +attr("language", node(X, Package), Language) :- + condition_holds(ConditionID, node(X, Package)), + pkg_fact(Package,language(ConditionID, Language)). + +%----------------------------------------------------------------------------- % Version semantics %----------------------------------------------------------------------------- diff --git a/lib/spack/spack/solver/counter.py b/lib/spack/spack/solver/counter.py index 72bff5f5bf..8da8aa1b4e 100644 --- a/lib/spack/spack/solver/counter.py +++ b/lib/spack/spack/solver/counter.py @@ -10,6 +10,7 @@ from llnl.util import lang import spack.deptypes as dt import spack.package_base import spack.repo +import spack.spec PossibleDependencies = Set[str] @@ -24,7 +25,13 @@ class Counter: """ def __init__(self, specs: List["spack.spec.Spec"], tests: bool) -> None: - self.specs = specs + runtime_pkgs = spack.repo.PATH.packages_with_tags("runtime") + runtime_virtuals = set() + for x in runtime_pkgs: + pkg_class = spack.repo.PATH.get_pkg_class(x) + runtime_virtuals.update(pkg_class.provided_virtual_names()) + + self.specs = specs + [spack.spec.Spec(x) for x in runtime_pkgs] self.link_run_types: dt.DepFlag = dt.LINK | dt.RUN | dt.TEST self.all_types: dt.DepFlag = dt.ALL @@ -33,7 +40,9 @@ class Counter: self.all_types = dt.LINK | dt.RUN | dt.BUILD self._possible_dependencies: PossibleDependencies = set() - self._possible_virtuals: Set[str] = set(x.name for x in specs if x.virtual) + self._possible_virtuals: Set[str] = ( + set(x.name for x in specs if x.virtual) | runtime_virtuals + ) def possible_dependencies(self) -> PossibleDependencies: """Returns the list of possible dependencies""" |