Compiler.default_libc

Some logic to detect what libc the c / cxx compilers use by default,
based on `-dynamic-linker`.

The function `compiler.default_libc()` returns a `Spec` of the form
`glibc@x.y` or `musl@x.y` with the `external_path` property set.

The idea is this can be injected as a dependency.

If we can't run the dynamic linker directly, fall back to `ldd` relative
to the prefix computed from `ld.so.`
This commit is contained in:
Harmen Stoppels 2024-03-13 17:03:10 +01:00 committed by Harmen Stoppels
parent e8c41cdbcb
commit 209a3bf302
11 changed files with 212 additions and 110 deletions

View file

@ -35,6 +35,7 @@ packages:
java: [openjdk, jdk, ibm-java] java: [openjdk, jdk, ibm-java]
jpeg: [libjpeg-turbo, libjpeg] jpeg: [libjpeg-turbo, libjpeg]
lapack: [openblas, amdlibflame] lapack: [openblas, amdlibflame]
libc: [glibc, musl]
libgfortran: [ gcc-runtime ] libgfortran: [ gcc-runtime ]
libglx: [mesa+glx, mesa18+glx] libglx: [mesa+glx, mesa18+glx]
libifcore: [ intel-oneapi-runtime ] libifcore: [ intel-oneapi-runtime ]

View file

