summaryrefslogtreecommitdiff
path: root/var
diff options
context:
space:
mode:
authorMassimiliano Culpo <massimiliano.culpo@gmail.com>2024-03-25 06:59:21 +0100
committerGitHub <noreply@github.com>2024-03-24 22:59:21 -0700
commit0c9a53ba3a9c94ee60fa3825160e72ff025fb8b7 (patch)
tree62162f8c017b87af8b55d0829cd663824b15df87 /var
parent1fd4353289ec58913a4faf70e4851f66767bb623 (diff)
downloadspack-0c9a53ba3a9c94ee60fa3825160e72ff025fb8b7.tar.gz
spack-0c9a53ba3a9c94ee60fa3825160e72ff025fb8b7.tar.bz2
spack-0c9a53ba3a9c94ee60fa3825160e72ff025fb8b7.tar.xz
spack-0c9a53ba3a9c94ee60fa3825160e72ff025fb8b7.zip
Add `intel-oneapi-runtime`, allow injecting virtual dependencies (#42062)
This PR adds: - A new runtime for `%oneapi` compilers, called `intel-oneapi-runtime` - Information to both `gcc-runtime` and `intel-oneapi-runtime`, to ensure that we don't mix compilers using different soname for either `libgfortran` or `libifcore` To do so, the following internal mechanisms have been implemented: - Possibility to inject virtual dependencies from the `runtime_constraints` callback on packages Information has been added to `gcc-runtime` to provide the correct soname under different conditions on its `%gcc`. Rules injected into the solver looks like: ```prolog % Add a dependency on 'gfortran@5' for nodes compiled with gcc@=13.2.0 and using the 'fortran' language attr("dependency_holds", node(ID, Package), "gfortran", "link") :- attr("node", node(ID, Package)), attr("node_compiler", node(ID, Package), "gcc"), attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"), not external(node(ID, Package)), not runtime(Package), attr("language", node(ID, Package), "fortran"). attr("virtual_node", node(RuntimeID, "gfortran")) :- attr("depends_on", node(ID, Package), ProviderNode, "link"), provider(ProviderNode, node(RuntimeID, "gfortran")), attr("node", node(ID, Package)), attr("node_compiler", node(ID, Package), "gcc"), attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"), not external(node(ID, Package)), not runtime(Package), attr("language", node(ID, Package), "fortran"). attr("node_version_satisfies", node(RuntimeID, "gfortran"), "5") :- attr("depends_on", node(ID, Package), ProviderNode, "link"), provider(ProviderNode, node(RuntimeID, "gfortran")), attr("node", node(ID, Package)), attr("node_compiler", node(ID, Package), "gcc"), attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"), not external(node(ID, Package)), not runtime(Package), attr("language", node(ID, Package), "fortran"). ```
Diffstat (limited to 'var')
-rw-r--r--var/spack/repos/builtin/packages/gcc-runtime/package.py94
-rw-r--r--var/spack/repos/builtin/packages/gcc/package.py30
-rw-r--r--var/spack/repos/builtin/packages/hdf5/package.py3
-rw-r--r--var/spack/repos/builtin/packages/intel-oneapi-compilers/package.py28
-rw-r--r--var/spack/repos/builtin/packages/intel-oneapi-runtime/package.py61
-rw-r--r--var/spack/repos/compiler_runtime.test/packages/gcc/package.py12
6 files changed, 173 insertions, 55 deletions
diff --git a/var/spack/repos/builtin/packages/gcc-runtime/package.py b/var/spack/repos/builtin/packages/gcc-runtime/package.py
index 085042b8ce..d883085cc9 100644
--- a/var/spack/repos/builtin/packages/gcc-runtime/package.py
+++ b/var/spack/repos/builtin/packages/gcc-runtime/package.py
@@ -22,6 +22,9 @@ class GccRuntime(Package):
tags = ["runtime"]
+ # gcc-runtime versions are declared dynamically
+ skip_version_audit = ["platform=linux", "platform=darwin"]
+
maintainers("haampie")
license("GPL-3.0-or-later WITH GCC-exception-3.1")
@@ -44,9 +47,15 @@ class GccRuntime(Package):
"ubsan",
]
+ # libgfortran ABI
+ provides("fortran-rt", "libgfortran")
+ provides("libgfortran@3", when="%gcc@:6")
+ provides("libgfortran@4", when="%gcc@7")
+ provides("libgfortran@5", when="%gcc@8:")
+
def install(self, spec, prefix):
if spec.platform in ["linux", "cray", "freebsd"]:
- libraries = self._get_libraries_elf()
+ libraries = get_elf_libraries(compiler=self.compiler, libraries=self.LIBRARIES)
elif spec.platform == "darwin":
libraries = self._get_libraries_macho()
else:
@@ -61,47 +70,6 @@ class GccRuntime(Package):
for path, name in libraries:
install(path, os.path.join(prefix.lib, name))
- def _get_libraries_elf(self):
- """Get the GCC runtime libraries for ELF binaries"""
- cc = Executable(self.compiler.cc)
- lib_regex = re.compile(rb"\blib[a-z-_]+\.so\.\d+\b")
- path_and_install_name = []
-
- for name in self.LIBRARIES:
- # Look for the dynamic library that gcc would use to link,
- # that is with .so extension and without abi suffix.
- path = cc(f"-print-file-name=lib{name}.so", output=str).strip()
-
- # gcc reports an absolute path on success
- if not os.path.isabs(path):
- continue
-
- # Now there are two options:
- # 1. the file is an ELF file
- # 2. the file is a linker script referencing the actual library
- with open(path, "rb") as f:
- try:
- # Try to parse as an ELF file
- soname = parse_elf(f, dynamic_section=True).dt_soname_str.decode("utf-8")
- except Exception:
- # On failure try to "parse" as ld script; the actual
- # library needs to be mentioned by filename.
- f.seek(0)
- script_matches = lib_regex.findall(f.read())
- if len(script_matches) != 1:
- continue
- soname = script_matches[0].decode("utf-8")
-
- # Now locate and install the runtime library
- runtime_path = cc(f"-print-file-name={soname}", output=str).strip()
-
- if not os.path.isabs(runtime_path):
- continue
-
- path_and_install_name.append((runtime_path, soname))
-
- return path_and_install_name
-
def _get_libraries_macho(self):
"""Same as _get_libraries_elf but for Mach-O binaries"""
cc = Executable(self.compiler.cc)
@@ -152,3 +120,45 @@ class GccRuntime(Package):
@property
def headers(self):
return HeaderList([])
+
+
+def get_elf_libraries(compiler, libraries):
+ """Get the GCC runtime libraries for ELF binaries"""
+ cc = Executable(compiler.cc)
+ lib_regex = re.compile(rb"\blib[a-z-_]+\.so\.\d+\b")
+ path_and_install_name = []
+
+ for name in libraries:
+ # Look for the dynamic library that gcc would use to link,
+ # that is with .so extension and without abi suffix.
+ path = cc(f"-print-file-name=lib{name}.so", output=str).strip()
+
+ # gcc reports an absolute path on success
+ if not os.path.isabs(path):
+ continue
+
+ # Now there are two options:
+ # 1. the file is an ELF file
+ # 2. the file is a linker script referencing the actual library
+ with open(path, "rb") as f:
+ try:
+ # Try to parse as an ELF file
+ soname = parse_elf(f, dynamic_section=True).dt_soname_str.decode("utf-8")
+ except Exception:
+ # On failure try to "parse" as ld script; the actual
+ # library needs to be mentioned by filename.
+ f.seek(0)
+ script_matches = lib_regex.findall(f.read())
+ if len(script_matches) != 1:
+ continue
+ soname = script_matches[0].decode("utf-8")
+
+ # Now locate and install the runtime library
+ runtime_path = cc(f"-print-file-name={soname}", output=str).strip()
+
+ if not os.path.isabs(runtime_path):
+ continue
+
+ path_and_install_name.append((runtime_path, soname))
+
+ return path_and_install_name
diff --git a/var/spack/repos/builtin/packages/gcc/package.py b/var/spack/repos/builtin/packages/gcc/package.py
index ea11cba15a..f9ff5b9fbe 100644
--- a/var/spack/repos/builtin/packages/gcc/package.py
+++ b/var/spack/repos/builtin/packages/gcc/package.py
@@ -1144,7 +1144,7 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage):
)
@classmethod
- def runtime_constraints(cls, *, compiler, pkg):
+ def runtime_constraints(cls, *, spec, pkg):
"""Callback function to inject runtime-related rules into the solver.
Rule-injection is obtained through method calls of the ``pkg`` argument.
@@ -1153,7 +1153,7 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage):
we'll document the behavior at https://spack.readthedocs.io/en/latest/
Args:
- compiler: compiler object (node attribute) currently considered
+ spec: spec that will inject runtime dependencies
pkg: object used to forward information to the solver
"""
pkg("*").depends_on(
@@ -1163,11 +1163,27 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage):
description="If any package uses %gcc, it depends on gcc-runtime",
)
pkg("*").depends_on(
- f"gcc-runtime@{str(compiler.version)}:",
- when=f"%{str(compiler.spec)}",
+ f"gcc-runtime@{str(spec.version)}:",
+ when=f"%{str(spec)}",
type="link",
- description=f"If any package uses %{str(compiler.spec)}, "
- f"it depends on gcc-runtime@{str(compiler.version)}:",
+ description=f"If any package uses %{str(spec)}, "
+ f"it depends on gcc-runtime@{str(spec.version)}:",
)
+
+ gfortran_str = "libgfortran@5"
+ if spec.satisfies("gcc@:6"):
+ gfortran_str = "libgfortran@3"
+ elif spec.satisfies("gcc@7"):
+ gfortran_str = "libgfortran@4"
+
+ for fortran_virtual in ("fortran-rt", gfortran_str):
+ pkg("*").depends_on(
+ fortran_virtual,
+ when=f"%{str(spec)}",
+ languages=["fortran"],
+ type="link",
+ description=f"Add a dependency on '{gfortran_str}' for nodes compiled with "
+ f"{str(spec)} and using the 'fortran' language",
+ )
# The version of gcc-runtime is the same as the %gcc used to "compile" it
- pkg("gcc-runtime").requires(f"@={str(compiler.version)}", when=f"%{str(compiler.spec)}")
+ pkg("gcc-runtime").requires(f"@={str(spec.version)}", when=f"%{str(spec)}")
diff --git a/var/spack/repos/builtin/packages/hdf5/package.py b/var/spack/repos/builtin/packages/hdf5/package.py
index a7f267d810..87a53995e4 100644
--- a/var/spack/repos/builtin/packages/hdf5/package.py
+++ b/var/spack/repos/builtin/packages/hdf5/package.py
@@ -33,6 +33,9 @@ class Hdf5(CMakePackage):
license("custom")
+ depends_on("cxx", type="build", when="+cxx")
+ depends_on("fortran", type="build", when="+fortran")
+
# The 'develop' version is renamed so that we could uninstall (or patch) it
# without affecting other develop version.
version("develop-1.15", branch="develop")
diff --git a/var/spack/repos/builtin/packages/intel-oneapi-compilers/package.py b/var/spack/repos/builtin/packages/intel-oneapi-compilers/package.py
index 44a52b8d28..ffa6e6bb08 100644
--- a/var/spack/repos/builtin/packages/intel-oneapi-compilers/package.py
+++ b/var/spack/repos/builtin/packages/intel-oneapi-compilers/package.py
@@ -388,3 +388,31 @@ class IntelOneapiCompilers(IntelOneApiPackage):
p = join_path(self.component_prefix.linux, d)
if find(p, "*." + dso_suffix, recursive=False):
yield p
+
+ @classmethod
+ def runtime_constraints(cls, *, spec, pkg):
+ pkg("*").depends_on(
+ "intel-oneapi-runtime",
+ when="%oneapi",
+ type="link",
+ description="If any package uses %oneapi, it depends on intel-oneapi-runtime",
+ )
+ pkg("*").depends_on(
+ f"intel-oneapi-runtime@{str(spec.version)}:",
+ when=f"%{str(spec)}",
+ 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"):
+ pkg("*").depends_on(
+ fortran_virtual,
+ when=f"%{str(spec)}",
+ languages=["fortran"],
+ type="link",
+ description=f"Add a dependency on 'libifcore' for nodes compiled with "
+ f"{str(spec)} and using the 'fortran' language",
+ )
+ # The version of gcc-runtime is the same as the %gcc used to "compile" it
+ pkg("intel-oneapi-runtime").requires(f"@={str(spec.version)}", when=f"%{str(spec)}")
diff --git a/var/spack/repos/builtin/packages/intel-oneapi-runtime/package.py b/var/spack/repos/builtin/packages/intel-oneapi-runtime/package.py
new file mode 100644
index 0000000000..575d51444e
--- /dev/null
+++ b/var/spack/repos/builtin/packages/intel-oneapi-runtime/package.py
@@ -0,0 +1,61 @@
+# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os
+
+from llnl.util import tty
+
+from spack.package import *
+from spack.pkg.builtin.gcc_runtime import get_elf_libraries
+
+
+class IntelOneapiRuntime(Package):
+ """Package for OneAPI compiler runtime libraries"""
+
+ homepage = "https://software.intel.com/content/www/us/en/develop/tools/oneapi.html"
+ has_code = False
+
+ tags = ["runtime"]
+
+ requires("%oneapi")
+
+ depends_on("gcc-runtime", type="link")
+
+ LIBRARIES = [
+ "imf",
+ "intlc",
+ "irng",
+ "svml",
+ "ifcore", # Fortran
+ "ifcoremt", # Fortran
+ "ifport", # Fortran
+ "iomp5",
+ "sycl",
+ ]
+
+ # libifcore ABI
+ provides("fortran-rt", "libifcore@5", when="%oneapi@2021:")
+ provides("sycl")
+
+ conflicts("platform=windows", msg="IntelOneAPI can only be installed on Linux, and FreeBSD")
+ conflicts("platform=darwin", msg="IntelOneAPI can only be installed on Linux, and FreeBSD")
+
+ def install(self, spec, prefix):
+ libraries = get_elf_libraries(compiler=self.compiler, libraries=self.LIBRARIES)
+ mkdir(prefix.lib)
+
+ if not libraries:
+ tty.warn("Could not detect any shared OneAPI runtime libraries")
+ return
+
+ for path, name in libraries:
+ install(path, os.path.join(prefix.lib, name))
+
+ @property
+ def libs(self):
+ return LibraryList([])
+
+ @property
+ def headers(self):
+ return HeaderList([])
diff --git a/var/spack/repos/compiler_runtime.test/packages/gcc/package.py b/var/spack/repos/compiler_runtime.test/packages/gcc/package.py
index c70ff4faff..ef28e411d3 100644
--- a/var/spack/repos/compiler_runtime.test/packages/gcc/package.py
+++ b/var/spack/repos/compiler_runtime.test/packages/gcc/package.py
@@ -14,7 +14,7 @@ class Gcc(Package):
version("12.3.0")
@classmethod
- def runtime_constraints(cls, *, compiler, pkg):
+ def runtime_constraints(cls, *, spec, pkg):
pkg("*").depends_on(
"gcc-runtime",
when="%gcc",
@@ -22,11 +22,11 @@ class Gcc(Package):
description="If any package uses %gcc, it depends on gcc-runtime",
)
pkg("*").depends_on(
- f"gcc-runtime@{str(compiler.version)}:",
- when=f"%{str(compiler.spec)}",
+ f"gcc-runtime@{str(spec.version)}:",
+ when=f"%{str(spec)}",
type="link",
- description=f"If any package uses %{str(compiler.spec)}, "
- f"it depends on gcc-runtime@{str(compiler.version)}:",
+ description=f"If any package uses %{str(spec)}, "
+ f"it depends on gcc-runtime@{str(spec.version)}:",
)
# The version of gcc-runtime is the same as the %gcc used to "compile" it
- pkg("gcc-runtime").requires(f"@={str(compiler.version)}", when=f"%{str(compiler.spec)}")
+ pkg("gcc-runtime").requires(f"@={str(spec.version)}", when=f"%{str(spec)}")