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