gcc/oneapi: inject runtime iff language virtual (#49956)

Currently we inject runtimes when a package has a direct build dep on a
compiler, but what matters is whether the package depends on a language.

That way we can avoid recursion of injecting runtimes to runtimes
without a rule in the solver: runtimes don't depend on languages, they
just have a build dep on the same compiler.
This commit is contained in:
Harmen Stoppels 2025-04-08 19:51:16 +02:00 committed by GitHub
parent 4372907fc1
commit 8ffd6c29bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 46 additions and 73 deletions

View File

@ -3201,12 +3201,13 @@ def define_runtime_constraints(self) -> List[spack.spec.Spec]:
# FIXME (compiler as nodes): think of using isinstance(compiler_cls, WrappedCompiler) # FIXME (compiler as nodes): think of using isinstance(compiler_cls, WrappedCompiler)
# Add a dependency on the compiler wrapper # Add a dependency on the compiler wrapper
recorder("*").depends_on( for language in ("c", "cxx", "fortran"):
"compiler-wrapper", recorder("*").depends_on(
when=f"%{compiler.name}@{compiler.versions}", "compiler-wrapper",
type="build", when=f"%[virtuals={language}] {compiler.name}@{compiler.versions}",
description=f"Add the compiler wrapper when using {compiler}", type="build",
) description=f"Add the compiler wrapper when using {compiler} for {language}",
)
if not using_libc_compatibility(): if not using_libc_compatibility():
continue continue
@ -3601,11 +3602,9 @@ def rule_body_from(self, when_spec: "spack.spec.Spec") -> Tuple[str, str]:
# (avoid adding virtuals everywhere, if a single edge needs it) # (avoid adding virtuals everywhere, if a single edge needs it)
_, provider, virtual = clause.args _, provider, virtual = clause.args
clause.args = "virtual_on_edge", node_placeholder, provider, virtual clause.args = "virtual_on_edge", node_placeholder, provider, virtual
body_str = ( body_str = ",\n".join(f" {x}" for x in body_clauses)
f" {f',{os.linesep} '.join(str(x) for x in body_clauses)},\n" body_str += f",\n not external({node_variable})"
f" not external({node_variable}),\n" body_str = body_str.replace(f'"{node_placeholder}"', f"{node_variable}")
f" not runtime(Package)"
).replace(f'"{node_placeholder}"', f"{node_variable}")
for old, replacement in when_substitutions.items(): for old, replacement in when_substitutions.items():
body_str = body_str.replace(old, replacement) body_str = body_str.replace(old, replacement)
return body_str, node_variable return body_str, node_variable

View File

@ -81,19 +81,13 @@ def runtime_constraints(cls, *, spec, pkg):
spec: spec that will inject runtime dependencies spec: spec that will inject runtime dependencies
pkg: object used to forward information to the solver pkg: object used to forward information to the solver
""" """
pkg("*").depends_on( for language in ("c", "cxx", "fortran"):
"gcc-runtime", pkg("*").depends_on(
when="%gcc", f"gcc-runtime@{spec.version}:",
type="link", when=f"%[virtuals={language}] {spec.name}@{spec.versions}",
description="If any package uses %gcc, it depends on gcc-runtime", type="link",
) description=f"Inject gcc-runtime when gcc is used as a {language} compiler",
pkg("*").depends_on( )
f"gcc-runtime@{str(spec.version)}:",
when=f"^[deptypes=build] {spec.name}@{spec.versions}",
type="link",
description=f"If any package uses %{str(spec)}, "
f"it depends on gcc-runtime@{str(spec.version)}:",
)
gfortran_str = "libgfortran@5" gfortran_str = "libgfortran@5"
if spec.satisfies("gcc@:6"): if spec.satisfies("gcc@:6"):
@ -104,18 +98,14 @@ def runtime_constraints(cls, *, spec, pkg):
for fortran_virtual in ("fortran-rt", gfortran_str): for fortran_virtual in ("fortran-rt", gfortran_str):
pkg("*").depends_on( pkg("*").depends_on(
fortran_virtual, fortran_virtual,
when=f"^[virtuals=fortran deptypes=build] {spec.name}@{spec.versions}", when=f"%[virtuals=fortran] {spec.name}@{spec.versions}",
type="link", type="link",
description=f"Add a dependency on '{gfortran_str}' for nodes compiled with " description=f"Add a dependency on '{gfortran_str}' for nodes compiled with "
f"{str(spec)} and using the 'fortran' language", f"{spec} and using the 'fortran' language",
) )
# The version of gcc-runtime is the same as the %gcc used to "compile" it # The version of gcc-runtime is the same as the %gcc used to "compile" it
pkg("gcc-runtime").requires( pkg("gcc-runtime").requires(f"@{spec.versions}", when=f"%{spec.name}@{spec.versions}")
f"@{str(spec.versions)}", when=f"^[deptypes=build] {spec.name}@{spec.versions}"
)
# If a node used %gcc@X.Y its dependencies must use gcc-runtime@:X.Y # If a node used %gcc@X.Y its dependencies must use gcc-runtime@:X.Y
# (technically @:X is broader than ... <= @=X but this should work in practice) # (technically @:X is broader than ... <= @=X but this should work in practice)
pkg("*").propagate( pkg("*").propagate(f"gcc@:{spec.version}", when=f"%{spec.name}@{spec.versions}")
f"gcc@:{str(spec.version)}", when=f"^[deptypes=build] {spec.name}@{spec.versions}"
)

View File

@ -1158,19 +1158,13 @@ def runtime_constraints(cls, *, spec, pkg):
spec: spec that will inject runtime dependencies spec: spec that will inject runtime dependencies
pkg: object used to forward information to the solver pkg: object used to forward information to the solver
""" """
pkg("*").depends_on( for language in ("c", "cxx", "fortran"):
"gcc-runtime", pkg("*").depends_on(
when="%gcc", f"gcc-runtime@{spec.version}:",
type="link", when=f"%[virtuals={language}] {spec.name}@{spec.versions}",
description="If any package uses %gcc, it depends on gcc-runtime", type="link",
) description=f"Inject gcc-runtime when gcc is used as a {language} compiler",
pkg("*").depends_on( )
f"gcc-runtime@{str(spec.version)}:",
when=f"^[deptypes=build] {spec.name}@{spec.versions}",
type="link",
description=f"If any package uses %{str(spec)}, "
f"it depends on gcc-runtime@{str(spec.version)}:",
)
gfortran_str = "libgfortran@5" gfortran_str = "libgfortran@5"
if spec.satisfies("gcc@:6"): if spec.satisfies("gcc@:6"):
@ -1181,21 +1175,17 @@ def runtime_constraints(cls, *, spec, pkg):
for fortran_virtual in ("fortran-rt", gfortran_str): for fortran_virtual in ("fortran-rt", gfortran_str):
pkg("*").depends_on( pkg("*").depends_on(
fortran_virtual, fortran_virtual,
when=f"^[virtuals=fortran deptypes=build] {spec.name}@{spec.versions}", when=f"%[virtuals=fortran] {spec.name}@{spec.versions}",
type="link", type="link",
description=f"Add a dependency on '{gfortran_str}' for nodes compiled with " description=f"Add a dependency on '{gfortran_str}' for nodes compiled with "
f"{str(spec)} and using the 'fortran' language", f"{spec} and using the 'fortran' language",
) )
# The version of gcc-runtime is the same as the %gcc used to "compile" it # The version of gcc-runtime is the same as the %gcc used to "compile" it
pkg("gcc-runtime").requires( pkg("gcc-runtime").requires(f"@{spec.versions}", when=f"%{spec.name}@{spec.versions}")
f"@{str(spec.versions)}", when=f"^[deptypes=build] {spec.name}@{spec.versions}"
)
# If a node used %gcc@X.Y its dependencies must use gcc-runtime@:X.Y # If a node used %gcc@X.Y its dependencies must use gcc-runtime@:X.Y
# (technically @:X is broader than ... <= @=X but this should work in practice) # (technically @:X is broader than ... <= @=X but this should work in practice)
pkg("*").propagate( pkg("*").propagate(f"gcc@:{spec.version}", when=f"%{spec.name}@{spec.versions}")
f"gcc@:{str(spec.version)}", when=f"^[deptypes=build] {spec.name}@{spec.versions}"
)
def _post_buildcache_install_hook(self): def _post_buildcache_install_hook(self):
if not self.spec.satisfies("platform=linux"): if not self.spec.satisfies("platform=linux"):

View File

@ -655,38 +655,32 @@ def determine_variants(cls, exes, version_str):
@classmethod @classmethod
def runtime_constraints(cls, *, spec, pkg): def runtime_constraints(cls, *, spec, pkg):
pkg("*").depends_on( for language in ("c", "cxx", "fortran"):
"intel-oneapi-runtime", pkg("*").depends_on(
when="%oneapi", f"intel-oneapi-runtime@{spec.version}:",
type="link", when=f"%[virtuals={language}] {spec.name}@{spec.versions}",
description="If any package uses %oneapi, it depends on intel-oneapi-runtime", type="link",
) description="Inject intel-oneapi-runtime when oneapi is used as "
pkg("*").depends_on( f"a {language} compiler",
f"intel-oneapi-runtime@{str(spec.version)}:", )
when=f"^[deptypes=build] {spec.name}@{spec.versions}",
type="link",
description=f"If any package uses %{str(spec)}, "
f"it depends on intel-oneapi-runtime@{str(spec.version)}:",
)
for fortran_virtual in ("fortran-rt", "libifcore@5"): for fortran_virtual in ("fortran-rt", "libifcore@5"):
pkg("*").depends_on( pkg("*").depends_on(
fortran_virtual, fortran_virtual,
when=f"^[virtuals=fortran deptypes=build] {spec.name}@{spec.versions}", when=f"%[virtuals=fortran] {spec.name}@{spec.versions}",
type="link", type="link",
description=f"Add a dependency on 'libifcore' for nodes compiled with " description="Add a dependency on 'libifcore' for nodes compiled with "
f"{str(spec)} and using the 'fortran' language", f"{spec.name}@{spec.versions} and using the 'fortran' language",
) )
# The version of intel-oneapi-runtime is the same as the %oneapi used to "compile" it # The version of intel-oneapi-runtime is the same as the %oneapi used to "compile" it
pkg("intel-oneapi-runtime").requires( pkg("intel-oneapi-runtime").requires(
f"@{str(spec.versions)}", when=f"^[deptypes=build] {spec.name}@{spec.versions}" f"@{spec.versions}", when=f"%{spec.name}@{spec.versions}"
) )
# If a node used %intel-oneapi=runtime@X.Y its dependencies must use @:X.Y # If a node used %intel-oneapi-runtime@X.Y its dependencies must use @:X.Y
# (technically @:X is broader than ... <= @=X but this should work in practice) # (technically @:X is broader than ... <= @=X but this should work in practice)
pkg("*").propagate( pkg("*").propagate(
f"intel-oneapi-compilers@:{str(spec.version)}", f"intel-oneapi-compilers@:{spec.version}", when=f"%{spec.name}@{spec.versions}"
when=f"^[deptypes=build] {spec.name}@{spec.versions}",
) )
def _cc_path(self): def _cc_path(self):