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