@ -8,9 +8,11 @@
import os import os
import platform import platform
import re import re
import shlex
import shutil import shutil
import sys import sys
import tempfile import tempfile
from subprocess import PIPE, run
from typing import List, Optional, Sequence from typing import List, Optional, Sequence
import llnl.path import llnl.path
@ -184,6 +186,113 @@ def _parse_non_system_link_dirs(string: str) -> List[str]:
return list(p for p in link_dirs if not in_system_subdirectory(p)) return list(p for p in link_dirs if not in_system_subdirectory(p))
def _parse_dynamic_linker(output: str):
"""Parse -dynamic-linker /path/to/ld.so from compiler output"""
for line in reversed(output.splitlines()):
if "-dynamic-linker" not in line:
continue
args = shlex.split(line)
for idx in reversed(range(1, len(args))):
arg = args[idx]
if arg == "-dynamic-linker" or args == "--dynamic-linker":
return args[idx + 1]
elif arg.startswith("--dynamic-linker=") or arg.startswith("-dynamic-linker="):
return arg.split("=", 1)[1]
def _libc_from_ldd(ldd: str) -> Optional["spack.spec.Spec"]:
try:
result = run([ldd, "--version"], stdout=PIPE, stderr=PIPE, check=False)
stdout = result.stdout.decode("utf-8")
except Exception:
return None
if not re.search("gnu|glibc", stdout, re.IGNORECASE):
return None
version_str = re.match(r".+\(.+\) (.+)", stdout)
if not version_str:
return None
try:
return spack.spec.Spec(f"glibc@={version_str.group(1)}")
except Exception:
return None
def _libc_from_dynamic_linker(dynamic_linker: str) -> Optional["spack.spec.Spec"]:
if not os.path.exists(dynamic_linker):
return None
# The dynamic linker is usually installed in the same /lib(64)?/ld-*.so path across all
# distros. The rest of libc is elsewhere, e.g. /usr. Typically the dynamic linker is then
# a symlink into /usr/lib, which we use to for determining the actual install prefix of
# libc.
realpath = os.path.realpath(dynamic_linker)
prefix = os.path.dirname(realpath)
# Remove the multiarch suffix if it exists
if os.path.basename(prefix) not in ("lib", "lib64"):
prefix = os.path.dirname(prefix)
# Non-standard install layout -- just bail.
if os.path.basename(prefix) not in ("lib", "lib64"):
return None
prefix = os.path.dirname(prefix)
# Now try to figure out if glibc or musl, which is the only ones we support.
# In recent glibc we can simply execute the dynamic loader. In musl that's always the case.
try:
result = run([dynamic_linker, "--version"], stdout=PIPE, stderr=PIPE, check=False)
stdout = result.stdout.decode("utf-8")
stderr = result.stderr.decode("utf-8")
except Exception:
return None
# musl prints to stderr
if stderr.startswith("musl libc"):
version_str = re.search(r"^Version (.+)$", stderr, re.MULTILINE)
if not version_str:
return None
try:
spec = spack.spec.Spec(f"musl@={version_str.group(1)}")
spec.external_path = prefix
return spec
except Exception:
return None
elif re.search("gnu|glibc", stdout, re.IGNORECASE):
# output is like "ld.so (...) stable release version 2.33." write a regex for it
match = re.search(r"version (\d+\.\d+(?:\.\d+)?)", stdout)
if not match:
return None
try:
version = match.group(1)
spec = spack.spec.Spec(f"glibc@={version}")
spec.external_path = prefix
return spec
except Exception:
return None
else:
# Could not get the version by running the dynamic linker directly. Instead locate `ldd`
# relative to the dynamic linker.
ldd = os.path.join(prefix, "bin", "ldd")
if not os.path.exists(ldd):
# If `/lib64/ld.so` was not a symlink to `/usr/lib/ld.so` we can try to use /usr as
# prefix. This is the case on ubuntu 18.04 where /lib != /usr/lib.
if prefix != "/":
return None
prefix = "/usr"
ldd = os.path.join(prefix, "bin", "ldd")
if not os.path.exists(ldd):
return None
maybe_spec = _libc_from_ldd(ldd)
if not maybe_spec:
return None
maybe_spec.external_path = prefix
return maybe_spec
def in_system_subdirectory(path): def in_system_subdirectory(path):
system_dirs = [ system_dirs = [
"/lib/", "/lib/",
@ -417,17 +526,33 @@ def real_version(self):
self._real_version = self.version self._real_version = self.version
return self._real_version return self._real_version
def implicit_rpaths(self): def implicit_rpaths(self) -> List[str]:
if self.enable_implicit_rpaths is False: if self.enable_implicit_rpaths is False:
return [] return []
# Put CXX first since it has the most linking issues output = self.compiler_verbose_output
# And because it has flags that affect linking
link_dirs = self._get_compiler_link_paths() if not output:
return []
link_dirs = _parse_non_system_link_dirs(output)
all_required_libs = list(self.required_libs) + Compiler._all_compiler_rpath_libraries all_required_libs = list(self.required_libs) + Compiler._all_compiler_rpath_libraries
return list(paths_containing_libs(link_dirs, all_required_libs)) return list(paths_containing_libs(link_dirs, all_required_libs))
def default_libc(self) -> Optional["spack.spec.Spec"]:
output = self.compiler_verbose_output
if not output:
return None
dynamic_linker = _parse_dynamic_linker(output)
if not dynamic_linker:
return None
return _libc_from_dynamic_linker(dynamic_linker)
@property @property
def required_libs(self): def required_libs(self):
"""For executables created with this compiler, the compiler libraries """For executables created with this compiler, the compiler libraries
@ -436,17 +561,17 @@ def required_libs(self):
# By default every compiler returns the empty list # By default every compiler returns the empty list
return [] return []
def _get_compiler_link_paths(self): @property
def compiler_verbose_output(self) -> Optional[str]:
"""Verbose output from compiling a dummy C source file. Output is cached."""
if not hasattr(self, "_compile_c_source_output"):
self._compile_c_source_output = self._compile_dummy_c_source()
return self._compile_c_source_output
def _compile_dummy_c_source(self) -> Optional[str]:
cc = self.cc if self.cc else self.cxx cc = self.cc if self.cc else self.cxx
if not cc or not self.verbose_flag: if not cc or not self.verbose_flag:
# Cannot determine implicit link paths without a compiler / verbose flag return None
return []
# What flag types apply to first_compiler, in what order
if cc == self.cc:
flags = ["cflags", "cppflags", "ldflags"]
else:
flags = ["cxxflags", "cppflags", "ldflags"]
try: try:
tmpdir = tempfile.mkdtemp(prefix="spack-implicit-link-info") tmpdir = tempfile.mkdtemp(prefix="spack-implicit-link-info")
@ -458,20 +583,19 @@ def _get_compiler_link_paths(self):
"int main(int argc, char* argv[]) { (void)argc; (void)argv; return 0; }\n" "int main(int argc, char* argv[]) { (void)argc; (void)argv; return 0; }\n"
) )
cc_exe = spack.util.executable.Executable(cc) cc_exe = spack.util.executable.Executable(cc)
for flag_type in flags: for flag_type in ["cflags" if cc == self.cc else "cxxflags", "cppflags", "ldflags"]:
cc_exe.add_default_arg(*self.flags.get(flag_type, [])) cc_exe.add_default_arg(*self.flags.get(flag_type, []))
with self.compiler_environment(): with self.compiler_environment():
output = cc_exe(self.verbose_flag, fin, "-o", fout, output=str, error=str) return cc_exe(self.verbose_flag, fin, "-o", fout, output=str, error=str)
return _parse_non_system_link_dirs(output)
except spack.util.executable.ProcessError as pe: except spack.util.executable.ProcessError as pe:
tty.debug("ProcessError: Command exited with non-zero status: " + pe.long_message) tty.debug("ProcessError: Command exited with non-zero status: " + pe.long_message)
return [] return None
finally: finally:
shutil.rmtree(tmpdir, ignore_errors=True) shutil.rmtree(tmpdir, ignore_errors=True)
@property @property
def verbose_flag(self): def verbose_flag(self) -> Optional[str]:
""" """
This property should be overridden in the compiler subclass if a This property should be overridden in the compiler subclass if a
verbose flag is available. verbose flag is available.

