diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 3f551dc119b5d..1d37d68c1d405 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -19,6 +19,7 @@ use crate::flags::{Color, Subcommand}; use crate::install; use crate::native; use crate::run; +use crate::setup; use crate::test; use crate::tool::{self, SourceType}; use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t}; @@ -433,8 +434,11 @@ impl<'a> ShouldRun<'a> { // single alias, which does not correspond to any on-disk path pub fn alias(mut self, alias: &str) -> Self { + // exceptional case for `Kind::Setup` because its `library` + // and `compiler` options would otherwise naively match with + // `compiler` and `library` folders respectively. assert!( - !self.builder.src.join(alias).exists(), + self.kind == Kind::Setup || !self.builder.src.join(alias).exists(), "use `builder.path()` for real paths: {}", alias ); @@ -758,8 +762,9 @@ impl<'a> Builder<'a> { run::CollectLicenseMetadata, run::GenerateCopyright, ), + Kind::Setup => describe!(setup::Profile), // These commands either don't use paths, or they're special-cased in Build::build() - Kind::Clean | Kind::Format | Kind::Setup => vec![], + Kind::Clean | Kind::Format => vec![], } } @@ -822,7 +827,11 @@ impl<'a> Builder<'a> { Subcommand::Install { ref paths } => (Kind::Install, &paths[..]), Subcommand::Run { ref paths, .. } => (Kind::Run, &paths[..]), Subcommand::Format { .. } => (Kind::Format, &[][..]), - Subcommand::Clean { .. } | Subcommand::Setup { .. } => { + Subcommand::Setup { profile: ref path } => ( + Kind::Setup, + path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)), + ), + Subcommand::Clean { .. } => { panic!() } }; diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 37a8eb884efb0..851cb5ecf4c26 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -143,7 +143,7 @@ pub enum Subcommand { args: Vec, }, Setup { - profile: Option, + profile: Option, }, } @@ -351,7 +351,7 @@ To learn more about a subcommand, run `./x.py -h`", // fn usage() let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! { - let config = Config::parse(&["build".to_string()]); + let config = Config::parse(&["setup".to_string()]); let build = Build::new(config); let paths = Builder::get_help(&build, subcommand); @@ -621,7 +621,7 @@ Arguments: } Kind::Setup => { let profile = if paths.len() > 1 { - println!("\nat most one profile can be passed to setup\n"); + eprintln!("\nerror: At most one profile can be passed to setup\n"); usage(1, &opts, verbose, &subcommand_help) } else if let Some(path) = paths.pop() { let profile_string = t!(path.into_os_string().into_string().map_err( diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index f0c9a948727f0..47fb4a38d05be 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -730,10 +730,6 @@ impl Build { return clean::clean(self, all); } - if let Subcommand::Setup { profile } = &self.config.cmd { - return setup::setup(&self.config, *profile); - } - // Download rustfmt early so that it can be used in rust-analyzer configs. let _ = &builder::Builder::new(&self).initial_rustfmt(); diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index c7f98a7d0d149..57426ce3d5109 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -1,3 +1,4 @@ +use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::Config; use crate::{t, VERSION}; use std::env::consts::EXE_SUFFIX; @@ -9,7 +10,7 @@ use std::process::Command; use std::str::FromStr; use std::{fmt, fs, io}; -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum Profile { Compiler, Codegen, @@ -48,6 +49,16 @@ impl Profile { } out } + + pub fn as_str(&self) -> &'static str { + match self { + Profile::Compiler => "compiler", + Profile::Codegen => "codegen", + Profile::Library => "library", + Profile::Tools => "tools", + Profile::User => "user", + } + } } impl FromStr for Profile { @@ -69,24 +80,58 @@ impl FromStr for Profile { impl fmt::Display for Profile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Profile::Compiler => write!(f, "compiler"), - Profile::Codegen => write!(f, "codegen"), - Profile::Library => write!(f, "library"), - Profile::User => write!(f, "user"), - Profile::Tools => write!(f, "tools"), + f.write_str(self.as_str()) + } +} + +impl Step for Profile { + type Output = (); + const DEFAULT: bool = true; + + fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { + for choice in Profile::all() { + run = run.alias(choice.as_str()); } + run + } + + fn make_run(run: RunConfig<'_>) { + // for Profile, `run.paths` will have 1 and only 1 element + // this is because we only accept at most 1 path from user input. + // If user calls `x.py setup` without arguments, the interactive TUI + // will guide user to provide one. + let profile = if run.paths.len() > 1 { + // HACK: `builder` runs this step with all paths if no path was passed. + t!(interactive_path()) + } else { + run.paths + .first() + .unwrap() + .assert_single_path() + .path + .as_path() + .as_os_str() + .to_str() + .unwrap() + .parse() + .unwrap() + }; + + run.builder.ensure(profile); + } + + fn run(self, builder: &Builder<'_>) { + setup(&builder.build.config, self) } } -pub fn setup(config: &Config, profile: Option) { - let profile = profile.unwrap_or_else(|| t!(interactive_path())); +pub fn setup(config: &Config, profile: Profile) { let stage_path = ["build", config.build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string()); if !rustup_installed() && profile != Profile::User { eprintln!("`rustup` is not installed; cannot link `stage1` toolchain"); - } else if stage_dir_exists(&stage_path[..]) { + } else if stage_dir_exists(&stage_path[..]) && !config.dry_run() { attempt_toolchain_link(&stage_path[..]); } @@ -104,7 +149,9 @@ pub fn setup(config: &Config, profile: Option) { Profile::User => &["dist", "build"], }; - t!(install_git_hook_maybe(&config)); + if !config.dry_run() { + t!(install_git_hook_maybe(&config)); + } println!(); @@ -144,6 +191,7 @@ fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) { changelog-seen = {}\n", profile, VERSION ); + t!(fs::write(path, settings)); let include_path = profile.include_path(&config.src);