summaryrefslogtreecommitdiffstats
path: root/src/bootstrap/config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bootstrap/config.rs')
-rw-r--r--src/bootstrap/config.rs232
1 files changed, 171 insertions, 61 deletions
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index fe932fd6b..4821d20a8 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -20,10 +20,11 @@ use std::str::FromStr;
use crate::cache::{Interned, INTERNER};
use crate::cc_detect::{ndk_compiler, Language};
use crate::channel::{self, GitInfo};
+use crate::compile::CODEGEN_BACKEND_PREFIX;
pub use crate::flags::Subcommand;
use crate::flags::{Color, Flags, Warnings};
use crate::util::{exe, output, t};
-use build_helper::detail_exit_macro;
+use build_helper::exit;
use once_cell::sync::OnceCell;
use semver::Version;
use serde::{Deserialize, Deserializer};
@@ -50,7 +51,7 @@ pub enum DryRun {
UserSelected,
}
-#[derive(Copy, Clone, Default)]
+#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub enum DebuginfoLevel {
#[default]
None,
@@ -130,7 +131,7 @@ pub struct Config {
pub sanitizers: bool,
pub profiler: bool,
pub omit_git_hash: bool,
- pub exclude: Vec<PathBuf>,
+ pub skip: Vec<PathBuf>,
pub include_default_paths: bool,
pub rustc_error_format: Option<String>,
pub json_output: bool,
@@ -232,8 +233,8 @@ pub struct Config {
pub llvm_profile_use: Option<String>,
pub llvm_profile_generate: bool,
pub llvm_libunwind_default: Option<LlvmLibunwind>,
- pub llvm_bolt_profile_generate: bool,
- pub llvm_bolt_profile_use: Option<String>,
+
+ pub reproducible_artifacts: Vec<String>,
pub build: TargetSelection,
pub hosts: Vec<TargetSelection>,
@@ -356,7 +357,7 @@ impl FromStr for LlvmLibunwind {
"no" => Ok(Self::No),
"in-tree" => Ok(Self::InTree),
"system" => Ok(Self::System),
- invalid => Err(format!("Invalid value '{}' for rust.llvm-libunwind config.", invalid)),
+ invalid => Err(format!("Invalid value '{invalid}' for rust.llvm-libunwind config.")),
}
}
}
@@ -420,7 +421,7 @@ impl std::str::FromStr for RustcLto {
"thin" => Ok(RustcLto::Thin),
"fat" => Ok(RustcLto::Fat),
"off" => Ok(RustcLto::Off),
- _ => Err(format!("Invalid value for rustc LTO: {}", s)),
+ _ => Err(format!("Invalid value for rustc LTO: {s}")),
}
}
}
@@ -498,7 +499,7 @@ impl fmt::Display for TargetSelection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.triple)?;
if let Some(file) = self.file {
- write!(f, "({})", file)?;
+ write!(f, "({file})")?;
}
Ok(())
}
@@ -506,7 +507,7 @@ impl fmt::Display for TargetSelection {
impl fmt::Debug for TargetSelection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self)
+ write!(f, "{self}")
}
}
@@ -533,7 +534,7 @@ pub struct Target {
pub linker: Option<PathBuf>,
pub ndk: Option<PathBuf>,
pub sanitizers: Option<bool>,
- pub profiler: Option<bool>,
+ pub profiler: Option<StringOrBool>,
pub rpath: Option<bool>,
pub crt_static: Option<bool>,
pub musl_root: Option<PathBuf>,
@@ -646,7 +647,7 @@ macro_rules! define_config {
panic!("overriding existing option")
} else {
eprintln!("overriding existing option: `{}`", stringify!($field));
- detail_exit_macro!(2);
+ exit!(2);
}
} else {
self.$field = other.$field;
@@ -745,7 +746,7 @@ impl<T> Merge for Option<T> {
panic!("overriding existing option")
} else {
eprintln!("overriding existing option");
- detail_exit_macro!(2);
+ exit!(2);
}
} else {
*self = other;
@@ -862,9 +863,9 @@ define_config! {
}
}
-#[derive(Debug, Deserialize)]
+#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
-enum StringOrBool {
+pub enum StringOrBool {
String(String),
Bool(bool),
}
@@ -875,11 +876,16 @@ impl Default for StringOrBool {
}
}
-#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
-#[serde(untagged)]
+impl StringOrBool {
+ fn is_string_or_true(&self) -> bool {
+ matches!(self, Self::String(_) | Self::Bool(true))
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RustOptimize {
- #[serde(deserialize_with = "deserialize_and_validate_opt_level")]
String(String),
+ Int(u8),
Bool(bool),
}
@@ -889,26 +895,73 @@ impl Default for RustOptimize {
}
}
-fn deserialize_and_validate_opt_level<'de, D>(d: D) -> Result<String, D::Error>
-where
- D: serde::de::Deserializer<'de>,
-{
- let v = String::deserialize(d)?;
- if ["0", "1", "2", "3", "s", "z"].iter().find(|x| **x == v).is_some() {
- Ok(v)
- } else {
- Err(format!(r#"unrecognized option for rust optimize: "{}", expected one of "0", "1", "2", "3", "s", "z""#, v)).map_err(serde::de::Error::custom)
+impl<'de> Deserialize<'de> for RustOptimize {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ deserializer.deserialize_any(OptimizeVisitor)
+ }
+}
+
+struct OptimizeVisitor;
+
+impl<'de> serde::de::Visitor<'de> for OptimizeVisitor {
+ type Value = RustOptimize;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ formatter.write_str(r#"one of: 0, 1, 2, 3, "s", "z", true, false"#)
+ }
+
+ fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ if ["s", "z"].iter().find(|x| **x == value).is_some() {
+ Ok(RustOptimize::String(value.to_string()))
+ } else {
+ Err(format_optimize_error_msg(value)).map_err(serde::de::Error::custom)
+ }
+ }
+
+ fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ if matches!(value, 0..=3) {
+ Ok(RustOptimize::Int(value as u8))
+ } else {
+ Err(format_optimize_error_msg(value)).map_err(serde::de::Error::custom)
+ }
+ }
+
+ fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(RustOptimize::Bool(value))
}
}
+fn format_optimize_error_msg(v: impl std::fmt::Display) -> String {
+ format!(
+ r#"unrecognized option for rust optimize: "{v}", expected one of 0, 1, 2, 3, "s", "z", true, false"#
+ )
+}
+
impl RustOptimize {
pub(crate) fn is_release(&self) -> bool {
- if let RustOptimize::Bool(true) | RustOptimize::String(_) = &self { true } else { false }
+ match &self {
+ RustOptimize::Bool(true) | RustOptimize::String(_) => true,
+ RustOptimize::Int(i) => *i > 0,
+ RustOptimize::Bool(false) => false,
+ }
}
pub(crate) fn get_opt_level(&self) -> Option<String> {
match &self {
RustOptimize::String(s) => Some(s.clone()),
+ RustOptimize::Int(i) => Some(i.to_string()),
RustOptimize::Bool(_) => None,
}
}
@@ -991,7 +1044,7 @@ define_config! {
llvm_libunwind: Option<String> = "llvm-libunwind",
android_ndk: Option<String> = "android-ndk",
sanitizers: Option<bool> = "sanitizers",
- profiler: Option<bool> = "profiler",
+ profiler: Option<StringOrBool> = "profiler",
rpath: Option<bool> = "rpath",
crt_static: Option<bool> = "crt-static",
musl_root: Option<String> = "musl-root",
@@ -1054,7 +1107,7 @@ impl Config {
.and_then(|table: toml::Value| TomlConfig::deserialize(table))
.unwrap_or_else(|err| {
eprintln!("failed to parse TOML configuration '{}': {err}", file.display());
- detail_exit_macro!(2);
+ exit!(2);
})
}
Self::parse_inner(args, get_toml)
@@ -1066,7 +1119,7 @@ impl Config {
// Set flags.
config.paths = std::mem::take(&mut flags.paths);
- config.exclude = flags.exclude;
+ config.skip = flags.skip.into_iter().chain(flags.exclude).collect();
config.include_default_paths = flags.include_default_paths;
config.rustc_error_format = flags.rustc_error_format;
config.json_output = flags.json_output;
@@ -1081,15 +1134,6 @@ impl Config {
config.free_args = std::mem::take(&mut flags.free_args);
config.llvm_profile_use = flags.llvm_profile_use;
config.llvm_profile_generate = flags.llvm_profile_generate;
- config.llvm_bolt_profile_generate = flags.llvm_bolt_profile_generate;
- config.llvm_bolt_profile_use = flags.llvm_bolt_profile_use;
-
- if config.llvm_bolt_profile_generate && config.llvm_bolt_profile_use.is_some() {
- eprintln!(
- "Cannot use both `llvm_bolt_profile_generate` and `llvm_bolt_profile_use` at the same time"
- );
- detail_exit_macro!(1);
- }
// Infer the rest of the configuration.
@@ -1179,7 +1223,7 @@ impl Config {
include_path.push("src");
include_path.push("bootstrap");
include_path.push("defaults");
- include_path.push(format!("config.{}.toml", include));
+ include_path.push(format!("config.{include}.toml"));
let included_toml = get_toml(&include_path);
toml.merge(included_toml, ReplaceOpt::IgnoreDuplicate);
}
@@ -1212,7 +1256,7 @@ impl Config {
}
}
eprintln!("failed to parse override `{option}`: `{err}");
- detail_exit_macro!(2)
+ exit!(2)
}
toml.merge(override_toml, ReplaceOpt::Override);
@@ -1328,6 +1372,25 @@ impl Config {
let mut omit_git_hash = None;
if let Some(rust) = toml.rust {
+ set(&mut config.channel, rust.channel);
+
+ config.download_rustc_commit = config.download_ci_rustc_commit(rust.download_rustc);
+ // This list is incomplete, please help by expanding it!
+ if config.download_rustc_commit.is_some() {
+ // We need the channel used by the downloaded compiler to match the one we set for rustdoc;
+ // otherwise rustdoc-ui tests break.
+ let ci_channel = t!(fs::read_to_string(config.src.join("src/ci/channel")));
+ let ci_channel = ci_channel.trim_end();
+ if config.channel != ci_channel
+ && !(config.channel == "dev" && ci_channel == "nightly")
+ {
+ panic!(
+ "setting rust.channel={} is incompatible with download-rustc",
+ config.channel
+ );
+ }
+ }
+
debug = rust.debug;
debug_assertions = rust.debug_assertions;
debug_assertions_std = rust.debug_assertions_std;
@@ -1339,6 +1402,7 @@ impl Config {
debuginfo_level_std = rust.debuginfo_level_std;
debuginfo_level_tools = rust.debuginfo_level_tools;
debuginfo_level_tests = rust.debuginfo_level_tests;
+
config.rust_split_debuginfo = rust
.split_debuginfo
.as_deref()
@@ -1354,7 +1418,6 @@ impl Config {
set(&mut config.jemalloc, rust.jemalloc);
set(&mut config.test_compare_mode, rust.test_compare_mode);
set(&mut config.backtrace, rust.backtrace);
- set(&mut config.channel, rust.channel);
config.description = rust.description;
set(&mut config.rust_dist_src, rust.dist_src);
set(&mut config.verbose_tests, rust.verbose_tests);
@@ -1387,16 +1450,27 @@ impl Config {
.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
if let Some(ref backends) = rust.codegen_backends {
- config.rust_codegen_backends =
- backends.iter().map(|s| INTERNER.intern_str(s)).collect();
+ let available_backends = vec!["llvm", "cranelift", "gcc"];
+
+ config.rust_codegen_backends = backends.iter().map(|s| {
+ if let Some(backend) = s.strip_prefix(CODEGEN_BACKEND_PREFIX) {
+ if available_backends.contains(&backend) {
+ panic!("Invalid value '{s}' for 'rust.codegen-backends'. Instead, please use '{backend}'.");
+ } else {
+ println!("help: '{s}' for 'rust.codegen-backends' might fail. \
+ Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
+ In this case, it would be referred to as '{backend}'.");
+ }
+ }
+
+ INTERNER.intern_str(s)
+ }).collect();
}
config.rust_codegen_units = rust.codegen_units.map(threads_from_config);
config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config);
config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use);
config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate);
- config.download_rustc_commit = config.download_ci_rustc_commit(rust.download_rustc);
-
config.rust_lto = rust
.lto
.as_deref()
@@ -1408,6 +1482,8 @@ impl Config {
config.rust_profile_generate = flags.rust_profile_generate;
}
+ config.reproducible_artifacts = flags.reproducible_artifact;
+
// rust_info must be set before is_ci_llvm_available() is called.
let default = config.channel == "dev";
config.omit_git_hash = omit_git_hash.unwrap_or(default);
@@ -1452,7 +1528,7 @@ impl Config {
let asserts = llvm_assertions.unwrap_or(false);
config.llvm_from_ci = match llvm.download_ci_llvm {
Some(StringOrBool::String(s)) => {
- assert!(s == "if-available", "unknown option `{}` for download-ci-llvm", s);
+ assert!(s == "if-available", "unknown option `{s}` for download-ci-llvm");
crate::llvm::is_ci_llvm_available(&config, asserts)
}
Some(StringOrBool::Bool(b)) => b,
@@ -1508,6 +1584,11 @@ impl Config {
let mut target = Target::from_triple(&triple);
if let Some(ref s) = cfg.llvm_config {
+ if config.download_rustc_commit.is_some() && triple == &*config.build.triple {
+ panic!(
+ "setting llvm_config for the host is incompatible with download-rustc"
+ );
+ }
target.llvm_config = Some(config.src.join(s));
}
target.llvm_has_rust_patches = cfg.llvm_has_rust_patches;
@@ -1673,6 +1754,18 @@ impl Config {
}
}
+ /// Runs a command, printing out nice contextual information if it fails.
+ /// Exits if the command failed to execute at all, otherwise returns its
+ /// `status.success()`.
+ #[deprecated = "use `Builder::try_run` instead where possible"]
+ pub(crate) fn try_run(&self, cmd: &mut Command) -> Result<(), ()> {
+ if self.dry_run() {
+ return Ok(());
+ }
+ self.verbose(&format!("running: {cmd:?}"));
+ build_helper::util::try_run(cmd, self.is_verbose())
+ }
+
/// A git invocation which runs inside the source directory.
///
/// Use this rather than `Command::new("git")` in order to support out-of-tree builds.
@@ -1709,10 +1802,10 @@ impl Config {
pub(crate) fn artifact_version_part(&self, commit: &str) -> String {
let (channel, version) = if self.rust_info.is_managed_git_subrepository() {
let mut channel = self.git();
- channel.arg("show").arg(format!("{}:src/ci/channel", commit));
+ channel.arg("show").arg(format!("{commit}:src/ci/channel"));
let channel = output(&mut channel);
let mut version = self.git();
- version.arg("show").arg(format!("{}:src/version", commit));
+ version.arg("show").arg(format!("{commit}:src/version"));
let version = output(&mut version);
(channel.trim().to_owned(), version.trim().to_owned())
} else {
@@ -1729,10 +1822,10 @@ impl Config {
"help: consider using a git checkout or ensure these files are readable"
);
if let Err(channel) = channel {
- eprintln!("reading {}/src/ci/channel failed: {:?}", src, channel);
+ eprintln!("reading {src}/src/ci/channel failed: {channel:?}");
}
if let Err(version) = version {
- eprintln!("reading {}/src/version failed: {:?}", src, version);
+ eprintln!("reading {src}/src/version failed: {version:?}");
}
panic!();
}
@@ -1778,6 +1871,12 @@ impl Config {
self.out.join(&*self.build.triple).join("ci-llvm")
}
+ /// Directory where the extracted `rustc-dev` component is stored.
+ pub(crate) fn ci_rustc_dir(&self) -> PathBuf {
+ assert!(self.download_rustc());
+ self.out.join(self.build.triple).join("ci-rustc")
+ }
+
/// Determine whether llvm should be linked dynamically.
///
/// If `false`, llvm should be linked statically.
@@ -1813,11 +1912,11 @@ impl Config {
self.download_rustc_commit().is_some()
}
- pub(crate) fn download_rustc_commit(&self) -> Option<&'static str> {
+ pub(crate) fn download_rustc_commit(&self) -> Option<&str> {
static DOWNLOAD_RUSTC: OnceCell<Option<String>> = OnceCell::new();
if self.dry_run() && DOWNLOAD_RUSTC.get().is_none() {
// avoid trying to actually download the commit
- return None;
+ return self.download_rustc_commit.as_deref();
}
DOWNLOAD_RUSTC
@@ -1852,7 +1951,7 @@ impl Config {
pub fn verbose(&self, msg: &str) {
if self.verbose > 0 {
- println!("{}", msg);
+ println!("{msg}");
}
}
@@ -1864,12 +1963,24 @@ impl Config {
self.target_config.values().any(|t| t.sanitizers == Some(true)) || self.sanitizers
}
+ pub fn profiler_path(&self, target: TargetSelection) -> Option<&str> {
+ match self.target_config.get(&target)?.profiler.as_ref()? {
+ StringOrBool::String(s) => Some(s),
+ StringOrBool::Bool(_) => None,
+ }
+ }
+
pub fn profiler_enabled(&self, target: TargetSelection) -> bool {
- self.target_config.get(&target).map(|t| t.profiler).flatten().unwrap_or(self.profiler)
+ self.target_config
+ .get(&target)
+ .and_then(|t| t.profiler.as_ref())
+ .map(StringOrBool::is_string_or_true)
+ .unwrap_or(self.profiler)
}
pub fn any_profiler_enabled(&self) -> bool {
- self.target_config.values().any(|t| t.profiler == Some(true)) || self.profiler
+ self.target_config.values().any(|t| matches!(&t.profiler, Some(p) if p.is_string_or_true()))
+ || self.profiler
}
pub fn rpath_enabled(&self, target: TargetSelection) -> bool {
@@ -1930,10 +2041,9 @@ impl Config {
{
let prev_version = format!("{}.{}.x", source_version.major, source_version.minor - 1);
eprintln!(
- "Unexpected rustc version: {}, we should use {}/{} to build source with {}",
- rustc_version, prev_version, source_version, source_version
+ "Unexpected rustc version: {rustc_version}, we should use {prev_version}/{source_version} to build source with {source_version}"
);
- detail_exit_macro!(1);
+ exit!(1);
}
}
@@ -1945,7 +2055,7 @@ impl Config {
Some(StringOrBool::Bool(true)) => false,
Some(StringOrBool::String(s)) if s == "if-unchanged" => true,
Some(StringOrBool::String(other)) => {
- panic!("unrecognized option for download-rustc: {}", other)
+ panic!("unrecognized option for download-rustc: {other}")
}
};
@@ -1969,7 +2079,7 @@ impl Config {
println!("help: maybe your repository history is too shallow?");
println!("help: consider disabling `download-rustc`");
println!("help: or fetch enough history to include one upstream commit");
- crate::detail_exit_macro!(1);
+ crate::exit!(1);
}
// Warn if there were changes to the compiler or standard library since the ancestor commit.