Skip to content

Commit 5d0398d

Browse files
Rollup merge of rust-lang#128935 - lqd:needs-zstd, r=Kobzol
More work on `zstd` compression r? `@Kobzol` as we've discussed this. This is a draft to show the current approach of supporting zstd in compiletest, and making the tests using it unconditional. Knowing whether llvm/lld was built with `LLVM_ENABLE_ZSTD` is quite hard, so there are two strategies. There are details in the code, and we can discuss this approach. Until we know the config used to build CI artifacts, it seems our options are somewhat limited in any case. zlib compression seems always enabled, so we only check this in its dedicated test, allowing the test to ignore errors due to zstd not being supported. The zstd test is made unconditional in what it tests, by relying on `needs-llvm-zstd` to be ignored when `llvm.libzstd` isn't enabled in `config.toml`. try-job: x86_64-gnu try-job: x86_64-msvc try-job: x86_64-gnu-distcheck
2 parents d5f6ef0 + 5d83cb2 commit 5d0398d

File tree

10 files changed

+175
-46
lines changed

10 files changed

+175
-46
lines changed

src/bootstrap/src/core/config/config.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -1885,6 +1885,22 @@ impl Config {
18851885
warn("link-shared");
18861886
}
18871887

1888+
// FIXME(#129153): instead of all the ad-hoc `download-ci-llvm` checks that follow,
1889+
// use the `builder-config` present in tarballs since #128822 to compare the local
1890+
// config to the ones used to build the LLVM artifacts on CI, and only notify users
1891+
// if they've chosen a different value.
1892+
1893+
if libzstd.is_some() {
1894+
println!(
1895+
"WARNING: when using `download-ci-llvm`, the local `llvm.libzstd` option, \
1896+
like almost all `llvm.*` options, will be ignored and set by the LLVM CI \
1897+
artifacts builder config."
1898+
);
1899+
println!(
1900+
"HELP: To use `llvm.libzstd` for LLVM/LLD builds, set `download-ci-llvm` option to false."
1901+
);
1902+
}
1903+
18881904
// None of the LLVM options, except assertions, are supported
18891905
// when using downloaded LLVM. We could just ignore these but
18901906
// that's potentially confusing, so force them to not be
@@ -1894,7 +1910,6 @@ impl Config {
18941910
check_ci_llvm!(optimize_toml);
18951911
check_ci_llvm!(thin_lto);
18961912
check_ci_llvm!(release_debuginfo);
1897-
check_ci_llvm!(libzstd);
18981913
check_ci_llvm!(targets);
18991914
check_ci_llvm!(experimental_targets);
19001915
check_ci_llvm!(clang_cl);

src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile

+3-3
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/
6262
RUN ./build-clang.sh
6363
ENV CC=clang CXX=clang++
6464

65-
# rustc's LLVM needs zstd.
66-
COPY scripts/zstd.sh /tmp/
67-
RUN ./zstd.sh
65+
# Build zstd to enable `llvm.libzstd`.
66+
COPY host-x86_64/dist-x86_64-linux/build-zstd.sh /tmp/
67+
RUN ./build-zstd.sh
6868

6969
COPY scripts/sccache.sh /scripts/
7070
RUN sh /scripts/sccache.sh

src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ ENV RUST_CONFIGURE_ARGS \
2828
--build=x86_64-unknown-linux-gnu \
2929
--enable-sanitizers \
3030
--enable-profiler \
31-
--enable-compiler-docs
31+
--enable-compiler-docs \
32+
--set llvm.libzstd=true
3233
ENV SCRIPT python3 ../x.py --stage 2 test

src/tools/compiletest/src/command-list.rs

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
141141
"needs-force-clang-based-tests",
142142
"needs-git-hash",
143143
"needs-llvm-components",
144+
"needs-llvm-zstd",
144145
"needs-profiler-support",
145146
"needs-relocation-model-pic",
146147
"needs-run-enabled",

src/tools/compiletest/src/header.rs

+101
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,107 @@ pub fn extract_llvm_version_from_binary(binary_path: &str) -> Option<u32> {
12031203
None
12041204
}
12051205

1206+
/// For tests using the `needs-llvm-zstd` directive:
1207+
/// - for local LLVM builds, try to find the static zstd library in the llvm-config system libs.
1208+
/// - for `download-ci-llvm`, see if `lld` was built with zstd support.
1209+
pub fn llvm_has_libzstd(config: &Config) -> bool {
1210+
// Strategy 1: works for local builds but not with `download-ci-llvm`.
1211+
//
1212+
// We check whether `llvm-config` returns the zstd library. Bootstrap's `llvm.libzstd` will only
1213+
// ask to statically link it when building LLVM, so we only check if the list of system libs
1214+
// contains a path to that static lib, and that it exists.
1215+
//
1216+
// See compiler/rustc_llvm/build.rs for more details and similar expectations.
1217+
fn is_zstd_in_config(llvm_bin_dir: &Path) -> Option<()> {
1218+
let llvm_config_path = llvm_bin_dir.join("llvm-config");
1219+
let output = Command::new(llvm_config_path).arg("--system-libs").output().ok()?;
1220+
assert!(output.status.success(), "running llvm-config --system-libs failed");
1221+
1222+
let libs = String::from_utf8(output.stdout).ok()?;
1223+
for lib in libs.split_whitespace() {
1224+
if lib.ends_with("libzstd.a") && Path::new(lib).exists() {
1225+
return Some(());
1226+
}
1227+
}
1228+
1229+
None
1230+
}
1231+
1232+
// Strategy 2: `download-ci-llvm`'s `llvm-config --system-libs` will not return any libs to
1233+
// use.
1234+
//
1235+
// The CI artifacts also don't contain the bootstrap config used to build them: otherwise we
1236+
// could have looked at the `llvm.libzstd` config.
1237+
//
1238+
// We infer whether `LLVM_ENABLE_ZSTD` was used to build LLVM as a byproduct of testing whether
1239+
// `lld` supports it. If not, an error will be emitted: "LLVM was not built with
1240+
// LLVM_ENABLE_ZSTD or did not find zstd at build time".
1241+
#[cfg(unix)]
1242+
fn is_lld_built_with_zstd(llvm_bin_dir: &Path) -> Option<()> {
1243+
let lld_path = llvm_bin_dir.join("lld");
1244+
if lld_path.exists() {
1245+
// We can't call `lld` as-is, it expects to be invoked by a compiler driver using a
1246+
// different name. Prepare a temporary symlink to do that.
1247+
let lld_symlink_path = llvm_bin_dir.join("ld.lld");
1248+
if !lld_symlink_path.exists() {
1249+
std::os::unix::fs::symlink(lld_path, &lld_symlink_path).ok()?;
1250+
}
1251+
1252+
// Run `lld` with a zstd flag. We expect this command to always error here, we don't
1253+
// want to link actual files and don't pass any.
1254+
let output = Command::new(&lld_symlink_path)
1255+
.arg("--compress-debug-sections=zstd")
1256+
.output()
1257+
.ok()?;
1258+
assert!(!output.status.success());
1259+
1260+
// Look for a specific error caused by LLVM not being built with zstd support. We could
1261+
// also look for the "no input files" message, indicating the zstd flag was accepted.
1262+
let stderr = String::from_utf8(output.stderr).ok()?;
1263+
let zstd_available = !stderr.contains("LLVM was not built with LLVM_ENABLE_ZSTD");
1264+
1265+
// We don't particularly need to clean the link up (so the previous commands could fail
1266+
// in theory but won't in practice), but we can try.
1267+
std::fs::remove_file(lld_symlink_path).ok()?;
1268+
1269+
if zstd_available {
1270+
return Some(());
1271+
}
1272+
}
1273+
1274+
None
1275+
}
1276+
1277+
#[cfg(not(unix))]
1278+
fn is_lld_built_with_zstd(_llvm_bin_dir: &Path) -> Option<()> {
1279+
None
1280+
}
1281+
1282+
if let Some(llvm_bin_dir) = &config.llvm_bin_dir {
1283+
// Strategy 1: for local LLVM builds.
1284+
if is_zstd_in_config(llvm_bin_dir).is_some() {
1285+
return true;
1286+
}
1287+
1288+
// Strategy 2: for LLVM artifacts built on CI via `download-ci-llvm`.
1289+
//
1290+
// It doesn't work for cases where the artifacts don't contain the linker, but it's
1291+
// best-effort: CI has `llvm.libzstd` and `lld` enabled on the x64 linux artifacts, so it
1292+
// will at least work there.
1293+
//
1294+
// If this can be improved and expanded to less common cases in the future, it should.
1295+
if config.target == "x86_64-unknown-linux-gnu"
1296+
&& config.host == config.target
1297+
&& is_lld_built_with_zstd(llvm_bin_dir).is_some()
1298+
{
1299+
return true;
1300+
}
1301+
}
1302+
1303+
// Otherwise, all hope is lost.
1304+
false
1305+
}
1306+
12061307
/// Takes a directive of the form "<version1> [- <version2>]",
12071308
/// returns the numeric representation of <version1> and <version2> as
12081309
/// tuple: (<version1> as u32, <version2> as u32)

src/tools/compiletest/src/header/needs.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::common::{Config, Sanitizer};
2-
use crate::header::IgnoreDecision;
2+
use crate::header::{llvm_has_libzstd, IgnoreDecision};
33

44
pub(super) fn handle_needs(
55
cache: &CachedNeedsConditions,
@@ -144,6 +144,11 @@ pub(super) fn handle_needs(
144144
condition: cache.symlinks,
145145
ignore_reason: "ignored if symlinks are unavailable",
146146
},
147+
Need {
148+
name: "needs-llvm-zstd",
149+
condition: cache.llvm_zstd,
150+
ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression",
151+
},
147152
];
148153

149154
let (name, comment) = match ln.split_once([':', ' ']) {
@@ -169,7 +174,7 @@ pub(super) fn handle_needs(
169174
} else {
170175
return IgnoreDecision::Ignore {
171176
reason: if let Some(comment) = comment {
172-
format!("{} ({comment})", need.ignore_reason)
177+
format!("{} ({})", need.ignore_reason, comment.trim())
173178
} else {
174179
need.ignore_reason.into()
175180
},
@@ -210,6 +215,8 @@ pub(super) struct CachedNeedsConditions {
210215
rust_lld: bool,
211216
dlltool: bool,
212217
symlinks: bool,
218+
/// Whether LLVM built with zstd, for the `needs-llvm-zstd` directive.
219+
llvm_zstd: bool,
213220
}
214221

215222
impl CachedNeedsConditions {
@@ -253,6 +260,7 @@ impl CachedNeedsConditions {
253260
.join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" })
254261
.exists(),
255262

263+
llvm_zstd: llvm_has_libzstd(&config),
256264
dlltool: find_dlltool(&config),
257265
symlinks: has_symlinks(),
258266
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Checks debuginfo compression both for the always-enabled zlib, and when the optional zstd is
2+
// enabled:
3+
// - via rustc's `debuginfo-compression`,
4+
// - and via rust-lld's `compress-debug-sections`
5+
6+
//@ needs-llvm-zstd: we want LLVM/LLD to be built with zstd support
7+
//@ needs-rust-lld: the system linker will most likely not support zstd
8+
//@ only-linux
9+
//@ ignore-cross-compile
10+
11+
use run_make_support::{llvm_readobj, run_in_tmpdir, Rustc};
12+
13+
fn check_compression(compression: &str, to_find: &str) {
14+
// check compressed debug sections via rustc flag
15+
prepare_and_check(to_find, |rustc| {
16+
rustc.arg(&format!("-Zdebuginfo-compression={compression}"))
17+
});
18+
19+
// check compressed debug sections via rust-lld flag
20+
prepare_and_check(to_find, |rustc| {
21+
rustc.link_arg(&format!("-Wl,--compress-debug-sections={compression}"))
22+
});
23+
}
24+
25+
fn prepare_and_check<F: FnOnce(&mut Rustc) -> &mut Rustc>(to_find: &str, prepare_rustc: F) {
26+
run_in_tmpdir(|| {
27+
let mut rustc = Rustc::new();
28+
rustc
29+
.arg("-Zlinker-features=+lld")
30+
.arg("-Clink-self-contained=+linker")
31+
.arg("-Zunstable-options")
32+
.arg("-Cdebuginfo=full")
33+
.input("main.rs");
34+
prepare_rustc(&mut rustc).run();
35+
llvm_readobj().arg("-t").arg("main").run().assert_stdout_contains(to_find);
36+
});
37+
}
38+
39+
fn main() {
40+
check_compression("zlib", "ZLIB");
41+
check_compression("zstd", "ZSTD");
42+
}

tests/run-make/rust-lld-compress-debug-sections/rmake.rs

-39
This file was deleted.

0 commit comments

Comments
 (0)