about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/compiletest/src/header.rs128
-rw-r--r--src/tools/compiletest/src/header/needs.rs226
2 files changed, 250 insertions, 104 deletions
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index bc90c413cfd..7613508077f 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -11,10 +11,10 @@ use tracing::*;
 use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
 use crate::header::cfg::parse_cfg_name_directive;
 use crate::header::cfg::MatchOutcome;
-use crate::util;
 use crate::{extract_cdb_version, extract_gdb_version};
 
 mod cfg;
+mod needs;
 #[cfg(test)]
 mod tests;
 
@@ -660,14 +660,6 @@ impl Config {
         }
     }
 
-    fn parse_needs_matching_clang(&self, line: &str) -> bool {
-        self.parse_name_directive(line, "needs-matching-clang")
-    }
-
-    fn parse_needs_profiler_support(&self, line: &str) -> bool {
-        self.parse_name_directive(line, "needs-profiler-support")
-    }
-
     fn has_cfg_prefix(&self, line: &str, prefix: &str) -> bool {
         // returns whether this line contains this prefix or not. For prefix
         // "ignore", returns true if line says "ignore-x86_64", "ignore-arch",
@@ -871,69 +863,13 @@ pub fn make_test_description<R: Read>(
     let mut ignore_message = None;
     let mut should_fail = false;
 
-    let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
-    let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some();
-    let has_asm_support = config.has_asm_support();
-    let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_cfi = util::CFI_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_kcfi = util::KCFI_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_kasan = util::KASAN_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_memtag = util::MEMTAG_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_shadow_call_stack = util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(&&*config.target);
-    let has_xray = util::XRAY_SUPPORTED_TARGETS.contains(&&*config.target);
-
-    // For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find
-    // whether `rust-lld` is present in the compiler under test.
-    //
-    // The --compile-lib-path is the path to host shared libraries, but depends on the OS. For
-    // example:
-    // - on linux, it can be <sysroot>/lib
-    // - on windows, it can be <sysroot>/bin
-    //
-    // However, `rust-lld` is only located under the lib path, so we look for it there.
-    let has_rust_lld = config
-        .compile_lib_path
-        .parent()
-        .expect("couldn't traverse to the parent of the specified --compile-lib-path")
-        .join("lib")
-        .join("rustlib")
-        .join(&config.target)
-        .join("bin")
-        .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" })
-        .exists();
-
-    fn is_on_path(file: &'static str) -> impl Fn() -> bool {
-        move || env::split_paths(&env::var_os("PATH").unwrap()).any(|dir| dir.join(file).is_file())
-    }
-
-    // On Windows, dlltool.exe is used for all architectures.
-    #[cfg(windows)]
-    let (has_i686_dlltool, has_x86_64_dlltool) =
-        (is_on_path("dlltool.exe"), is_on_path("dlltool.exe"));
-    // For non-Windows, there are architecture specific dlltool binaries.
-    #[cfg(not(windows))]
-    let (has_i686_dlltool, has_x86_64_dlltool) =
-        (is_on_path("i686-w64-mingw32-dlltool"), is_on_path("x86_64-w64-mingw32-dlltool"));
+    let needs_cache = needs::CachedNeedsConditions::load(config);
 
     iter_header(path, src, &mut |revision, ln| {
         if revision.is_some() && revision != cfg {
             return;
         }
-        macro_rules! reason {
-            ($e:expr) => {
-                ignore |= match $e {
-                    true => {
-                        ignore_message = Some(stringify!($e));
-                        true
-                    }
-                    false => ignore,
-                }
-            };
-        }
+
         macro_rules! decision {
             ($e:expr) => {
                 match $e {
@@ -944,6 +880,10 @@ pub fn make_test_description<R: Read>(
                         // compiletest so it won't grow indefinitely.
                         ignore_message = Some(Box::leak(Box::<str>::from(reason)));
                     }
+                    IgnoreDecision::Error { message } => {
+                        eprintln!("error: {}: {message}", path.display());
+                        panic!();
+                    }
                     IgnoreDecision::Continue => {}
                 }
             };
@@ -989,48 +929,27 @@ pub fn make_test_description<R: Read>(
             };
         }
 
+        decision!(needs::handle_needs(&needs_cache, config, ln));
         decision!(ignore_llvm(config, ln));
         decision!(ignore_cdb(config, ln));
         decision!(ignore_gdb(config, ln));
         decision!(ignore_lldb(config, ln));
 
-        reason!(
-            config.run_clang_based_tests_with.is_none() && config.parse_needs_matching_clang(ln)
-        );
-        reason!(!has_asm_support && config.parse_name_directive(ln, "needs-asm-support"));
-        reason!(!rustc_has_profiler_support && config.parse_needs_profiler_support(ln));
-        reason!(!config.run_enabled() && config.parse_name_directive(ln, "needs-run-enabled"));
-        reason!(
-            !rustc_has_sanitizer_support
-                && config.parse_name_directive(ln, "needs-sanitizer-support")
-        );
-        reason!(!has_asan && config.parse_name_directive(ln, "needs-sanitizer-address"));
-        reason!(!has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi"));
-        reason!(!has_kcfi && config.parse_name_directive(ln, "needs-sanitizer-kcfi"));
-        reason!(!has_kasan && config.parse_name_directive(ln, "needs-sanitizer-kasan"));
-        reason!(!has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak"));
-        reason!(!has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory"));
-        reason!(!has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread"));
-        reason!(!has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress"));
-        reason!(!has_memtag && config.parse_name_directive(ln, "needs-sanitizer-memtag"));
-        reason!(
-            !has_shadow_call_stack
-                && config.parse_name_directive(ln, "needs-sanitizer-shadow-call-stack")
-        );
-        reason!(!config.can_unwind() && config.parse_name_directive(ln, "needs-unwind"));
-        reason!(!has_xray && config.parse_name_directive(ln, "needs-xray"));
-        reason!(
-            config.target == "wasm32-unknown-unknown"
-                && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS)
-        );
-        reason!(!has_rust_lld && config.parse_name_directive(ln, "needs-rust-lld"));
-        reason!(config.parse_name_directive(ln, "needs-i686-dlltool") && !has_i686_dlltool());
-        reason!(config.parse_name_directive(ln, "needs-x86_64-dlltool") && !has_x86_64_dlltool());
-        reason!(
-            config.parse_name_directive(ln, "rust-lldb")
-                && config.debugger == Some(Debugger::Lldb)
-                && !config.lldb_native_rust
-        );
+        if config.target == "wasm32-unknown-unknown" {
+            if config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) {
+                decision!(IgnoreDecision::Ignore {
+                    reason: "ignored when checking the run results on WASM".into(),
+                });
+            }
+        }
+
+        if config.debugger == Some(Debugger::Lldb) && !config.lldb_native_rust {
+            if config.parse_name_directive(ln, "rust-lldb") {
+                decision!(IgnoreDecision::Ignore {
+                    reason: "ignored on targets wihtout Rust's LLDB".into()
+                });
+            }
+        }
 
         should_fail |= config.parse_name_directive(ln, "should-fail");
     });
@@ -1226,4 +1145,5 @@ fn ignore_llvm(config: &Config, line: &str) -> IgnoreDecision {
 enum IgnoreDecision {
     Ignore { reason: String },
     Continue,
+    Error { message: String },
 }
diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs
new file mode 100644
index 00000000000..9a7c4b86115
--- /dev/null
+++ b/src/tools/compiletest/src/header/needs.rs
@@ -0,0 +1,226 @@
+use crate::common::Config;
+use crate::header::IgnoreDecision;
+use crate::util;
+
+pub(super) fn handle_needs(
+    cache: &CachedNeedsConditions,
+    config: &Config,
+    ln: &str,
+) -> IgnoreDecision {
+    // Note thet we intentionally still put the needs- prefix here to make the file show up when
+    // grepping for a directive name, even though we could technically strip that.
+    let needs = &[
+        Need {
+            name: "needs-asm-support",
+            condition: config.has_asm_support(),
+            ignore_reason: "ignored on targets without inline assembly support",
+        },
+        Need {
+            name: "needs-sanitizer-support",
+            condition: cache.sanitizer_support,
+            ignore_reason: "ignored on targets without sanitizers support",
+        },
+        Need {
+            name: "needs-sanitizer-address",
+            condition: cache.sanitizer_address,
+            ignore_reason: "ignored on targets without address sanitizer",
+        },
+        Need {
+            name: "needs-sanitizer-cfi",
+            condition: cache.sanitizer_cfi,
+            ignore_reason: "ignored on targets without CFI sanitizer",
+        },
+        Need {
+            name: "needs-sanitizer-kcfi",
+            condition: cache.sanitizer_kcfi,
+            ignore_reason: "ignored on targets without kernel CFI sanitizer",
+        },
+        Need {
+            name: "needs-sanitizer-kasan",
+            condition: cache.sanitizer_kasan,
+            ignore_reason: "ignored on targets without kernel address sanitizer",
+        },
+        Need {
+            name: "needs-sanitizer-leak",
+            condition: cache.sanitizer_leak,
+            ignore_reason: "ignored on targets without leak sanitizer",
+        },
+        Need {
+            name: "needs-sanitizer-memory",
+            condition: cache.sanitizer_memory,
+            ignore_reason: "ignored on targets without memory sanitizer",
+        },
+        Need {
+            name: "needs-sanitizer-thread",
+            condition: cache.sanitizer_thread,
+            ignore_reason: "ignored on targets without thread sanitizer",
+        },
+        Need {
+            name: "needs-sanitizer-hwaddress",
+            condition: cache.sanitizer_hwaddress,
+            ignore_reason: "ignored on targets without hardware-assisted address sanitizer",
+        },
+        Need {
+            name: "needs-sanitizer-memtag",
+            condition: cache.sanitizer_memtag,
+            ignore_reason: "ignored on targets without memory tagging sanitizer",
+        },
+        Need {
+            name: "needs-sanitizer-shadow-call-stack",
+            condition: cache.sanitizer_shadow_call_stack,
+            ignore_reason: "ignored on targets without shadow call stacks",
+        },
+        Need {
+            name: "needs-run-enabled",
+            condition: config.run_enabled(),
+            ignore_reason: "ignored when running the resulting test binaries is disabled",
+        },
+        Need {
+            name: "needs-unwind",
+            condition: config.can_unwind(),
+            ignore_reason: "ignored on targets without unwinding support",
+        },
+        Need {
+            name: "needs-profiler-support",
+            condition: std::env::var_os("RUSTC_PROFILER_SUPPORT").is_some(),
+            ignore_reason: "ignored when profiler support is disabled",
+        },
+        Need {
+            name: "needs-matching-clang",
+            condition: config.run_clang_based_tests_with.is_some(),
+            ignore_reason: "ignored when the used clang does not match the built LLVM",
+        },
+        Need {
+            name: "needs-xray",
+            condition: cache.xray,
+            ignore_reason: "ignored on targets without xray tracing",
+        },
+        Need {
+            name: "needs-rust-lld",
+            condition: cache.rust_lld,
+            ignore_reason: "ignored on targets without Rust's LLD",
+        },
+        Need {
+            name: "needs-i686-dlltool",
+            condition: cache.i686_dlltool,
+            ignore_reason: "ignored when dlltool for i686 is not present",
+        },
+        Need {
+            name: "needs-x86_64-dlltool",
+            condition: cache.x86_64_dlltool,
+            ignore_reason: "ignored when dlltool for x86_64 is not present",
+        },
+    ];
+
+    let (name, comment) = match ln.split_once([':', ' ']) {
+        Some((name, comment)) => (name, Some(comment)),
+        None => (ln, None),
+    };
+
+    if !name.starts_with("needs-") {
+        return IgnoreDecision::Continue;
+    }
+
+    let mut found_valid = false;
+    for need in needs {
+        if need.name == name {
+            if need.condition {
+                found_valid = true;
+                break;
+            } else {
+                return IgnoreDecision::Ignore {
+                    reason: if let Some(comment) = comment {
+                        format!("{} ({comment})", need.ignore_reason)
+                    } else {
+                        need.ignore_reason.into()
+                    },
+                };
+            }
+        }
+    }
+
+    if found_valid {
+        IgnoreDecision::Continue
+    } else {
+        IgnoreDecision::Error { message: format!("invalid needs directive: {name}") }
+    }
+}
+
+struct Need {
+    name: &'static str,
+    condition: bool,
+    ignore_reason: &'static str,
+}
+
+pub(super) struct CachedNeedsConditions {
+    sanitizer_support: bool,
+    sanitizer_address: bool,
+    sanitizer_cfi: bool,
+    sanitizer_kcfi: bool,
+    sanitizer_kasan: bool,
+    sanitizer_leak: bool,
+    sanitizer_memory: bool,
+    sanitizer_thread: bool,
+    sanitizer_hwaddress: bool,
+    sanitizer_memtag: bool,
+    sanitizer_shadow_call_stack: bool,
+    xray: bool,
+    rust_lld: bool,
+    i686_dlltool: bool,
+    x86_64_dlltool: bool,
+}
+
+impl CachedNeedsConditions {
+    pub(super) fn load(config: &Config) -> Self {
+        let path = std::env::var_os("PATH").expect("missing PATH environment variable");
+        let path = std::env::split_paths(&path).collect::<Vec<_>>();
+
+        let target = &&*config.target;
+        Self {
+            sanitizer_support: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(),
+            sanitizer_address: util::ASAN_SUPPORTED_TARGETS.contains(target),
+            sanitizer_cfi: util::CFI_SUPPORTED_TARGETS.contains(target),
+            sanitizer_kcfi: util::KCFI_SUPPORTED_TARGETS.contains(target),
+            sanitizer_kasan: util::KASAN_SUPPORTED_TARGETS.contains(target),
+            sanitizer_leak: util::LSAN_SUPPORTED_TARGETS.contains(target),
+            sanitizer_memory: util::MSAN_SUPPORTED_TARGETS.contains(target),
+            sanitizer_thread: util::TSAN_SUPPORTED_TARGETS.contains(target),
+            sanitizer_hwaddress: util::HWASAN_SUPPORTED_TARGETS.contains(target),
+            sanitizer_memtag: util::MEMTAG_SUPPORTED_TARGETS.contains(target),
+            sanitizer_shadow_call_stack: util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(target),
+            xray: util::XRAY_SUPPORTED_TARGETS.contains(target),
+
+            // For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find
+            // whether `rust-lld` is present in the compiler under test.
+            //
+            // The --compile-lib-path is the path to host shared libraries, but depends on the OS. For
+            // example:
+            // - on linux, it can be <sysroot>/lib
+            // - on windows, it can be <sysroot>/bin
+            //
+            // However, `rust-lld` is only located under the lib path, so we look for it there.
+            rust_lld: config
+                .compile_lib_path
+                .parent()
+                .expect("couldn't traverse to the parent of the specified --compile-lib-path")
+                .join("lib")
+                .join("rustlib")
+                .join(target)
+                .join("bin")
+                .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" })
+                .exists(),
+
+            // On Windows, dlltool.exe is used for all architectures.
+            #[cfg(windows)]
+            i686_dlltool: path.iter().any(|dir| dir.join("dlltool.exe").is_file()),
+            #[cfg(windows)]
+            x86_64_dlltool: path.iter().any(|dir| dir.join("dlltool.exe").is_file()),
+
+            // For non-Windows, there are architecture specific dlltool binaries.
+            #[cfg(not(windows))]
+            i686_dlltool: path.iter().any(|dir| dir.join("i686-w64-mingw32-dlltool").is_file()),
+            #[cfg(not(windows))]
+            x86_64_dlltool: path.iter().any(|dir| dir.join("x86_64-w64-mingw32-dlltool").is_file()),
+        }
+    }
+}