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