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