1use std::collections::BTreeSet;
5use std::path::PathBuf;
6
7use build_helper::ci::CiEnv;
8use serde::{Deserialize, Deserializer};
9
10use crate::core::backend::CodegenBackendKind;
11use crate::core::config::macros::define_config;
12use crate::core::config::toml::TomlConfig;
13use crate::core::config::{CompressDebuginfo, DebuginfoLevel, StringOrBool, TargetSelection};
14use crate::utils::helpers;
15
16define_config! {
17 #[derive(Default)]
19 struct Rust {
20 optimize: Option<RustOptimize> = "optimize",
21 debug: Option<bool> = "debug",
22 codegen_units: Option<u32> = "codegen-units",
23 codegen_units_std: Option<u32> = "codegen-units-std",
24 rustc_debug_assertions: Option<bool> = "debug-assertions",
25 randomize_layout: Option<bool> = "randomize-layout",
26 std_debug_assertions: Option<bool> = "debug-assertions-std",
27 tools_debug_assertions: Option<bool> = "debug-assertions-tools",
28 overflow_checks: Option<bool> = "overflow-checks",
29 overflow_checks_std: Option<bool> = "overflow-checks-std",
30 debug_logging: Option<bool> = "debug-logging",
31 debuginfo_level: Option<DebuginfoLevel> = "debuginfo-level",
32 debuginfo_level_rustc: Option<DebuginfoLevel> = "debuginfo-level-rustc",
33 debuginfo_level_std: Option<DebuginfoLevel> = "debuginfo-level-std",
34 debuginfo_level_tools: Option<DebuginfoLevel> = "debuginfo-level-tools",
35 debuginfo_level_tests: Option<DebuginfoLevel> = "debuginfo-level-tests",
36 compress_debuginfo: Option<CompressDebuginfo> = "compress-debuginfo",
37 backtrace: Option<bool> = "backtrace",
38 incremental: Option<bool> = "incremental",
39 default_linker: Option<String> = "default-linker",
40 channel: Option<String> = "channel",
41 musl_root: Option<String> = "musl-root",
42 rpath: Option<bool> = "rpath",
43 rustflags: Option<Vec<String>> = "rustflags",
44 strip: Option<bool> = "strip",
45 frame_pointers: Option<bool> = "frame-pointers",
46 stack_protector: Option<String> = "stack-protector",
47 verbose_tests: Option<bool> = "verbose-tests",
48 optimize_tests: Option<bool> = "optimize-tests",
49 codegen_tests: Option<bool> = "codegen-tests",
50 omit_git_hash: Option<bool> = "omit-git-hash",
51 dist_src: Option<bool> = "dist-src",
52 save_toolstates: Option<String> = "save-toolstates",
53 codegen_backends: Option<Vec<String>> = "codegen-backends",
54 llvm_bitcode_linker: Option<bool> = "llvm-bitcode-linker",
55 lld: Option<bool> = "lld",
56 bootstrap_override_lld: Option<BootstrapOverrideLld> = "bootstrap-override-lld",
57 llvm_tools: Option<bool> = "llvm-tools",
58 deny_warnings: Option<bool> = "deny-warnings",
59 backtrace_on_ice: Option<bool> = "backtrace-on-ice",
60 verify_llvm_ir: Option<bool> = "verify-llvm-ir",
61 thin_lto_import_instr_limit: Option<u32> = "thin-lto-import-instr-limit",
62 remap_debuginfo: Option<bool> = "remap-debuginfo",
63 jemalloc: Option<bool> = "jemalloc",
65 test_compare_mode: Option<bool> = "test-compare-mode",
66 llvm_libunwind: Option<String> = "llvm-libunwind",
67 control_flow_guard: Option<bool> = "control-flow-guard",
68 ehcont_guard: Option<bool> = "ehcont-guard",
69 new_symbol_mangling: Option<bool> = "new-symbol-mangling",
70 annotate_moves_size_limit: Option<u64> = "annotate-moves-size-limit",
71 profile_generate: Option<PathBuf> = "profile-generate",
73 profile_use: Option<PathBuf> = "profile-use",
75 download_rustc: Option<StringOrBool> = "download-rustc",
77 lto: Option<String> = "lto",
78 validate_mir_opts: Option<u32> = "validate-mir-opts",
79 std_features: Option<BTreeSet<String>> = "std-features",
80 break_on_ice: Option<bool> = "break-on-ice",
81 parallel_frontend_threads: Option<u32> = "parallel-frontend-threads",
82 stdlib_semver_baseline: Option<String> = "stdlib-semver-baseline",
83 wasm_proc_macros: Option<bool> = "wasm-proc-macros",
84 wasm_panic_unwind_default: Option<bool> = "wasm-panic-unwind-default",
85 }
86}
87
88#[derive(Copy, Clone, Default, Debug, PartialEq)]
111pub enum BootstrapOverrideLld {
112 #[default]
114 None,
115 SelfContained,
117 External,
121}
122
123impl BootstrapOverrideLld {
124 pub fn is_used(&self) -> bool {
125 match self {
126 BootstrapOverrideLld::SelfContained | BootstrapOverrideLld::External => true,
127 BootstrapOverrideLld::None => false,
128 }
129 }
130}
131
132impl<'de> Deserialize<'de> for BootstrapOverrideLld {
133 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
134 where
135 D: Deserializer<'de>,
136 {
137 struct LldModeVisitor;
138
139 impl serde::de::Visitor<'_> for LldModeVisitor {
140 type Value = BootstrapOverrideLld;
141
142 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 formatter.write_str("one of true, 'self-contained' or 'external'")
144 }
145
146 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
147 where
148 E: serde::de::Error,
149 {
150 Ok(if v { BootstrapOverrideLld::External } else { BootstrapOverrideLld::None })
151 }
152
153 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
154 where
155 E: serde::de::Error,
156 {
157 match v {
158 "external" => Ok(BootstrapOverrideLld::External),
159 "self-contained" => Ok(BootstrapOverrideLld::SelfContained),
160 _ => Err(E::custom(format!("unknown mode {v}"))),
161 }
162 }
163 }
164
165 deserializer.deserialize_any(LldModeVisitor)
166 }
167}
168
169#[derive(Clone, Debug, PartialEq, Eq)]
170pub enum RustOptimize {
171 String(String),
172 Int(u8),
173 Bool(bool),
174}
175
176impl Default for RustOptimize {
177 fn default() -> RustOptimize {
178 RustOptimize::Bool(false)
179 }
180}
181
182impl<'de> Deserialize<'de> for RustOptimize {
183 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
184 where
185 D: Deserializer<'de>,
186 {
187 deserializer.deserialize_any(OptimizeVisitor)
188 }
189}
190
191struct OptimizeVisitor;
192
193impl serde::de::Visitor<'_> for OptimizeVisitor {
194 type Value = RustOptimize;
195
196 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197 formatter.write_str(r#"one of: 0, 1, 2, 3, "s", "z", true, false"#)
198 }
199
200 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
201 where
202 E: serde::de::Error,
203 {
204 if matches!(value, "s" | "z") {
205 Ok(RustOptimize::String(value.to_string()))
206 } else {
207 Err(serde::de::Error::custom(format_optimize_error_msg(value)))
208 }
209 }
210
211 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
212 where
213 E: serde::de::Error,
214 {
215 if matches!(value, 0..=3) {
216 Ok(RustOptimize::Int(value as u8))
217 } else {
218 Err(serde::de::Error::custom(format_optimize_error_msg(value)))
219 }
220 }
221
222 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
223 where
224 E: serde::de::Error,
225 {
226 Ok(RustOptimize::Bool(value))
227 }
228}
229
230fn format_optimize_error_msg(v: impl std::fmt::Display) -> String {
231 format!(
232 r#"unrecognized option for rust optimize: "{v}", expected one of 0, 1, 2, 3, "s", "z", true, false"#
233 )
234}
235
236impl RustOptimize {
237 pub(crate) fn is_release(&self) -> bool {
238 match &self {
239 RustOptimize::Bool(true) | RustOptimize::String(_) => true,
240 RustOptimize::Int(i) => *i > 0,
241 RustOptimize::Bool(false) => false,
242 }
243 }
244
245 pub(crate) fn get_opt_level(&self) -> Option<String> {
246 match &self {
247 RustOptimize::String(s) => Some(s.clone()),
248 RustOptimize::Int(i) => Some(i.to_string()),
249 RustOptimize::Bool(_) => None,
250 }
251 }
252}
253
254pub fn check_incompatible_options_for_ci_rustc(
257 host: TargetSelection,
258 current_config_toml: TomlConfig,
259 ci_config_toml: TomlConfig,
260) -> Result<(), String> {
261 macro_rules! err {
262 ($current:expr, $expected:expr, $config_section:expr) => {
263 if let Some(current) = &$current {
264 if Some(current) != $expected.as_ref() {
265 return Err(format!(
266 "ERROR: Setting `{}` is incompatible with `rust.download-rustc`. \
267 Current value: {:?}, Expected value(s): {}{:?}",
268 format!("{}.{}", $config_section, stringify!($expected).replace("_", "-")),
269 $current,
270 if $expected.is_some() { "None/" } else { "" },
271 $expected,
272 ));
273 };
274 };
275 };
276 }
277
278 macro_rules! warn {
279 ($current:expr, $expected:expr, $config_section:expr) => {
280 if let Some(current) = &$current {
281 if Some(current) != $expected.as_ref() {
282 println!(
283 "WARNING: `{}` has no effect with `rust.download-rustc`. \
284 Current value: {:?}, Expected value(s): {}{:?}",
285 format!("{}.{}", $config_section, stringify!($expected).replace("_", "-")),
286 $current,
287 if $expected.is_some() { "None/" } else { "" },
288 $expected,
289 );
290 };
291 };
292 };
293 }
294
295 let current_profiler = current_config_toml.build.as_ref().and_then(|b| b.profiler);
296 let profiler = ci_config_toml.build.as_ref().and_then(|b| b.profiler);
297 err!(current_profiler, profiler, "build");
298
299 let current_optimized_compiler_builtins =
300 current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone());
301 let optimized_compiler_builtins =
302 ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone());
303 err!(current_optimized_compiler_builtins, optimized_compiler_builtins, "build");
304
305 let current_allocator = current_config_toml.build.as_ref().and_then(|b| b.allocator);
306 let allocator = ci_config_toml.build.as_ref().and_then(|b| b.allocator);
307 err!(current_allocator, allocator, "build");
308
309 let host_str = host.to_string();
312 if let Some(current_cfg) = current_config_toml.target.as_ref().and_then(|c| c.get(&host_str))
313 && current_cfg.profiler.is_some()
314 {
315 let ci_target_toml = ci_config_toml.target.as_ref().and_then(|c| c.get(&host_str));
316 let ci_cfg = ci_target_toml.ok_or(format!(
317 "Target specific config for '{host_str}' is not present for CI-rustc"
318 ))?;
319
320 let profiler = &ci_cfg.profiler;
321 err!(current_cfg.profiler, profiler, format!("target.{host_str}"));
322
323 let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins;
324 err!(
325 current_cfg.optimized_compiler_builtins,
326 optimized_compiler_builtins,
327 format!("target.{host_str}")
328 );
329
330 err!(current_cfg.allocator, &ci_cfg.allocator, format!("target.{host_str}"));
331 err!(current_cfg.jemalloc, &ci_cfg.jemalloc, format!("target.{host_str}"));
332 }
333
334 let (Some(current_rust_config), Some(ci_rust_config)) =
335 (current_config_toml.rust, ci_config_toml.rust)
336 else {
337 return Ok(());
338 };
339
340 let Rust {
341 optimize,
343 randomize_layout,
344 debug_logging,
345 debuginfo_level_rustc,
346 compress_debuginfo,
347 llvm_tools,
348 llvm_bitcode_linker,
349 stack_protector,
350 strip,
351 jemalloc,
352 rpath,
353 channel,
354 default_linker,
355 std_features,
356
357 incremental: _,
359 debug: _,
360 codegen_units: _,
361 codegen_units_std: _,
362 rustc_debug_assertions: _,
363 std_debug_assertions: _,
364 tools_debug_assertions: _,
365 overflow_checks: _,
366 overflow_checks_std: _,
367 debuginfo_level: _,
368 debuginfo_level_std: _,
369 debuginfo_level_tools: _,
370 debuginfo_level_tests: _,
371 backtrace: _,
372 musl_root: _,
373 verbose_tests: _,
374 optimize_tests: _,
375 codegen_tests: _,
376 omit_git_hash: _,
377 dist_src: _,
378 save_toolstates: _,
379 codegen_backends: _,
380 lld: _,
381 lto: _,
382 deny_warnings: _,
383 backtrace_on_ice: _,
384 verify_llvm_ir: _,
385 thin_lto_import_instr_limit: _,
386 remap_debuginfo: _,
387 test_compare_mode: _,
388 llvm_libunwind: _,
389 control_flow_guard: _,
390 ehcont_guard: _,
391 new_symbol_mangling: _,
392 annotate_moves_size_limit: _,
393 profile_generate: _,
394 profile_use: _,
395 download_rustc: _,
396 validate_mir_opts: _,
397 frame_pointers: _,
398 break_on_ice: _,
399 parallel_frontend_threads: _,
400 bootstrap_override_lld: _,
401 rustflags: _,
402 stdlib_semver_baseline: _,
403 wasm_proc_macros: _,
404 wasm_panic_unwind_default: _,
405 } = ci_rust_config;
406
407 err!(current_rust_config.optimize, optimize, "rust");
415 err!(current_rust_config.randomize_layout, randomize_layout, "rust");
416 err!(current_rust_config.compress_debuginfo, compress_debuginfo, "rust");
417 err!(current_rust_config.debug_logging, debug_logging, "rust");
418 err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc, "rust");
419 err!(current_rust_config.rpath, rpath, "rust");
420 err!(current_rust_config.strip, strip, "rust");
421 err!(current_rust_config.llvm_tools, llvm_tools, "rust");
422 err!(current_rust_config.llvm_bitcode_linker, llvm_bitcode_linker, "rust");
423 err!(current_rust_config.jemalloc, jemalloc, "rust");
424 err!(current_rust_config.default_linker, default_linker, "rust");
425 err!(current_rust_config.stack_protector, stack_protector, "rust");
426 err!(current_rust_config.std_features, std_features, "rust");
427
428 warn!(current_rust_config.channel, channel, "rust");
429
430 Ok(())
431}
432
433pub(crate) const BUILTIN_CODEGEN_BACKENDS: &[&str] = &["llvm", "cranelift", "gcc"];
434
435pub(crate) fn parse_codegen_backends(
437 backends: Vec<String>,
438 section: &str,
439) -> Vec<CodegenBackendKind> {
440 const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_";
441
442 let mut found_backends = vec![];
443 for backend in &backends {
444 if let Some(stripped) = backend.strip_prefix(CODEGEN_BACKEND_PREFIX) {
445 panic!(
446 "Invalid value '{backend}' for '{section}.codegen-backends'. \
447 Codegen backends are defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
448 Please, use '{stripped}' instead."
449 )
450 }
451 let backend = match backend.as_str() {
452 "llvm" => CodegenBackendKind::Llvm,
453 "cranelift" => CodegenBackendKind::Cranelift,
454 "gcc" => CodegenBackendKind::Gcc,
455 backend => CodegenBackendKind::Custom(backend.to_string()),
456 };
457
458 if found_backends.contains(&backend) {
459 panic!(
460 "Duplicate value '{}' for '{section}.codegen-backends'. \
461 Each codegen backend should only be specified once.",
462 backend.name()
463 );
464 }
465
466 if !BUILTIN_CODEGEN_BACKENDS.contains(&backend.name()) {
467 if CiEnv::is_rust_lang_managed_ci_job() {
468 eprintln!("Unknown codegen backend {}", backend.name());
469 helpers::exit_process(1);
470 }
471
472 println!(
473 "HELP: '{}' for '{section}.codegen-backends' might fail. \
474 List of known codegen backends: {BUILTIN_CODEGEN_BACKENDS:?}",
475 backend.name()
476 );
477 }
478 found_backends.push(backend);
479 }
480 if found_backends.is_empty() {
481 eprintln!("ERROR: `{section}.codegen-backends` should not be set to `[]`");
482 helpers::exit_process(1);
483 }
484 found_backends
485}