View file

@ -64,7 +64,7 @@ def verbose_flag(self):
# #
# This way, we at least enable the implicit rpath detection, which is # This way, we at least enable the implicit rpath detection, which is
# based on compilation of a C file (see method # based on compilation of a C file (see method
# spack.compiler._get_compiler_link_paths): in the case of a mixed # spack.compiler._compile_dummy_c_source): in the case of a mixed
# NAG/GCC toolchain, the flag will be passed to g++ (e.g. # NAG/GCC toolchain, the flag will be passed to g++ (e.g.
# 'g++ -Wl,-v ./main.c'), otherwise, the flag will be passed to nagfor # 'g++ -Wl,-v ./main.c'), otherwise, the flag will be passed to nagfor
# (e.g. 'nagfor -Wl,-v ./main.c' - note that nagfor recognizes '.c' # (e.g. 'nagfor -Wl,-v ./main.c' - note that nagfor recognizes '.c'

View file

@ -566,6 +566,23 @@ def _spec_with_default_name(spec_str, name):
return spec return spec
def _external_config_with_implictit_externals():
# Read packages.yaml and normalize it, so that it will not contain entries referring to
# virtual packages.
packages_yaml = _normalize_packages_yaml(spack.config.get("packages"))
# Add externals for libc from compilers on Linux
if spack.platforms.host().name != "linux":
return packages_yaml
for compiler in all_compilers_in_config():
libc = compiler.default_libc()
if libc:
entry = {"spec": f"{libc} %{compiler.spec}", "prefix": libc.external_path}
packages_yaml.setdefault(libc.name, {}).setdefault("externals", []).append(entry)
return packages_yaml
class ErrorHandler: class ErrorHandler:
def __init__(self, model): def __init__(self, model):
self.model = model self.model = model
@ -1554,12 +1571,8 @@ def emit_facts_from_requirement_rules(self, rules: List[RequirementRule]):
requirement_weight += 1 requirement_weight += 1
def external_packages(self): def external_packages(self):
"""Facts on external packages, as read from packages.yaml""" """Facts on external packages, from packages.yaml and implicit externals."""
# Read packages.yaml and normalize it, so that it packages_yaml = _external_config_with_implictit_externals()
# will not contain entries referring to virtual
# packages.
packages_yaml = spack.config.get("packages")
packages_yaml = _normalize_packages_yaml(packages_yaml)
self.gen.h1("External packages") self.gen.h1("External packages")
for pkg_name, data in packages_yaml.items(): for pkg_name, data in packages_yaml.items():
@ -3185,12 +3198,8 @@ def no_flags(self, node, flag_type):
self._specs[node].compiler_flags[flag_type] = [] self._specs[node].compiler_flags[flag_type] = []
def external_spec_selected(self, node, idx): def external_spec_selected(self, node, idx):
"""This means that the external spec and index idx """This means that the external spec and index idx has been selected for this package."""
has been selected for this package. packages_yaml = _external_config_with_implictit_externals()
"""
packages_yaml = spack.config.get("packages")
packages_yaml = _normalize_packages_yaml(packages_yaml)
spec_info = packages_yaml[node.pkg]["externals"][int(idx)] spec_info = packages_yaml[node.pkg]["externals"][int(idx)]
self._specs[node].external_path = spec_info.get("prefix", None) self._specs[node].external_path = spec_info.get("prefix", None)
self._specs[node].external_modules = spack.spec.Spec._format_module_list( self._specs[node].external_modules = spack.spec.Spec._format_module_list(

View file

@ -12,16 +12,3 @@
% macOS % macOS
os_compatible("monterey", "bigsur"). os_compatible("monterey", "bigsur").
os_compatible("bigsur", "catalina"). os_compatible("bigsur", "catalina").
% Ubuntu
os_compatible("ubuntu22.04", "ubuntu21.10").
os_compatible("ubuntu21.10", "ubuntu21.04").
os_compatible("ubuntu21.04", "ubuntu20.10").
os_compatible("ubuntu20.10", "ubuntu20.04").
os_compatible("ubuntu20.04", "ubuntu19.10").
os_compatible("ubuntu19.10", "ubuntu19.04").
os_compatible("ubuntu19.04", "ubuntu18.10").
os_compatible("ubuntu18.10", "ubuntu18.04").
%EL8
os_compatible("rhel8", "rocky8").

View file

@ -14,6 +14,7 @@
import spack.compilers import spack.compilers
import spack.spec import spack.spec
import spack.util.environment import spack.util.environment
import spack.util.module_cmd
from spack.compiler import Compiler from spack.compiler import Compiler
from spack.util.executable import Executable, ProcessError from spack.util.executable import Executable, ProcessError
@ -137,14 +138,6 @@ def __init__(self):
environment={}, environment={},
) )
def _get_compiler_link_paths(self):
# Mock os.path.isdir so the link paths don't have to exist
old_isdir = os.path.isdir
os.path.isdir = lambda x: True
ret = super()._get_compiler_link_paths()
os.path.isdir = old_isdir
return ret
@property @property
def name(self): def name(self):
return "mockcompiler" return "mockcompiler"
@ -162,34 +155,25 @@ def verbose_flag(self):
required_libs = ["libgfortran"] required_libs = ["libgfortran"]
def test_implicit_rpaths(dirs_with_libfiles, monkeypatch): @pytest.mark.not_on_windows("Not supported on Windows (yet)")
def test_implicit_rpaths(dirs_with_libfiles):
lib_to_dirs, all_dirs = dirs_with_libfiles lib_to_dirs, all_dirs = dirs_with_libfiles
def try_all_dirs(*args):
return all_dirs
monkeypatch.setattr(MockCompiler, "_get_compiler_link_paths", try_all_dirs)
expected_rpaths = set(lib_to_dirs["libstdc++"] + lib_to_dirs["libgfortran"])
compiler = MockCompiler() compiler = MockCompiler()
compiler._compile_c_source_output = "ld " + " ".join(f"-L{d}" for d in all_dirs)
retrieved_rpaths = compiler.implicit_rpaths() retrieved_rpaths = compiler.implicit_rpaths()
assert set(retrieved_rpaths) == expected_rpaths assert set(retrieved_rpaths) == set(lib_to_dirs["libstdc++"] + lib_to_dirs["libgfortran"])
no_flag_dirs = ["/path/to/first/lib", "/path/to/second/lib64"] without_flag_output = "ld -L/path/to/first/lib -L/path/to/second/lib64"
no_flag_output = "ld -L%s -L%s" % tuple(no_flag_dirs) with_flag_output = "ld -L/path/to/first/with/flag/lib -L/path/to/second/lib64"
flag_dirs = ["/path/to/first/with/flag/lib", "/path/to/second/lib64"]
flag_output = "ld -L%s -L%s" % tuple(flag_dirs)
def call_compiler(exe, *args, **kwargs): def call_compiler(exe, *args, **kwargs):
# This method can replace Executable.__call__ to emulate a compiler that # This method can replace Executable.__call__ to emulate a compiler that
# changes libraries depending on a flag. # changes libraries depending on a flag.
if "--correct-flag" in exe.exe: if "--correct-flag" in exe.exe:
return flag_output return with_flag_output
return no_flag_output return without_flag_output
@pytest.mark.not_on_windows("Not supported on Windows (yet)") @pytest.mark.not_on_windows("Not supported on Windows (yet)")
@ -203,8 +187,8 @@ def call_compiler(exe, *args, **kwargs):
("cc", "cppflags"), ("cc", "cppflags"),
], ],
) )
@pytest.mark.enable_compiler_link_paths @pytest.mark.enable_compiler_execution
def test_get_compiler_link_paths(monkeypatch, exe, flagname): def test_compile_dummy_c_source_adds_flags(monkeypatch, exe, flagname):
# create fake compiler that emits mock verbose output # create fake compiler that emits mock verbose output
compiler = MockCompiler() compiler = MockCompiler()
monkeypatch.setattr(Executable, "__call__", call_compiler) monkeypatch.setattr(Executable, "__call__", call_compiler)
@ -221,40 +205,38 @@ def test_get_compiler_link_paths(monkeypatch, exe, flagname):
assert False assert False
# Test without flags # Test without flags
assert compiler._get_compiler_link_paths() == no_flag_dirs assert compiler._compile_dummy_c_source() == without_flag_output
if flagname: if flagname:
# set flags and test # set flags and test
compiler.flags = {flagname: ["--correct-flag"]} compiler.flags = {flagname: ["--correct-flag"]}
assert compiler._get_compiler_link_paths() == flag_dirs assert compiler._compile_dummy_c_source() == with_flag_output
def test_get_compiler_link_paths_no_path(): @pytest.mark.enable_compiler_execution
def test_compile_dummy_c_source_no_path():
compiler = MockCompiler() compiler = MockCompiler()
compiler.cc = None compiler.cc = None
compiler.cxx = None compiler.cxx = None
compiler.f77 = None assert compiler._compile_dummy_c_source() is None
compiler.fc = None
assert compiler._get_compiler_link_paths() == []
def test_get_compiler_link_paths_no_verbose_flag(): @pytest.mark.enable_compiler_execution
def test_compile_dummy_c_source_no_verbose_flag():
compiler = MockCompiler() compiler = MockCompiler()
compiler._verbose_flag = None compiler._verbose_flag = None
assert compiler._get_compiler_link_paths() == [] assert compiler._compile_dummy_c_source() is None
@pytest.mark.not_on_windows("Not supported on Windows (yet)") @pytest.mark.not_on_windows("Not supported on Windows (yet)")
@pytest.mark.enable_compiler_link_paths @pytest.mark.enable_compiler_execution
def test_get_compiler_link_paths_load_env(working_env, monkeypatch, tmpdir): def test_compile_dummy_c_source_load_env(working_env, monkeypatch, tmpdir):
gcc = str(tmpdir.join("gcc")) gcc = str(tmpdir.join("gcc"))
with open(gcc, "w") as f: with open(gcc, "w") as f:
f.write( f.write(
"""#!/bin/sh f"""#!/bin/sh
if [ "$ENV_SET" = "1" ] && [ "$MODULE_LOADED" = "1" ]; then if [ "$ENV_SET" = "1" ] && [ "$MODULE_LOADED" = "1" ]; then
echo '""" printf '{without_flag_output}'
+ no_flag_output
+ """'
fi fi
""" """
) )
@ -274,7 +256,7 @@ def module(*args):
compiler.environment = {"set": {"ENV_SET": "1"}} compiler.environment = {"set": {"ENV_SET": "1"}}
compiler.modules = ["turn_on"] compiler.modules = ["turn_on"]
assert compiler._get_compiler_link_paths() == no_flag_dirs assert compiler._compile_dummy_c_source() == without_flag_output
# Get the desired flag from the specified compiler spec. # Get the desired flag from the specified compiler spec.

View file

@ -34,6 +34,7 @@
import spack.binary_distribution import spack.binary_distribution
import spack.caches import spack.caches
import spack.cmd.buildcache import spack.cmd.buildcache
import spack.compiler
import spack.compilers import spack.compilers
import spack.config import spack.config
import spack.database import spack.database
@ -269,10 +270,6 @@ def clean_test_environment():
ev.deactivate() ev.deactivate()
def _verify_executables_noop(*args):
return None
def _host(): def _host():
"""Mock archspec host so there is no inconsistency on the Windows platform """Mock archspec host so there is no inconsistency on the Windows platform
This function cannot be local as it needs to be pickleable""" This function cannot be local as it needs to be pickleable"""
@ -298,9 +295,7 @@ def mock_compiler_executable_verification(request, monkeypatch):
If a test is marked in that way this is a no-op.""" If a test is marked in that way this is a no-op."""
if "enable_compiler_verification" not in request.keywords: if "enable_compiler_verification" not in request.keywords:
monkeypatch.setattr( monkeypatch.setattr(spack.compiler.Compiler, "verify_executables", _return_none)
spack.compiler.Compiler, "verify_executables", _verify_executables_noop
)
# Hooks to add command line options or set other custom behaviors. # Hooks to add command line options or set other custom behaviors.
@ -934,26 +929,16 @@ def dirs_with_libfiles(tmpdir_factory):
yield lib_to_dirs, all_dirs yield lib_to_dirs, all_dirs
def _compiler_link_paths_noop(*args): def _return_none(*args):
return [] return None
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
def disable_compiler_execution(monkeypatch, request): def disable_compiler_execution(monkeypatch, request):
""" """Disable compiler execution to determine implicit link paths and libc flavor and version.
This fixture can be disabled for tests of the compiler link path To re-enable use `@pytest.mark.enable_compiler_execution`"""
functionality by:: if "enable_compiler_execution" not in request.keywords:
monkeypatch.setattr(spack.compiler.Compiler, "_compile_dummy_c_source", _return_none)
@pytest.mark.enable_compiler_link_paths
If a test is marked in that way this is a no-op."""
if "enable_compiler_link_paths" not in request.keywords:
# Compiler.determine_implicit_rpaths actually runs the compiler. So
# replace that function with a noop that simulates finding no implicit
# RPATHs
monkeypatch.setattr(
spack.compiler.Compiler, "_get_compiler_link_paths", _compiler_link_paths_noop
)
@pytest.fixture(scope="function") @pytest.fixture(scope="function")

View file

@ -12,7 +12,7 @@ markers =
requires_executables: tests that requires certain executables in PATH to run requires_executables: tests that requires certain executables in PATH to run
nomockstage: use a stage area specifically created for this test, instead of relying on a common mock stage nomockstage: use a stage area specifically created for this test, instead of relying on a common mock stage
enable_compiler_verification: enable compiler verification within unit tests enable_compiler_verification: enable compiler verification within unit tests
enable_compiler_link_paths: verifies compiler link paths within unit tests enable_compiler_execution: enable compiler execution to detect link paths and libc
disable_clean_stage_check: avoid failing tests if there are leftover files in the stage area disable_clean_stage_check: avoid failing tests if there are leftover files in the stage area
only_clingo: mark unit tests that run only with clingo only_clingo: mark unit tests that run only with clingo
only_original: mark unit tests that are specific to the original concretizer only_original: mark unit tests that are specific to the original concretizer

View file

@ -1185,5 +1185,13 @@ def runtime_constraints(cls, *, spec, pkg):
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"{str(spec)} and using the 'fortran' language",
) )
libc = compiler.default_libc()
if libc:
pkg("*").depends_on(
str(libc), when=f"%{str(compiler.spec)}", type="link", description="Add libc"
)
# 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(f"@={str(spec.version)}", when=f"%{str(spec)}") pkg("gcc-runtime").requires(f"@={str(spec.version)}", when=f"%{str(spec)}")

View file

@ -20,9 +20,12 @@ class Glibc(AutotoolsPackage, GNUMirrorPackage):
maintainers("haampie") maintainers("haampie")
build_directory = "build" build_directory = "build"
tags = ["runtime"]
license("LGPL-2.1-or-later") license("LGPL-2.1-or-later")
provides("libc")
version("master", branch="master") version("master", branch="master")
version("2.39", sha256="97f84f3b7588cd54093a6f6389b0c1a81e70d99708d74963a2e3eab7c7dc942d") version("2.39", sha256="97f84f3b7588cd54093a6f6389b0c1a81e70d99708d74963a2e3eab7c7dc942d")
version("2.38", sha256="16e51e0455e288f03380b436e41d5927c60945abd86d0c9852b84be57dd6ed5e") version("2.38", sha256="16e51e0455e288f03380b436e41d5927c60945abd86d0c9852b84be57dd6ed5e")

View file

@ -25,9 +25,12 @@ class Musl(MakefilePackage):
homepage = "https://www.musl-libc.org" homepage = "https://www.musl-libc.org"
url = "https://www.musl-libc.org/releases/musl-1.1.23.tar.gz" url = "https://www.musl-libc.org/releases/musl-1.1.23.tar.gz"
tags = ["runtime"]
license("MIT") license("MIT")
provides("libc")
version("1.2.4", sha256="7a35eae33d5372a7c0da1188de798726f68825513b7ae3ebe97aaaa52114f039") version("1.2.4", sha256="7a35eae33d5372a7c0da1188de798726f68825513b7ae3ebe97aaaa52114f039")
version("1.2.3", sha256="7d5b0b6062521e4627e099e4c9dc8248d32a30285e959b7eecaa780cf8cfd4a4") version("1.2.3", sha256="7d5b0b6062521e4627e099e4c9dc8248d32a30285e959b7eecaa780cf8cfd4a4")
version("1.2.2", sha256="9b969322012d796dc23dda27a35866034fa67d8fb67e0e2c45c913c3d43219dd") version("1.2.2", sha256="9b969322012d796dc23dda27a35866034fa67d8fb67e0e2c45c913c3d43219dd")