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 bootstrap_override_lld_legacy: Option<BootstrapOverrideLld> = "use-lld",
54 llvm_tools: Option<bool> = "llvm-tools",
55 deny_warnings: Option<bool> = "deny-warnings",
56 backtrace_on_ice: Option<bool> = "backtrace-on-ice",
57 verify_llvm_ir: Option<bool> = "verify-llvm-ir",
58 thin_lto_import_instr_limit: Option<u32> = "thin-lto-import-instr-limit",
59 remap_debuginfo: Option<bool> = "remap-debuginfo",
60 jemalloc: Option<bool> = "jemalloc",
61 test_compare_mode: Option<bool> = "test-compare-mode",
62 llvm_libunwind: Option<String> = "llvm-libunwind",
63 control_flow_guard: Option<bool> = "control-flow-guard",
64 ehcont_guard: Option<bool> = "ehcont-guard",
65 new_symbol_mangling: Option<bool> = "new-symbol-mangling",
66 annotate_moves_size_limit: Option<u64> = "annotate-moves-size-limit",
67 profile_generate: Option<String> = "profile-generate",
68 profile_use: Option<String> = "profile-use",
69 download_rustc: Option<StringOrBool> = "download-rustc",
71 lto: Option<String> = "lto",
72 validate_mir_opts: Option<u32> = "validate-mir-opts",
73 std_features: Option<BTreeSet<String>> = "std-features",
74 break_on_ice: Option<bool> = "break-on-ice",
75 parallel_frontend_threads: Option<u32> = "parallel-frontend-threads",
76 }
77}
78
79#[derive(Copy, Clone, Default, Debug, PartialEq)]
102pub enum BootstrapOverrideLld {
103 #[default]
105 None,
106 SelfContained,
108 External,
112}
113
114impl BootstrapOverrideLld {
115 pub fn is_used(&self) -> bool {
116 match self {
117 BootstrapOverrideLld::SelfContained | BootstrapOverrideLld::External => true,
118 BootstrapOverrideLld::None => false,
119 }
120 }
121}
122
123impl<'de> Deserialize<'de> for BootstrapOverrideLld {
124 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
125 where
126 D: Deserializer<'de>,
127 {
128 struct LldModeVisitor;
129
130 impl serde::de::Visitor<'_> for LldModeVisitor {
131 type Value = BootstrapOverrideLld;
132
133 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 formatter.write_str("one of true, 'self-contained' or 'external'")
135 }
136
137 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
138 where
139 E: serde::de::Error,
140 {
141 Ok(if v { BootstrapOverrideLld::External } else { BootstrapOverrideLld::None })
142 }
143
144 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
145 where
146 E: serde::de::Error,
147 {
148 match v {
149 "external" => Ok(BootstrapOverrideLld::External),
150 "self-contained" => Ok(BootstrapOverrideLld::SelfContained),
151 _ => Err(E::custom(format!("unknown mode {v}"))),
152 }
153 }
154 }
155
156 deserializer.deserialize_any(LldModeVisitor)
157 }
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
161pub enum RustOptimize {
162 String(String),
163 Int(u8),
164 Bool(bool),
165}
166
167impl Default for RustOptimize {
168 fn default() -> RustOptimize {
169 RustOptimize::Bool(false)
170 }
171}
172
173impl<'de> Deserialize<'de> for RustOptimize {
174 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
175 where
176 D: Deserializer<'de>,
177 {
178 deserializer.deserialize_any(OptimizeVisitor)
179 }
180}
181
182struct OptimizeVisitor;
183
184impl serde::de::Visitor<'_> for OptimizeVisitor {
185 type Value = RustOptimize;
186
187 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 formatter.write_str(r#"one of: 0, 1, 2, 3, "s", "z", true, false"#)
189 }
190
191 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
192 where
193 E: serde::de::Error,
194 {
195 if matches!(value, "s" | "z") {
196 Ok(RustOptimize::String(value.to_string()))
197 } else {
198 Err(serde::de::Error::custom(format_optimize_error_msg(value)))
199 }
200 }
201
202 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
203 where
204 E: serde::de::Error,
205 {
206 if matches!(value, 0..=3) {
207 Ok(RustOptimize::Int(value as u8))
208 } else {
209 Err(serde::de::Error::custom(format_optimize_error_msg(value)))
210 }
211 }
212
213 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
214 where
215 E: serde::de::Error,
216 {
217 Ok(RustOptimize::Bool(value))
218 }
219}
220
221fn format_optimize_error_msg(v: impl std::fmt::Display) -> String {
222 format!(
223 r#"unrecognized option for rust optimize: "{v}", expected one of 0, 1, 2, 3, "s", "z", true, false"#
224 )
225}
226
227impl RustOptimize {
228 pub(crate) fn is_release(&self) -> bool {
229 match &self {
230 RustOptimize::Bool(true) | RustOptimize::String(_) => true,
231 RustOptimize::Int(i) => *i > 0,
232 RustOptimize::Bool(false) => false,
233 }
234 }
235
236 pub(crate) fn get_opt_level(&self) -> Option<String> {
237 match &self {
238 RustOptimize::String(s) => Some(s.clone()),
239 RustOptimize::Int(i) => Some(i.to_string()),
240 RustOptimize::Bool(_) => None,
241 }
242 }
243}
244
245pub fn check_incompatible_options_for_ci_rustc(
248 host: TargetSelection,
249 current_config_toml: TomlConfig,
250 ci_config_toml: TomlConfig,
251) -> Result<(), String> {
252 macro_rules! err {
253 ($current:expr, $expected:expr, $config_section:expr) => {
254 if let Some(current) = &$current {
255 if Some(current) != $expected.as_ref() {
256 return Err(format!(
257 "ERROR: Setting `{}` is incompatible with `rust.download-rustc`. \
258 Current value: {:?}, Expected value(s): {}{:?}",
259 format!("{}.{}", $config_section, stringify!($expected).replace("_", "-")),
260 $current,
261 if $expected.is_some() { "None/" } else { "" },
262 $expected,
263 ));
264 };
265 };
266 };
267 }
268
269 macro_rules! warn {
270 ($current:expr, $expected:expr, $config_section:expr) => {
271 if let Some(current) = &$current {
272 if Some(current) != $expected.as_ref() {
273 println!(
274 "WARNING: `{}` has no effect with `rust.download-rustc`. \
275 Current value: {:?}, Expected value(s): {}{:?}",
276 format!("{}.{}", $config_section, stringify!($expected).replace("_", "-")),
277 $current,
278 if $expected.is_some() { "None/" } else { "" },
279 $expected,
280 );
281 };
282 };
283 };
284 }
285
286 let current_profiler = current_config_toml.build.as_ref().and_then(|b| b.profiler);
287 let profiler = ci_config_toml.build.as_ref().and_then(|b| b.profiler);
288 err!(current_profiler, profiler, "build");
289
290 let current_optimized_compiler_builtins =
291 current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone());
292 let optimized_compiler_builtins =
293 ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone());
294 err!(current_optimized_compiler_builtins, optimized_compiler_builtins, "build");
295
296 let host_str = host.to_string();
299 if let Some(current_cfg) = current_config_toml.target.as_ref().and_then(|c| c.get(&host_str))
300 && current_cfg.profiler.is_some()
301 {
302 let ci_target_toml = ci_config_toml.target.as_ref().and_then(|c| c.get(&host_str));
303 let ci_cfg = ci_target_toml.ok_or(format!(
304 "Target specific config for '{host_str}' is not present for CI-rustc"
305 ))?;
306
307 let profiler = &ci_cfg.profiler;
308 err!(current_cfg.profiler, profiler, "build");
309
310 let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins;
311 err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build");
312 }
313
314 let (Some(current_rust_config), Some(ci_rust_config)) =
315 (current_config_toml.rust, ci_config_toml.rust)
316 else {
317 return Ok(());
318 };
319
320 let Rust {
321 optimize,
323 randomize_layout,
324 debug_logging,
325 debuginfo_level_rustc,
326 compress_debuginfo,
327 llvm_tools,
328 llvm_bitcode_linker,
329 stack_protector,
330 strip,
331 jemalloc,
332 rpath,
333 channel,
334 default_linker,
335 std_features,
336
337 incremental: _,
339 debug: _,
340 codegen_units: _,
341 codegen_units_std: _,
342 rustc_debug_assertions: _,
343 std_debug_assertions: _,
344 tools_debug_assertions: _,
345 overflow_checks: _,
346 overflow_checks_std: _,
347 debuginfo_level: _,
348 debuginfo_level_std: _,
349 debuginfo_level_tools: _,
350 debuginfo_level_tests: _,
351 backtrace: _,
352 musl_root: _,
353 verbose_tests: _,
354 optimize_tests: _,
355 codegen_tests: _,
356 omit_git_hash: _,
357 dist_src: _,
358 save_toolstates: _,
359 codegen_backends: _,
360 lld: _,
361 lto: _,
362 deny_warnings: _,
363 backtrace_on_ice: _,
364 verify_llvm_ir: _,
365 thin_lto_import_instr_limit: _,
366 remap_debuginfo: _,
367 test_compare_mode: _,
368 llvm_libunwind: _,
369 control_flow_guard: _,
370 ehcont_guard: _,
371 new_symbol_mangling: _,
372 annotate_moves_size_limit: _,
373 profile_generate: _,
374 profile_use: _,
375 download_rustc: _,
376 validate_mir_opts: _,
377 frame_pointers: _,
378 break_on_ice: _,
379 parallel_frontend_threads: _,
380 bootstrap_override_lld: _,
381 bootstrap_override_lld_legacy: _,
382 rustflags: _,
383 } = ci_rust_config;
384
385 err!(current_rust_config.optimize, optimize, "rust");
393 err!(current_rust_config.randomize_layout, randomize_layout, "rust");
394 err!(current_rust_config.compress_debuginfo, compress_debuginfo, "rust");
395 err!(current_rust_config.debug_logging, debug_logging, "rust");
396 err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc, "rust");
397 err!(current_rust_config.rpath, rpath, "rust");
398 err!(current_rust_config.strip, strip, "rust");
399 err!(current_rust_config.llvm_tools, llvm_tools, "rust");
400 err!(current_rust_config.llvm_bitcode_linker, llvm_bitcode_linker, "rust");
401 err!(current_rust_config.jemalloc, jemalloc, "rust");
402 err!(current_rust_config.default_linker, default_linker, "rust");
403 err!(current_rust_config.stack_protector, stack_protector, "rust");
404 err!(current_rust_config.std_features, std_features, "rust");
405
406 warn!(current_rust_config.channel, channel, "rust");
407
408 Ok(())
409}
410
411pub(crate) const BUILTIN_CODEGEN_BACKENDS: &[&str] = &["llvm", "cranelift", "gcc"];
412
413pub(crate) fn parse_codegen_backends(
414 backends: Vec<String>,
415 section: &str,
416) -> Vec<CodegenBackendKind> {
417 const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_";
418
419 let mut found_backends = vec![];
420 for backend in &backends {
421 if let Some(stripped) = backend.strip_prefix(CODEGEN_BACKEND_PREFIX) {
422 panic!(
423 "Invalid value '{backend}' for '{section}.codegen-backends'. \
424 Codegen backends are defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
425 Please, use '{stripped}' instead."
426 )
427 }
428 let backend = match backend.as_str() {
429 "llvm" => CodegenBackendKind::Llvm,
430 "cranelift" => CodegenBackendKind::Cranelift,
431 "gcc" => CodegenBackendKind::Gcc,
432 backend => CodegenBackendKind::Custom(backend.to_string()),
433 };
434
435 if found_backends.contains(&backend) {
436 panic!(
437 "Duplicate value '{}' for '{section}.codegen-backends'. \
438 Each codegen backend should only be specified once.",
439 backend.name()
440 );
441 }
442
443 if !BUILTIN_CODEGEN_BACKENDS.contains(&backend.name()) {
444 if CiEnv::is_rust_lang_managed_ci_job() {
445 eprintln!("Unknown codegen backend {}", backend.name());
446 exit!(1);
447 }
448
449 println!(
450 "HELP: '{}' for '{section}.codegen-backends' might fail. \
451 List of known codegen backends: {BUILTIN_CODEGEN_BACKENDS:?}",
452 backend.name()
453 );
454 }
455 found_backends.push(backend);
456 }
457 if found_backends.is_empty() {
458 eprintln!("ERROR: `{section}.codegen-backends` should not be set to `[]`");
459 exit!(1);
460 }
461 found_backends
462}