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