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