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