From 613aaf8d7ad42e8d5bffbf0ee8e84b99a43052fe Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Wed, 13 Mar 2024 20:22:18 +0100 Subject: [PATCH] integration-test: Set `rust-lld` as a linker only on macOS The recommendation (coming from rust-lang/rust#130062) for Linux hosts is using C compiler driver as a linker, which is able to find system-wide libraries. Using linker binaries directly in `-C linker` (e.g. `-C linker=rust-lld`) often results in errors like: ``` cargo:warning=error: linking with `rust-lld` failed: exit status: 1ger, ppv-lite86, libc... cargo:warning= | cargo:warning= = note: LC_ALL="C" PATH="/home/vadorovsky/.rustup/toolchains/stable-x86_64-un cargo:warning= = note: rust-lld: error: unable to find library -lgcc_s cargo:warning= rust-lld: error: unable to find library -lc cargo:warning= cargo:warning= cargo:warning= cargo:warning=error: aborting due to 1 previous error ``` Not touching the linker settings is usually the best approach for Linux systems. Native builds pick up the default C toolchain. Cross builds default to GCC cross wrapper, but that's easy to supress with clang and lld using RUSTFLAGS. However, `-C linker=rust-lld` still works the best on macOS, where Rust toolchains come with libc and runtime library and there is no need to link any system libraries. Keep setting it only for macOS. Fixes #907 --- xtask/src/run.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/xtask/src/run.rs b/xtask/src/run.rs index c92d08d59..b7e835781 100644 --- a/xtask/src/run.rs +++ b/xtask/src/run.rs @@ -54,12 +54,60 @@ pub fn build(target: Option<&str>, f: F) -> Result> where F: FnOnce(&mut Command) -> &mut Command, { - // Always use rust-lld and -Zbuild-std in case we're cross-compiling. let mut cmd = Command::new("cargo"); cmd.args(["build", "--message-format=json"]); if let Some(target) = target { - let config = format!("target.{target}.linker = \"rust-lld\""); - cmd.args(["--target", target, "--config", &config]); + cmd.args(["--target", target]); + // During a cross build, it's important to pick the correct linker. + // + // On Linux, C compiler driver should be always used as the linker. + // The C compiler eventually ends up calling the linker binary (e.g. + // ld, lld), but before doing that, it figures out the appropiate + // linker flags, which include the system library paths. Calling linker + // binaries directly results in them not being able to find system + // libraries (like libc or runtime library), which can manifest in + // errors like `unable to find library -lgcc_s`. + // + // The issue was discussed with the Rust maintainers[0] and the + // consensus is to always use `-C linker` to specify the C compiler + /// (e.g. `-C linker=gcc`, `-C linker=clang`). Choice of a specific + // linker (like ldd or mold) can be done with `-C link-arg=-fuse-ld=`. + // + // However, the same doesn't hold true for macOS. Cross toolchains for + // Linux targets on macOS hosts, provided by rustup, are self-contained, + // come with libc, runtime library and don't depend on any system + // libraries. Therefore, direct usage of rust-lld through + // `-C linker=rust-lld` works fine, because rust-lld is able to find + // libc and runtime in rustup's toolchain. + // + // Using system-wide compiler (clang) on macOS would take the opposite + // effect than on Linux. The system compiler would be the one not being + // able to find the Linux-compatible libc and runtime + // + // To sum it up, this is the way of determining the linker we follow: + // + // - On Linux, use a C compiler for the cross target. + // - On macOS, use rust-lld directly. + // + // The first point is already covered by the configuration in + // `.cargo/config.toml`, which uses cross GCC compilers as linkers for + // popular non-x86_64 targets (e.g. aarch64-linux-musl-gcc). People + // who want to use a different compiler (e.g. clang), can overwrite + // RUSTFLAGS. Set of flags like `-C linker=clang + // -C link-arg=--target=aarch64-unknown-linux-musl + // -C link-arg=-fuse-ld=lld` should result in the build which uses only + // LLVM and has no dependency on GCC. mold can be used with + // `-C link-arg=-fuse-ld=mold` + // + // To cover the macOS case, we explicitly set the linker to rust-lld, + // ignoring the default configuration from `.cargo/config.toml`. + // + // [0] https://github.com/rust-lang/rust/issues/130062 + #[cfg(target_os = "macos")] + { + let config = format!("target.{target}.linker = \"rust-lld\""); + cmd.args(["--config", &config]); + } } f(&mut cmd);