From e722250e7d672a831c6552f4a83aef8920356a93 Mon Sep 17 00:00:00 2001 From: Etienne Cordonnier Date: Tue, 9 Sep 2025 11:16:39 +0200 Subject: [PATCH] link musl libc statically In the context of musl, users are typically expecting statically-linked tools, as this is one of musl's strong suits and one of the biggest reasons to use it instead of glibc (that is not static-link-friendly for reasons). - remove the build-flag which was causing musl libc to be linked dynamically - re-add feat_os_unix_musl which was removed in https://github.com/uutils/coreutils/commit/21d5cef15308c018bcb0b34fcbdff47a24aa9fa6 - exclude stdbuf from feat_os_unix_musl, since libstdbuf.so can't be built with a statically linked musl libc - add test_musl_no_dynamic_deps so that this does not regress in the future Fixes https://github.com/uutils/coreutils/issues/8572 Signed-off-by: Etienne Cordonnier --- .cargo/config.toml | 5 ----- .github/workflows/CICD.yml | 6 +++--- Cargo.toml | 17 ++++++++++++++--- tests/test_util_name.rs | 30 ++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index d40950639..45efde1f3 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,8 +8,3 @@ linker = "x86_64-unknown-redox-gcc" [env] # See feat_external_libstdbuf in src/uu/stdbuf/Cargo.toml LIBSTDBUF_DIR = "/usr/local/libexec/coreutils" - -# libstdbuf must be a shared library, so musl libc can't be linked statically -# https://github.com/rust-lang/rust/issues/82193 -[build] -rustflags = ["-C", "target-feature=-crt-static"] diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index deb77c6b8..f3e7b2fad 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -531,13 +531,13 @@ jobs: # - { os , target , cargo-options , default-features, features , use-cross , toolchain, skip-tests, workspace-tests, skip-package, skip-publish } - { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , features: feat_os_unix_gnueabihf , use-cross: use-cross , skip-tests: true } - { os: ubuntu-24.04-arm , target: aarch64-unknown-linux-gnu , features: feat_os_unix_gnueabihf } - - { os: ubuntu-latest , target: aarch64-unknown-linux-musl , features: feat_os_unix , use-cross: use-cross , skip-tests: true } + - { os: ubuntu-latest , target: aarch64-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross , skip-tests: true } # - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_selinux , use-cross: use-cross } - { os: ubuntu-latest , target: i686-unknown-linux-gnu , features: "feat_os_unix,test_risky_names", use-cross: use-cross } - - { os: ubuntu-latest , target: i686-unknown-linux-musl , features: feat_os_unix , use-cross: use-cross } + - { os: ubuntu-latest , target: i686-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross } - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: "feat_os_unix,test_risky_names", use-cross: use-cross } - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: "feat_os_unix,uudoc" , use-cross: no, workspace-tests: true } - - { os: ubuntu-latest , target: x86_64-unknown-linux-musl , features: feat_os_unix , use-cross: use-cross } + - { os: ubuntu-latest , target: x86_64-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross } - { os: ubuntu-latest , target: x86_64-unknown-redox , features: feat_os_unix_redox , use-cross: redoxer , skip-tests: true } - { os: ubuntu-latest , target: wasm32-unknown-unknown , default-features: false, features: uucore/format, skip-tests: true, skip-package: true, skip-publish: true } - { os: macos-latest , target: aarch64-apple-darwin , features: feat_os_macos, workspace-tests: true } # M1 CPU diff --git a/Cargo.toml b/Cargo.toml index babf1f09b..fe3eb8bb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,7 +163,6 @@ feat_os_macos = [ "feat_require_unix_hostid", ] # "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms. -# Also used for targets binding to the "musl" library (ref: ) feat_os_unix = [ "feat_Tier1", # @@ -171,6 +170,15 @@ feat_os_unix = [ "feat_require_unix_hostid", "feat_require_unix_utmpx", ] +# "feat_os_unix_musl" == set of utilities which can be built/run on targets binding to the "musl" library (ref: ) +# It excludes stdbuf due to cdylib limitations (https://github.com/rust-lang/rust/issues/82193) +feat_os_unix_musl = [ + "feat_Tier1", + # + "feat_require_unix_musl", + "feat_require_unix_hostid", + "feat_require_unix_utmpx", +] # "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms feat_os_windows = [ "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" @@ -194,7 +202,11 @@ feat_os_unix_android = [ # ** NOTE: these `feat_require_...` sets should be minimized as much as possible to encourage cross-platform availability of utilities # # "feat_require_unix" == set of utilities requiring support which is only available on unix platforms -feat_require_unix = [ +feat_require_unix = ["feat_require_unix_core", "stdbuf"] +# "feat_require_unix_musl" == set of utilities requiring unix support, excluding stdbuf (cdylib not supported on musl) +feat_require_unix_musl = ["feat_require_unix_core"] +# "feat_require_unix_core" == core unix utilities (shared between regular unix and musl) +feat_require_unix_core = [ "chgrp", "chmod", "chown", @@ -210,7 +222,6 @@ feat_require_unix = [ "nohup", "pathchk", "stat", - "stdbuf", "stty", "timeout", "tty", diff --git a/tests/test_util_name.rs b/tests/test_util_name.rs index 23dbccfe7..99b7d6887 100644 --- a/tests/test_util_name.rs +++ b/tests/test_util_name.rs @@ -2,6 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. + +// spell-checker:ignore readelf + use uutests::util::TestScenario; #[cfg(unix)] @@ -280,3 +283,30 @@ fn util_version() { assert_eq!(format!("coreutils {ver} (multi-call binary)\n"), output_str); } } + +#[test] +#[cfg(target_env = "musl")] +fn test_musl_no_dynamic_deps() { + use std::process::Command; + + let scenario = TestScenario::new("test_musl_no_dynamic_deps"); + if !scenario.bin_path.exists() { + println!("Skipping test: Binary not found at {:?}", scenario.bin_path); + return; + } + + let output = Command::new("readelf") + .arg("-d") + .arg(&scenario.bin_path) + .output() + .expect("Failed to run readelf"); + + let stdout = String::from_utf8_lossy(&output.stdout); + + // Static binaries should have no NEEDED entries (dynamic library dependencies) + assert!( + !stdout.contains("NEEDED"), + "Found dynamic dependencies in musl binary:\n{}", + stdout + ); +}