use std::cmp;
use std::collections::{HashMap, HashSet};
use std::env;
-use std::ffi::OsString;
use std::fmt;
use std::fs;
use std::path::{Path, PathBuf};
use crate::channel::GitInfo;
pub use crate::flags::Subcommand;
use crate::flags::{Color, Flags};
-use crate::util::exe;
-use build_helper::t;
+use crate::util::{exe, t};
use serde::Deserialize;
macro_rules! check_ci_llvm {
// We are using a decl macro instead of a derive proc macro here to reduce the compile time of
// rustbuild.
-macro_rules! derive_merge {
+macro_rules! define_config {
($(#[$attr:meta])* struct $name:ident {
$($field:ident: $field_ty:ty,)*
}) => {
$(#[$attr])*
+ #[derive(Deserialize)]
+ #[serde(deny_unknown_fields, rename_all = "kebab-case")]
struct $name {
$($field: $field_ty,)*
}
}
}
-derive_merge! {
+define_config! {
/// TOML representation of various global build decisions.
- #[derive(Deserialize, Default)]
- #[serde(deny_unknown_fields, rename_all = "kebab-case")]
+ #[derive(Default)]
struct Build {
build: Option<String>,
host: Option<Vec<String>>,
target: Option<Vec<String>>,
- // This is ignored, the rust code always gets the build directory from the `BUILD_DIR` env variable
build_dir: Option<String>,
cargo: Option<String>,
rustc: Option<String>,
}
}
-derive_merge! {
+define_config! {
/// TOML representation of various global install decisions.
- #[derive(Deserialize)]
- #[serde(deny_unknown_fields, rename_all = "kebab-case")]
struct Install {
prefix: Option<String>,
sysconfdir: Option<String>,
}
}
-derive_merge! {
+define_config! {
/// TOML representation of how the LLVM build is configured.
- #[derive(Deserialize)]
- #[serde(deny_unknown_fields, rename_all = "kebab-case")]
struct Llvm {
skip_rebuild: Option<bool>,
optimize: Option<bool>,
}
}
-derive_merge! {
- #[derive(Deserialize)]
- #[serde(deny_unknown_fields, rename_all = "kebab-case")]
+define_config! {
struct Dist {
sign_folder: Option<String>,
gpg_password_file: Option<String>,
}
}
-derive_merge! {
+define_config! {
/// TOML representation of how the Rust build is configured.
- #[derive(Deserialize)]
- #[serde(deny_unknown_fields, rename_all = "kebab-case")]
struct Rust {
optimize: Option<bool>,
debug: Option<bool>,
}
}
-derive_merge! {
+define_config! {
/// TOML representation of how each build target is configured.
- #[derive(Deserialize)]
- #[serde(deny_unknown_fields, rename_all = "kebab-case")]
struct TomlTarget {
cc: Option<String>,
cxx: Option<String>,
}
impl Config {
- fn path_from_python(var_key: &str) -> PathBuf {
- match env::var_os(var_key) {
- Some(var_val) => Self::normalize_python_path(var_val),
- _ => panic!("expected '{}' to be set", var_key),
- }
- }
-
- /// Normalizes paths from Python slightly. We don't trust paths from Python (#49785).
- fn normalize_python_path(path: OsString) -> PathBuf {
- Path::new(&path).components().collect()
- }
-
pub fn default_opts() -> Config {
let mut config = Config::default();
config.llvm_optimize = true;
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
// Undo `src/bootstrap`
config.src = manifest_dir.parent().unwrap().parent().unwrap().to_owned();
- config.out = Config::path_from_python("BUILD_DIR");
+ config.out = PathBuf::from("build");
config.initial_cargo = PathBuf::from(env!("CARGO"));
config.initial_rustc = PathBuf::from(env!("RUSTC"));
config.llvm_profile_use = flags.llvm_profile_use;
config.llvm_profile_generate = flags.llvm_profile_generate;
- if config.dry_run {
- let dir = config.out.join("tmp-dry-run");
- t!(fs::create_dir_all(&dir));
- config.out = dir;
- }
-
#[cfg(test)]
let get_toml = |_| TomlConfig::default();
#[cfg(not(test))]
let get_toml = |file: &Path| {
use std::process;
- let contents = t!(fs::read_to_string(file), "`include` config not found");
+ let contents =
+ t!(fs::read_to_string(file), format!("config file {} not found", file.display()));
match toml::from_str(&contents) {
Ok(table) => table,
Err(err) => {
}
};
- let mut toml = flags.config.as_deref().map(get_toml).unwrap_or_else(TomlConfig::default);
+ // Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`, then `config.toml` in the root directory.
+ let toml_path = flags
+ .config
+ .clone()
+ .or_else(|| env::var_os("RUST_BOOTSTRAP_CONFIG").map(PathBuf::from));
+ let using_default_path = toml_path.is_none();
+ let mut toml_path = toml_path.unwrap_or_else(|| PathBuf::from("config.toml"));
+ if using_default_path && !toml_path.exists() {
+ toml_path = config.src.join(toml_path);
+ }
+
+ // Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path,
+ // but not if `config.toml` hasn't been created.
+ let mut toml = if !using_default_path || toml_path.exists() {
+ get_toml(&toml_path)
+ } else {
+ TomlConfig::default()
+ };
+
if let Some(include) = &toml.profile {
let mut include_path = config.src.clone();
include_path.push("src");
}
config.changelog_seen = toml.changelog_seen;
- if let Some(cfg) = flags.config {
- config.config = cfg;
- }
+ config.config = toml_path;
let build = toml.build.unwrap_or_default();
+ set(&mut config.initial_rustc, build.rustc.map(PathBuf::from));
+ set(&mut config.out, build.build_dir.map(PathBuf::from));
+ // NOTE: Bootstrap spawns various commands with different working directories.
+ // To avoid writing to random places on the file system, `config.out` needs to be an absolute path.
+ if !config.out.is_absolute() {
+ // `canonicalize` requires the path to already exist. Use our vendored copy of `absolute` instead.
+ config.out = crate::util::absolute(&config.out);
+ }
+
+ if config.dry_run {
+ let dir = config.out.join("tmp-dry-run");
+ t!(fs::create_dir_all(&dir));
+ config.out = dir;
+ }
+
config.hosts = if let Some(arg_host) = flags.host {
arg_host
} else if let Some(file_host) = build.host {