diff options
Diffstat (limited to 'src/bootstrap/config.rs')
-rw-r--r-- | src/bootstrap/config.rs | 262 |
1 files changed, 223 insertions, 39 deletions
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index cc3b3bc25..e192cda9a 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -21,9 +21,10 @@ use crate::cache::{Interned, INTERNER}; use crate::cc_detect::{ndk_compiler, Language}; use crate::channel::{self, GitInfo}; pub use crate::flags::Subcommand; -use crate::flags::{Color, Flags}; +use crate::flags::{Color, Flags, Warnings}; use crate::util::{exe, output, t}; use once_cell::sync::OnceCell; +use semver::Version; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; @@ -237,6 +238,8 @@ pub struct Config { initial_rustfmt: RefCell<RustfmtState>, #[cfg(test)] pub initial_rustfmt: RefCell<RustfmtState>, + + pub paths: Vec<PathBuf>, } #[derive(Default, Deserialize, Clone)] @@ -347,7 +350,7 @@ impl SplitDebuginfo { } /// LTO mode used for compiling rustc itself. -#[derive(Default, Clone, PartialEq)] +#[derive(Default, Clone, PartialEq, Debug)] pub enum RustcLto { Off, #[default] @@ -376,6 +379,16 @@ pub struct TargetSelection { file: Option<Interned<String>>, } +/// Newtype over `Vec<TargetSelection>` so we can implement custom parsing logic +#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct TargetSelectionList(Vec<TargetSelection>); + +pub fn target_selection_list(s: &str) -> Result<TargetSelectionList, String> { + Ok(TargetSelectionList( + s.split(",").filter(|s| !s.is_empty()).map(TargetSelection::from_user).collect(), + )) +} + impl TargetSelection { pub fn from_user(selection: &str) -> Self { let path = Path::new(selection); @@ -455,6 +468,7 @@ pub struct Target { pub ndk: Option<PathBuf>, pub sanitizers: Option<bool>, pub profiler: Option<bool>, + pub rpath: Option<bool>, pub crt_static: Option<bool>, pub musl_root: Option<PathBuf>, pub musl_libdir: Option<PathBuf>, @@ -494,29 +508,42 @@ struct TomlConfig { profile: Option<String>, } +/// Describes how to handle conflicts in merging two [`TomlConfig`] +#[derive(Copy, Clone, Debug)] +enum ReplaceOpt { + /// Silently ignore a duplicated value + IgnoreDuplicate, + /// Override the current value, even if it's `Some` + Override, + /// Exit with an error on duplicate values + ErrorOnDuplicate, +} + trait Merge { - fn merge(&mut self, other: Self); + fn merge(&mut self, other: Self, replace: ReplaceOpt); } impl Merge for TomlConfig { fn merge( &mut self, - TomlConfig { build, install, llvm, rust, dist, target, profile: _, changelog_seen: _ }: Self, + TomlConfig { build, install, llvm, rust, dist, target, profile: _, changelog_seen }: Self, + replace: ReplaceOpt, ) { - fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>) { + fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>, replace: ReplaceOpt) { if let Some(new) = y { if let Some(original) = x { - original.merge(new); + original.merge(new, replace); } else { *x = Some(new); } } } - do_merge(&mut self.build, build); - do_merge(&mut self.install, install); - do_merge(&mut self.llvm, llvm); - do_merge(&mut self.rust, rust); - do_merge(&mut self.dist, dist); + self.changelog_seen.merge(changelog_seen, replace); + do_merge(&mut self.build, build, replace); + do_merge(&mut self.install, install, replace); + do_merge(&mut self.llvm, llvm, replace); + do_merge(&mut self.rust, rust, replace); + do_merge(&mut self.dist, dist, replace); assert!(target.is_none(), "merging target-specific config is not currently supported"); } } @@ -533,10 +560,33 @@ macro_rules! define_config { } impl Merge for $name { - fn merge(&mut self, other: Self) { + fn merge(&mut self, other: Self, replace: ReplaceOpt) { $( - if !self.$field.is_some() { - self.$field = other.$field; + match replace { + ReplaceOpt::IgnoreDuplicate => { + if self.$field.is_none() { + self.$field = other.$field; + } + }, + ReplaceOpt::Override => { + if other.$field.is_some() { + self.$field = other.$field; + } + } + ReplaceOpt::ErrorOnDuplicate => { + if other.$field.is_some() { + if self.$field.is_some() { + if cfg!(test) { + panic!("overriding existing option") + } else { + eprintln!("overriding existing option: `{}`", stringify!($field)); + crate::detail_exit(2); + } + } else { + self.$field = other.$field; + } + } + } } )* } @@ -609,6 +659,37 @@ macro_rules! define_config { } } +impl<T> Merge for Option<T> { + fn merge(&mut self, other: Self, replace: ReplaceOpt) { + match replace { + ReplaceOpt::IgnoreDuplicate => { + if self.is_none() { + *self = other; + } + } + ReplaceOpt::Override => { + if other.is_some() { + *self = other; + } + } + ReplaceOpt::ErrorOnDuplicate => { + if other.is_some() { + if self.is_some() { + if cfg!(test) { + panic!("overriding existing option") + } else { + eprintln!("overriding existing option"); + crate::detail_exit(2); + } + } else { + *self = other; + } + } + } + } + } +} + define_config! { /// TOML representation of various global build decisions. #[derive(Default)] @@ -800,6 +881,7 @@ define_config! { android_ndk: Option<String> = "android-ndk", sanitizers: Option<bool> = "sanitizers", profiler: Option<bool> = "profiler", + rpath: Option<bool> = "rpath", crt_static: Option<bool> = "crt-static", musl_root: Option<String> = "musl-root", musl_libdir: Option<String> = "musl-libdir", @@ -849,48 +931,45 @@ impl Config { pub fn parse(args: &[String]) -> Config { #[cfg(test)] - let get_toml = |_: &_| TomlConfig::default(); + fn get_toml(_: &Path) -> TomlConfig { + TomlConfig::default() + } + #[cfg(not(test))] - let get_toml = |file: &Path| { + fn get_toml(file: &Path) -> TomlConfig { let contents = t!(fs::read_to_string(file), format!("config file {} not found", file.display())); // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of // TomlConfig and sub types to be monomorphized 5x by toml. - match toml::from_str(&contents) + toml::from_str(&contents) .and_then(|table: toml::Value| TomlConfig::deserialize(table)) - { - Ok(table) => table, - Err(err) => { - eprintln!("failed to parse TOML configuration '{}': {}", file.display(), err); + .unwrap_or_else(|err| { + eprintln!("failed to parse TOML configuration '{}': {err}", file.display()); crate::detail_exit(2); - } - } - }; - + }) + } Self::parse_inner(args, get_toml) } - fn parse_inner<'a>(args: &[String], get_toml: impl 'a + Fn(&Path) -> TomlConfig) -> Config { - let flags = Flags::parse(&args); + fn parse_inner(args: &[String], get_toml: impl Fn(&Path) -> TomlConfig) -> Config { + let mut flags = Flags::parse(&args); let mut config = Config::default_opts(); // Set flags. + config.paths = std::mem::take(&mut flags.paths); config.exclude = flags.exclude.into_iter().map(|path| TaskPath::parse(path)).collect(); config.include_default_paths = flags.include_default_paths; config.rustc_error_format = flags.rustc_error_format; config.json_output = flags.json_output; config.on_fail = flags.on_fail; - config.jobs = flags.jobs.map(threads_from_config); + config.jobs = Some(threads_from_config(flags.jobs as u32)); config.cmd = flags.cmd; config.incremental = flags.incremental; config.dry_run = if flags.dry_run { DryRun::UserSelected } else { DryRun::Disabled }; config.keep_stage = flags.keep_stage; config.keep_stage_std = flags.keep_stage_std; config.color = flags.color; - config.free_args = flags.free_args.clone().unwrap_or_default(); - if let Some(value) = flags.deny_warnings { - config.deny_warnings = value; - } + 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; @@ -985,9 +1064,41 @@ impl Config { include_path.push("defaults"); include_path.push(format!("config.{}.toml", include)); let included_toml = get_toml(&include_path); - toml.merge(included_toml); + toml.merge(included_toml, ReplaceOpt::IgnoreDuplicate); } + let mut override_toml = TomlConfig::default(); + for option in flags.set.iter() { + fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> { + toml::from_str(&option) + .and_then(|table: toml::Value| TomlConfig::deserialize(table)) + } + + let mut err = match get_table(option) { + Ok(v) => { + override_toml.merge(v, ReplaceOpt::ErrorOnDuplicate); + continue; + } + Err(e) => e, + }; + // We want to be able to set string values without quotes, + // like in `configure.py`. Try adding quotes around the right hand side + if let Some((key, value)) = option.split_once("=") { + if !value.contains('"') { + match get_table(&format!(r#"{key}="{value}""#)) { + Ok(v) => { + override_toml.merge(v, ReplaceOpt::ErrorOnDuplicate); + continue; + } + Err(e) => err = e, + } + } + } + eprintln!("failed to parse override `{option}`: `{err}"); + crate::detail_exit(2) + } + toml.merge(override_toml, ReplaceOpt::Override); + config.changelog_seen = toml.changelog_seen; let build = toml.build.unwrap_or_default(); @@ -1007,9 +1118,12 @@ impl Config { config.download_beta_toolchain(); config.out.join(config.build.triple).join("stage0/bin/rustc") }); + config.initial_cargo = build .cargo - .map(PathBuf::from) + .map(|cargo| { + t!(PathBuf::from(cargo).canonicalize(), "`initial_cargo` not found on disk") + }) .unwrap_or_else(|| config.out.join(config.build.triple).join("stage0/bin/cargo")); // NOTE: it's important this comes *after* we set `initial_rustc` just above. @@ -1019,14 +1133,14 @@ impl Config { config.out = dir; } - config.hosts = if let Some(arg_host) = flags.host { + config.hosts = if let Some(TargetSelectionList(arg_host)) = flags.host { arg_host } else if let Some(file_host) = build.host { file_host.iter().map(|h| TargetSelection::from_user(h)).collect() } else { vec![config.build] }; - config.targets = if let Some(arg_target) = flags.target { + config.targets = if let Some(TargetSelectionList(arg_target)) = flags.target { arg_target } else if let Some(file_target) = build.target { file_target.iter().map(|h| TargetSelection::from_user(h)).collect() @@ -1062,7 +1176,7 @@ impl Config { set(&mut config.print_step_rusage, build.print_step_rusage); set(&mut config.patch_binaries_for_nix, build.patch_binaries_for_nix); - config.verbose = cmp::max(config.verbose, flags.verbose); + config.verbose = cmp::max(config.verbose, flags.verbose as usize); if let Some(install) = toml.install { config.prefix = install.prefix.map(PathBuf::from); @@ -1135,7 +1249,14 @@ impl Config { config.rustc_default_linker = rust.default_linker; config.musl_root = rust.musl_root.map(PathBuf::from); config.save_toolstates = rust.save_toolstates.map(PathBuf::from); - set(&mut config.deny_warnings, flags.deny_warnings.or(rust.deny_warnings)); + set( + &mut config.deny_warnings, + match flags.warnings { + Warnings::Deny => Some(true), + Warnings::Warn => Some(false), + Warnings::Default => rust.deny_warnings, + }, + ); set(&mut config.backtrace_on_ice, rust.backtrace_on_ice); set(&mut config.rust_verify_llvm_ir, rust.verify_llvm_ir); config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit; @@ -1299,6 +1420,7 @@ impl Config { target.qemu_rootfs = cfg.qemu_rootfs.map(PathBuf::from); target.sanitizers = cfg.sanitizers; target.profiler = cfg.profiler; + target.rpath = cfg.rpath; config.target_config.insert(TargetSelection::from_user(&triple), target); } @@ -1307,7 +1429,7 @@ impl Config { if config.llvm_from_ci { let triple = &config.build.triple; let ci_llvm_bin = config.ci_llvm_root().join("bin"); - let mut build_target = config + let build_target = config .target_config .entry(config.build) .or_insert_with(|| Target::from_triple(&triple)); @@ -1440,6 +1562,28 @@ impl Config { git } + pub(crate) fn test_args(&self) -> Vec<&str> { + let mut test_args = match self.cmd { + Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => { + test_args.iter().flat_map(|s| s.split_whitespace()).collect() + } + _ => vec![], + }; + test_args.extend(self.free_args.iter().map(|s| s.as_str())); + test_args + } + + pub(crate) fn args(&self) -> Vec<&str> { + let mut args = match self.cmd { + Subcommand::Run { ref args, .. } => { + args.iter().flat_map(|s| s.split_whitespace()).collect() + } + _ => vec![], + }; + args.extend(self.free_args.iter().map(|s| s.as_str())); + args + } + /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI. /// Return the version it would have used for the given commit. pub(crate) fn artifact_version_part(&self, commit: &str) -> String { @@ -1608,6 +1752,10 @@ impl Config { self.target_config.values().any(|t| t.profiler == Some(true)) || self.profiler } + pub fn rpath_enabled(&self, target: TargetSelection) -> bool { + self.target_config.get(&target).map(|t| t.rpath).flatten().unwrap_or(self.rust_rpath) + } + pub fn llvm_enabled(&self) -> bool { self.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) } @@ -1632,6 +1780,42 @@ impl Config { self.rust_codegen_backends.get(0).cloned() } + pub fn check_build_rustc_version(&self) { + if self.dry_run() { + return; + } + + // check rustc version is same or lower with 1 apart from the building one + let mut cmd = Command::new(&self.initial_rustc); + cmd.arg("--version"); + let rustc_output = output(&mut cmd) + .lines() + .next() + .unwrap() + .split(' ') + .nth(1) + .unwrap() + .split('-') + .next() + .unwrap() + .to_owned(); + let rustc_version = Version::parse(&rustc_output.trim()).unwrap(); + let source_version = + Version::parse(&fs::read_to_string(self.src.join("src/version")).unwrap().trim()) + .unwrap(); + if !(source_version == rustc_version + || (source_version.major == rustc_version.major + && source_version.minor == rustc_version.minor + 1)) + { + 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 + ); + crate::detail_exit(1); + } + } + /// Returns the commit to download, or `None` if we shouldn't download CI artifacts. fn download_ci_rustc_commit(&self, download_rustc: Option<StringOrBool>) -> Option<String> { // If `download-rustc` is not set, default to rebuilding. |