1use crate::common::{
2 Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer, query_rustc_output,
3};
4use crate::directives::{DirectiveLine, IgnoreDecision};
5
6pub(super) fn handle_needs(
7 cache: &CachedNeedsConditions,
8 config: &Config,
9 ln: &DirectiveLine<'_>,
10) -> IgnoreDecision {
11 let needs = &[
14 Need {
15 name: "needs-asm-support",
16 condition: config.has_asm_support(),
17 ignore_reason: "ignored on targets without inline assembly support",
18 },
19 Need {
20 name: "needs-sanitizer-support",
21 condition: cache.sanitizer_support,
22 ignore_reason: "ignored on targets without sanitizers support",
23 },
24 Need {
25 name: "needs-sanitizer-address",
26 condition: cache.sanitizer_address,
27 ignore_reason: "ignored on targets without address sanitizer",
28 },
29 Need {
30 name: "needs-sanitizer-cfi",
31 condition: cache.sanitizer_cfi,
32 ignore_reason: "ignored on targets without CFI sanitizer",
33 },
34 Need {
35 name: "needs-sanitizer-dataflow",
36 condition: cache.sanitizer_dataflow,
37 ignore_reason: "ignored on targets without dataflow sanitizer",
38 },
39 Need {
40 name: "needs-sanitizer-kcfi",
41 condition: cache.sanitizer_kcfi,
42 ignore_reason: "ignored on targets without kernel CFI sanitizer",
43 },
44 Need {
45 name: "needs-sanitizer-kasan",
46 condition: cache.sanitizer_kasan,
47 ignore_reason: "ignored on targets without kernel address sanitizer",
48 },
49 Need {
50 name: "needs-sanitizer-leak",
51 condition: cache.sanitizer_leak,
52 ignore_reason: "ignored on targets without leak sanitizer",
53 },
54 Need {
55 name: "needs-sanitizer-memory",
56 condition: cache.sanitizer_memory,
57 ignore_reason: "ignored on targets without memory sanitizer",
58 },
59 Need {
60 name: "needs-sanitizer-thread",
61 condition: cache.sanitizer_thread,
62 ignore_reason: "ignored on targets without thread sanitizer",
63 },
64 Need {
65 name: "needs-sanitizer-hwaddress",
66 condition: cache.sanitizer_hwaddress,
67 ignore_reason: "ignored on targets without hardware-assisted address sanitizer",
68 },
69 Need {
70 name: "needs-sanitizer-memtag",
71 condition: cache.sanitizer_memtag,
72 ignore_reason: "ignored on targets without memory tagging sanitizer",
73 },
74 Need {
75 name: "needs-sanitizer-realtime",
76 condition: cache.sanitizer_realtime,
77 ignore_reason: "ignored on targets without realtime sanitizer",
78 },
79 Need {
80 name: "needs-sanitizer-shadow-call-stack",
81 condition: cache.sanitizer_shadow_call_stack,
82 ignore_reason: "ignored on targets without shadow call stacks",
83 },
84 Need {
85 name: "needs-sanitizer-safestack",
86 condition: cache.sanitizer_safestack,
87 ignore_reason: "ignored on targets without SafeStack support",
88 },
89 Need {
90 name: "needs-enzyme",
91 condition: config.has_enzyme && config.default_codegen_backend.is_llvm(),
92 ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend",
93 },
94 Need {
95 name: "needs-run-enabled",
96 condition: config.run_enabled(),
97 ignore_reason: "ignored when running the resulting test binaries is disabled",
98 },
99 Need {
100 name: "needs-threads",
101 condition: config.has_threads(),
102 ignore_reason: "ignored on targets without threading support",
103 },
104 Need {
105 name: "needs-subprocess",
106 condition: config.has_subprocess_support(),
107 ignore_reason: "ignored on targets without subprocess support",
108 },
109 Need {
110 name: "needs-unwind",
111 condition: config.can_unwind(),
112 ignore_reason: "ignored on targets without unwinding support",
113 },
114 Need {
115 name: "needs-profiler-runtime",
116 condition: config.profiler_runtime,
117 ignore_reason: "ignored when the profiler runtime is not available",
118 },
119 Need {
120 name: "needs-force-clang-based-tests",
121 condition: config.run_clang_based_tests_with.is_some(),
122 ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set",
123 },
124 Need {
125 name: "needs-xray",
126 condition: cache.xray,
127 ignore_reason: "ignored on targets without xray tracing",
128 },
129 Need {
130 name: "needs-rust-lld",
131 condition: cache.rust_lld,
132 ignore_reason: "ignored on targets without Rust's LLD",
133 },
134 Need {
135 name: "needs-dlltool",
136 condition: cache.dlltool,
137 ignore_reason: "ignored when dlltool for the current architecture is not present",
138 },
139 Need {
140 name: "needs-git-hash",
141 condition: config.git_hash,
142 ignore_reason: "ignored when git hashes have been omitted for building",
143 },
144 Need {
145 name: "needs-dynamic-linking",
146 condition: config.target_cfg().dynamic_linking,
147 ignore_reason: "ignored on targets without dynamic linking",
148 },
149 Need {
150 name: "needs-relocation-model-pic",
151 condition: config.target_cfg().relocation_model == "pic",
152 ignore_reason: "ignored on targets without PIC relocation model",
153 },
154 Need {
155 name: "needs-deterministic-layouts",
156 condition: !config.rust_randomized_layout,
157 ignore_reason: "ignored when randomizing layouts",
158 },
159 Need {
160 name: "needs-wasmtime",
161 condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")),
162 ignore_reason: "ignored when wasmtime runner is not available",
163 },
164 Need {
165 name: "needs-symlink",
166 condition: cache.symlinks,
167 ignore_reason: "ignored if symlinks are unavailable",
168 },
169 Need {
170 name: "needs-llvm-zstd",
171 condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(),
172 ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend",
173 },
174 Need {
175 name: "needs-rustc-debug-assertions",
176 condition: config.with_rustc_debug_assertions,
177 ignore_reason: "ignored if rustc wasn't built with debug assertions",
178 },
179 Need {
180 name: "needs-std-debug-assertions",
181 condition: config.with_std_debug_assertions,
182 ignore_reason: "ignored if std wasn't built with debug assertions",
183 },
184 Need {
185 name: "needs-target-std",
186 condition: build_helper::targets::target_supports_std(&config.target),
187 ignore_reason: "ignored if target does not support std",
188 },
189 ];
190
191 let &DirectiveLine { name, .. } = ln;
192
193 if name == "needs-target-has-atomic" {
194 let Some(rest) = ln.value_after_colon() else {
195 return IgnoreDecision::Error {
196 message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(),
197 };
198 };
199
200 let specified_widths = rest
202 .split(',')
203 .map(|width| width.trim())
204 .map(ToString::to_string)
205 .collect::<Vec<String>>();
206
207 for width in &specified_widths {
208 if !KNOWN_TARGET_HAS_ATOMIC_WIDTHS.contains(&width.as_str()) {
209 return IgnoreDecision::Error {
210 message: format!(
211 "unknown width specified in `needs-target-has-atomic`: `{width}` is not a \
212 known `target_has_atomic_width`, known values are `{:?}`",
213 KNOWN_TARGET_HAS_ATOMIC_WIDTHS
214 ),
215 };
216 }
217 }
218
219 let satisfies_all_specified_widths = specified_widths
220 .iter()
221 .all(|specified| config.target_cfg().target_has_atomic.contains(specified));
222 if satisfies_all_specified_widths {
223 return IgnoreDecision::Continue;
224 } else {
225 return IgnoreDecision::Ignore {
226 reason: format!(
227 "skipping test as target does not support all of the required `target_has_atomic` widths `{:?}`",
228 specified_widths
229 ),
230 };
231 }
232 }
233
234 if name == "needs-crate-type" {
236 let Some(rest) = ln.value_after_colon() else {
237 return IgnoreDecision::Error {
238 message:
239 "expected `needs-crate-type` to have a comma-separated list of crate types"
240 .to_string(),
241 };
242 };
243
244 let specified_crate_types = rest
246 .split(',')
247 .map(|crate_type| crate_type.trim())
248 .map(ToString::to_string)
249 .collect::<Vec<String>>();
250
251 for crate_type in &specified_crate_types {
252 if !KNOWN_CRATE_TYPES.contains(&crate_type.as_str()) {
253 return IgnoreDecision::Error {
254 message: format!(
255 "unknown crate type specified in `needs-crate-type`: `{crate_type}` is not \
256 a known crate type, known values are `{:?}`",
257 KNOWN_CRATE_TYPES
258 ),
259 };
260 }
261 }
262
263 let satisfies_all_crate_types = specified_crate_types
264 .iter()
265 .all(|specified| config.supported_crate_types().contains(specified));
266 if satisfies_all_crate_types {
267 return IgnoreDecision::Continue;
268 } else {
269 return IgnoreDecision::Ignore {
270 reason: format!(
271 "skipping test as target does not support all of the crate types `{:?}`",
272 specified_crate_types
273 ),
274 };
275 }
276 }
277
278 if !name.starts_with("needs-") {
279 return IgnoreDecision::Continue;
280 }
281
282 if name == "needs-llvm-components" {
284 return IgnoreDecision::Continue;
285 }
286
287 let mut found_valid = false;
288 for need in needs {
289 if need.name == name {
290 if need.condition {
291 found_valid = true;
292 break;
293 } else {
294 return IgnoreDecision::Ignore {
295 reason: if let Some(comment) = ln.remark_after_space() {
296 format!("{} ({})", need.ignore_reason, comment.trim())
297 } else {
298 need.ignore_reason.into()
299 },
300 };
301 }
302 }
303 }
304
305 if found_valid {
306 IgnoreDecision::Continue
307 } else {
308 IgnoreDecision::Error { message: format!("invalid needs directive: {name}") }
309 }
310}
311
312struct Need {
313 name: &'static str,
314 condition: bool,
315 ignore_reason: &'static str,
316}
317
318pub(super) struct CachedNeedsConditions {
319 sanitizer_support: bool,
320 sanitizer_address: bool,
321 sanitizer_cfi: bool,
322 sanitizer_dataflow: bool,
323 sanitizer_kcfi: bool,
324 sanitizer_kasan: bool,
325 sanitizer_leak: bool,
326 sanitizer_memory: bool,
327 sanitizer_thread: bool,
328 sanitizer_hwaddress: bool,
329 sanitizer_memtag: bool,
330 sanitizer_realtime: bool,
331 sanitizer_shadow_call_stack: bool,
332 sanitizer_safestack: bool,
333 xray: bool,
334 rust_lld: bool,
335 dlltool: bool,
336 symlinks: bool,
337 llvm_zstd: bool,
339}
340
341impl CachedNeedsConditions {
342 pub(super) fn load(config: &Config) -> Self {
343 let target = &&*config.target;
344 let sanitizers = &config.target_cfg().sanitizers;
345 Self {
346 sanitizer_support: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(),
347 sanitizer_address: sanitizers.contains(&Sanitizer::Address),
348 sanitizer_cfi: sanitizers.contains(&Sanitizer::Cfi),
349 sanitizer_dataflow: sanitizers.contains(&Sanitizer::Dataflow),
350 sanitizer_kcfi: sanitizers.contains(&Sanitizer::Kcfi),
351 sanitizer_kasan: sanitizers.contains(&Sanitizer::KernelAddress),
352 sanitizer_leak: sanitizers.contains(&Sanitizer::Leak),
353 sanitizer_memory: sanitizers.contains(&Sanitizer::Memory),
354 sanitizer_thread: sanitizers.contains(&Sanitizer::Thread),
355 sanitizer_hwaddress: sanitizers.contains(&Sanitizer::Hwaddress),
356 sanitizer_memtag: sanitizers.contains(&Sanitizer::Memtag),
357 sanitizer_realtime: sanitizers.contains(&Sanitizer::Realtime),
358 sanitizer_shadow_call_stack: sanitizers.contains(&Sanitizer::ShadowCallStack),
359 sanitizer_safestack: sanitizers.contains(&Sanitizer::Safestack),
360 xray: config.target_cfg().xray,
361
362 rust_lld: config
372 .host_compile_lib_path
373 .parent()
374 .expect("couldn't traverse to the parent of the specified --compile-lib-path")
375 .join("lib")
376 .join("rustlib")
377 .join(target)
378 .join("bin")
379 .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" })
380 .exists(),
381
382 llvm_zstd: llvm_has_zstd(&config),
383 dlltool: find_dlltool(&config),
384 symlinks: has_symlinks(),
385 }
386 }
387}
388
389fn find_dlltool(config: &Config) -> bool {
390 let path = std::env::var_os("PATH").expect("missing PATH environment variable");
391 let path = std::env::split_paths(&path).collect::<Vec<_>>();
392
393 if !(config.matches_os("windows") && config.matches_env("gnu") && config.matches_abi("")) {
395 return false;
396 }
397
398 let dlltool_found = if cfg!(windows) {
401 path.iter().any(|dir| dir.join("dlltool.exe").is_file())
402 } else if config.matches_arch("i686") {
403 path.iter().any(|dir| dir.join("i686-w64-mingw32-dlltool").is_file())
404 } else if config.matches_arch("x86_64") {
405 path.iter().any(|dir| dir.join("x86_64-w64-mingw32-dlltool").is_file())
406 } else {
407 false
408 };
409 dlltool_found
410}
411
412#[cfg(windows)]
416fn has_symlinks() -> bool {
417 if std::env::var_os("CI").is_some() {
418 return true;
419 }
420 let link = std::env::temp_dir().join("RUST_COMPILETEST_SYMLINK_CHECK");
421 if std::os::windows::fs::symlink_file("DOES NOT EXIST", &link).is_ok() {
422 std::fs::remove_file(&link).unwrap();
423 true
424 } else {
425 false
426 }
427}
428
429#[cfg(not(windows))]
430fn has_symlinks() -> bool {
431 true
432}
433
434fn llvm_has_zstd(config: &Config) -> bool {
435 let output = query_rustc_output(
438 config,
439 &["-Zunstable-options", "--print=backend-has-zstd"],
440 Default::default(),
441 );
442 match output.trim() {
443 "true" => true,
444 "false" => false,
445 _ => panic!("unexpected output from `--print=backend-has-zstd`: {output:?}"),
446 }
447}