gcc-runtime: add separate package for gcc runtime libs
The gcc-runtime package adds a separate node for gcc's dynamic runtime libraries. This should help with: 1. binary caches where rpaths for compiler support libs cannot be relocated because the compiler is missing on the target system 2. creating "minimal" container images The package is versioned like `gcc` (in principle it could be unversioned, but Spack doesn't always guarantee not mixing compilers)
This commit is contained in:
parent
0a5f2fc94d
commit
8371bb4e19
6 changed files with 250 additions and 1 deletions
|
@ -36,6 +36,7 @@
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.modules
|
import spack.modules
|
||||||
|
import spack.package_base
|
||||||
import spack.paths
|
import spack.paths
|
||||||
import spack.platforms
|
import spack.platforms
|
||||||
import spack.repo
|
import spack.repo
|
||||||
|
@ -607,6 +608,7 @@ def setup_main_options(args):
|
||||||
[(key, [spack.paths.mock_packages_path])]
|
[(key, [spack.paths.mock_packages_path])]
|
||||||
)
|
)
|
||||||
spack.repo.PATH = spack.repo.create(spack.config.CONFIG)
|
spack.repo.PATH = spack.repo.create(spack.config.CONFIG)
|
||||||
|
spack.package_base.WITH_GCC_RUNTIME = False
|
||||||
|
|
||||||
# If the user asked for it, don't check ssl certs.
|
# If the user asked for it, don't check ssl certs.
|
||||||
if args.insecure:
|
if args.insecure:
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
import spack.util.environment
|
import spack.util.environment
|
||||||
import spack.util.path
|
import spack.util.path
|
||||||
import spack.util.web
|
import spack.util.web
|
||||||
|
from spack.directives import _depends_on
|
||||||
from spack.filesystem_view import YamlFilesystemView
|
from spack.filesystem_view import YamlFilesystemView
|
||||||
from spack.install_test import (
|
from spack.install_test import (
|
||||||
PackageTest,
|
PackageTest,
|
||||||
|
@ -76,6 +77,7 @@
|
||||||
"""Allowed URL schemes for spack packages."""
|
"""Allowed URL schemes for spack packages."""
|
||||||
_ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file", "git"]
|
_ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file", "git"]
|
||||||
|
|
||||||
|
WITH_GCC_RUNTIME = True
|
||||||
|
|
||||||
#: Filename for the Spack build/install log.
|
#: Filename for the Spack build/install log.
|
||||||
_spack_build_logfile = "spack-build-out.txt"
|
_spack_build_logfile = "spack-build-out.txt"
|
||||||
|
@ -371,6 +373,20 @@ def _wrapper(instance, *args, **kwargs):
|
||||||
return _execute_under_condition
|
return _execute_under_condition
|
||||||
|
|
||||||
|
|
||||||
|
class BinaryPackage:
|
||||||
|
"""This adds a universal dependency on gcc-runtime."""
|
||||||
|
|
||||||
|
def maybe_depend_on_gcc_runtime(self):
|
||||||
|
# Do not depend on itself, and allow tests to disable this universal dep
|
||||||
|
if self.name == "gcc-runtime" or not WITH_GCC_RUNTIME:
|
||||||
|
return
|
||||||
|
for v in ["13", "12", "11", "10", "9", "8", "7", "6", "5", "4"]:
|
||||||
|
_depends_on(self, f"gcc-runtime@{v}:", type="link", when=f"%gcc@{v} platform=linux")
|
||||||
|
_depends_on(self, f"gcc-runtime@{v}:", type="link", when=f"%gcc@{v} platform=cray")
|
||||||
|
|
||||||
|
_directives_to_be_executed = [maybe_depend_on_gcc_runtime]
|
||||||
|
|
||||||
|
|
||||||
class PackageViewMixin:
|
class PackageViewMixin:
|
||||||
"""This collects all functionality related to adding installed Spack
|
"""This collects all functionality related to adding installed Spack
|
||||||
package to views. Packages can customize how they are added to views by
|
package to views. Packages can customize how they are added to views by
|
||||||
|
@ -433,7 +449,7 @@ def remove_files_from_view(self, view, merge_map):
|
||||||
Pb = TypeVar("Pb", bound="PackageBase")
|
Pb = TypeVar("Pb", bound="PackageBase")
|
||||||
|
|
||||||
|
|
||||||
class PackageBase(WindowsRPath, PackageViewMixin, metaclass=PackageMeta):
|
class PackageBase(WindowsRPath, PackageViewMixin, BinaryPackage, metaclass=PackageMeta):
|
||||||
"""This is the superclass for all spack packages.
|
"""This is the superclass for all spack packages.
|
||||||
|
|
||||||
***The Package class***
|
***The Package class***
|
||||||
|
|
|
@ -57,6 +57,11 @@
|
||||||
from spack.util.pattern import Bunch
|
from spack.util.pattern import Bunch
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
|
def drop_gcc_runtime():
|
||||||
|
spack.package_base.WITH_GCC_RUNTIME = False
|
||||||
|
|
||||||
|
|
||||||
def ensure_configuration_fixture_run_before(request):
|
def ensure_configuration_fixture_run_before(request):
|
||||||
"""Ensure that fixture mutating the configuration run before the one where
|
"""Ensure that fixture mutating the configuration run before the one where
|
||||||
the function is called.
|
the function is called.
|
||||||
|
|
|
@ -33,6 +33,8 @@ spack:
|
||||||
elfutils:
|
elfutils:
|
||||||
variants: +bzip2 ~nls +xz
|
variants: +bzip2 ~nls +xz
|
||||||
require: "%gcc"
|
require: "%gcc"
|
||||||
|
gcc-runtime:
|
||||||
|
require: "%gcc"
|
||||||
hdf5:
|
hdf5:
|
||||||
variants: +fortran +hl +shared
|
variants: +fortran +hl +shared
|
||||||
libfabric:
|
libfabric:
|
||||||
|
|
|
@ -17,6 +17,8 @@ spack:
|
||||||
variants: +mpi
|
variants: +mpi
|
||||||
elfutils:
|
elfutils:
|
||||||
variants: +bzip2 ~nls +xz
|
variants: +bzip2 ~nls +xz
|
||||||
|
gcc-runtime:
|
||||||
|
require: "%gcc"
|
||||||
hdf5:
|
hdf5:
|
||||||
require: "%gcc"
|
require: "%gcc"
|
||||||
variants: +fortran +hl +shared
|
variants: +fortran +hl +shared
|
||||||
|
|
222
var/spack/repos/builtin/packages/gcc-runtime/package.py
Normal file
222
var/spack/repos/builtin/packages/gcc-runtime/package.py
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
# Copyright 2013-2023 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
|
||||||
|
import re
|
||||||
|
|
||||||
|
from macholib import MachO, mach_o
|
||||||
|
|
||||||
|
from llnl.util import tty
|
||||||
|
|
||||||
|
from spack.package import *
|
||||||
|
from spack.util.elf import parse_elf
|
||||||
|
|
||||||
|
|
||||||
|
class GccRuntime(Package):
|
||||||
|
"""Package for GCC compiler runtime libraries"""
|
||||||
|
|
||||||
|
homepage = "https://gcc.gnu.org"
|
||||||
|
has_code = False
|
||||||
|
|
||||||
|
maintainers("haampie")
|
||||||
|
|
||||||
|
license("GPL-3.0-or-later WITH GCC-exception-3.1")
|
||||||
|
|
||||||
|
requires("%gcc")
|
||||||
|
|
||||||
|
LIBRARIES = [
|
||||||
|
"asan",
|
||||||
|
"atomic",
|
||||||
|
"gcc_s",
|
||||||
|
"gfortran",
|
||||||
|
"gomp",
|
||||||
|
"hwasan",
|
||||||
|
"itm",
|
||||||
|
"lsan",
|
||||||
|
"quadmath",
|
||||||
|
"ssp",
|
||||||
|
"stdc++",
|
||||||
|
"tsan",
|
||||||
|
"ubsan",
|
||||||
|
]
|
||||||
|
|
||||||
|
for v in [
|
||||||
|
"13.2",
|
||||||
|
"13.1",
|
||||||
|
"12.3",
|
||||||
|
"12.2",
|
||||||
|
"12.1",
|
||||||
|
"11.4",
|
||||||
|
"11.3",
|
||||||
|
"11.2",
|
||||||
|
"11.1",
|
||||||
|
"10.5",
|
||||||
|
"10.4",
|
||||||
|
"10.3",
|
||||||
|
"10.2",
|
||||||
|
"10.1",
|
||||||
|
"9.5",
|
||||||
|
"9.4",
|
||||||
|
"9.3",
|
||||||
|
"9.2",
|
||||||
|
"9.1",
|
||||||
|
"8.5",
|
||||||
|
"8.4",
|
||||||
|
"8.3",
|
||||||
|
"8.2",
|
||||||
|
"8.1",
|
||||||
|
"7.5",
|
||||||
|
"7.4",
|
||||||
|
"7.3",
|
||||||
|
"7.2",
|
||||||
|
"7.1",
|
||||||
|
"6.5",
|
||||||
|
"6.4",
|
||||||
|
"6.3",
|
||||||
|
"6.2",
|
||||||
|
"6.1",
|
||||||
|
"5.5",
|
||||||
|
"5.4",
|
||||||
|
"5.3",
|
||||||
|
"5.2",
|
||||||
|
"5.1",
|
||||||
|
"4.9.4",
|
||||||
|
"4.9.3",
|
||||||
|
"4.9.2",
|
||||||
|
"4.9.1",
|
||||||
|
"4.9.0",
|
||||||
|
"4.8.5",
|
||||||
|
"4.8.4",
|
||||||
|
"4.8.3",
|
||||||
|
"4.8.2",
|
||||||
|
"4.8.1",
|
||||||
|
"4.8.0",
|
||||||
|
"4.7.4",
|
||||||
|
"4.7.3",
|
||||||
|
"4.7.2",
|
||||||
|
"4.7.1",
|
||||||
|
"4.7.0",
|
||||||
|
"4.6.4",
|
||||||
|
"4.6.3",
|
||||||
|
"4.6.2",
|
||||||
|
"4.6.1",
|
||||||
|
"4.6.0",
|
||||||
|
"4.5.4",
|
||||||
|
"4.5.3",
|
||||||
|
"4.5.2",
|
||||||
|
"4.5.1",
|
||||||
|
"4.5.0",
|
||||||
|
]:
|
||||||
|
version(v)
|
||||||
|
requires(f"%gcc@{v}", when=f"@{v}")
|
||||||
|
|
||||||
|
def install(self, spec, prefix):
|
||||||
|
if spec.platform in ["linux", "cray", "freebsd"]:
|
||||||
|
libraries = self._get_libraries_elf()
|
||||||
|
elif spec.platform == "darwin":
|
||||||
|
libraries = self._get_libraries_macho()
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Unsupported platform")
|
||||||
|
|
||||||
|
mkdir(prefix.lib)
|
||||||
|
|
||||||
|
if not libraries:
|
||||||
|
tty.warn("Could not detect any shared GCC runtime libraries")
|
||||||
|
return
|
||||||
|
|
||||||
|
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)
|
||||||
|
path_and_install_name = []
|
||||||
|
|
||||||
|
for name in self.LIBRARIES:
|
||||||
|
if name == "gcc_s":
|
||||||
|
# On darwin, libgcc_s is versioned and can't be linked as -lgcc_s,
|
||||||
|
# but needs a suffix we don't know, so we parse it from the link line.
|
||||||
|
match = re.search(
|
||||||
|
r"\s-l(gcc_s\.[0-9.]+)\s", cc("-xc", "-", "-shared-libgcc", "-###", error=str)
|
||||||
|
)
|
||||||
|
if match is None:
|
||||||
|
continue
|
||||||
|
name = match.group(1)
|
||||||
|
|
||||||
|
path = cc(f"-print-file-name=lib{name}.dylib", output=str).strip()
|
||||||
|
|
||||||
|
if not os.path.isabs(path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
macho = MachO.MachO(path)
|
||||||
|
|
||||||
|
# Get the LC_ID_DYLIB load command
|
||||||
|
for load_command, _, data in macho.headers[-1].commands:
|
||||||
|
if load_command.cmd == mach_o.LC_ID_DYLIB:
|
||||||
|
# Strip off @rpath/ prefix, or even an absolute path.
|
||||||
|
dylib_name = os.path.basename(data.rstrip(b"\x00").decode())
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Locate by dylib name
|
||||||
|
runtime_path = cc(f"-print-file-name={dylib_name}", output=str).strip()
|
||||||
|
|
||||||
|
if not os.path.isabs(runtime_path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
path_and_install_name.append((runtime_path, dylib_name))
|
||||||
|
|
||||||
|
return path_and_install_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def libs(self):
|
||||||
|
# Currently these libs are not linkable with -l, they all have a suffix.
|
||||||
|
return LibraryList([])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
return HeaderList([])
|
Loading…
Reference in a new issue