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