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