summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/directives.py22
-rw-r--r--lib/spack/spack/package_base.py1
-rw-r--r--lib/spack/spack/solver/asp.py64
-rw-r--r--lib/spack/spack/solver/concretize.lp8
-rw-r--r--lib/spack/spack/solver/counter.py13
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"""