@@ -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)
0 commit comments