Skip to content

Commit cee7f18

Browse files
committed
compiletest: implement needs-lvm-zstd directive
1 parent b5723af commit cee7f18

File tree

3 files changed

+111
-1
lines changed

3 files changed

+111
-1
lines changed

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 101 additions & 0 deletions
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

Lines changed: 9 additions & 1 deletion
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([':', ' ']) {
@@ -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
}

0 commit comments

Comments
 (0)