diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index e861d520c534..559adf9860a2 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -38,10 +38,10 @@ version = "0.0.0" dependencies = [ "build_helper", "cc", + "clap", "cmake", "fd-lock", "filetime", - "getopts", "hex", "ignore", "libc", @@ -88,6 +88,40 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "once_cell", +] + +[[package]] +name = "clap_derive" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cmake" version = "0.1.48" @@ -257,15 +291,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - [[package]] name = "globset" version = "0.4.8" @@ -279,6 +304,12 @@ dependencies = [ "regex", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -425,6 +456,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + [[package]] name = "output_vt100" version = "0.1.3" @@ -452,6 +489,30 @@ dependencies = [ "yansi", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.46" @@ -527,9 +588,9 @@ checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "rustix" -version = "0.36.3" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1fbb4dfc4eb1d390c02df47760bb19a84bb80b301ecc947ab5406394d8223e" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" dependencies = [ "bitflags", "errno", @@ -666,12 +727,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - [[package]] name = "version_check" version = "0.9.4" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 663987f113c8..7e3f708d59a1 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -34,8 +34,8 @@ build_helper = { path = "../tools/build_helper" } cmake = "0.1.38" fd-lock = "3.0.8" filetime = "0.2" -getopts = "0.2.19" cc = "1.0.69" +clap = { version = "4.1.4", features = ["std", "usage", "help", "derive", "error-context"], default-features = false} libc = "0.2" hex = "0.4" object = { version = "0.29.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] } diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index b33fc02f49c2..36042bb34cd3 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -546,19 +546,24 @@ impl<'a> ShouldRun<'a> { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, clap::ValueEnum)] pub enum Kind { + #[clap(aliases = ["b"])] Build, + #[clap(aliases = ["c"])] Check, Clippy, Fix, Format, + #[clap(aliases = ["t"])] Test, Bench, + #[clap(aliases = ["d"])] Doc, Clean, Dist, Install, + #[clap(aliases = ["r"])] Run, Setup, } @@ -846,18 +851,19 @@ impl<'a> Builder<'a> { } pub fn new(build: &Build) -> Builder<'_> { + let paths = &build.config.paths; let (kind, paths) = match build.config.cmd { - Subcommand::Build { ref paths } => (Kind::Build, &paths[..]), - Subcommand::Check { ref paths } => (Kind::Check, &paths[..]), - Subcommand::Clippy { ref paths, .. } => (Kind::Clippy, &paths[..]), - Subcommand::Fix { ref paths } => (Kind::Fix, &paths[..]), - Subcommand::Doc { ref paths, .. } => (Kind::Doc, &paths[..]), - Subcommand::Test { ref paths, .. } => (Kind::Test, &paths[..]), - Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]), - Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]), - Subcommand::Install { ref paths } => (Kind::Install, &paths[..]), - Subcommand::Run { ref paths, .. } => (Kind::Run, &paths[..]), - Subcommand::Clean { ref paths, .. } => (Kind::Clean, &paths[..]), + Subcommand::Build => (Kind::Build, &paths[..]), + Subcommand::Check { .. } => (Kind::Check, &paths[..]), + Subcommand::Clippy { .. } => (Kind::Clippy, &paths[..]), + Subcommand::Fix => (Kind::Fix, &paths[..]), + Subcommand::Doc { .. } => (Kind::Doc, &paths[..]), + Subcommand::Test { .. } => (Kind::Test, &paths[..]), + Subcommand::Bench { .. } => (Kind::Bench, &paths[..]), + Subcommand::Dist => (Kind::Dist, &paths[..]), + Subcommand::Install => (Kind::Install, &paths[..]), + Subcommand::Run { .. } => (Kind::Run, &paths[..]), + Subcommand::Clean { .. } => (Kind::Clean, &paths[..]), Subcommand::Format { .. } => (Kind::Format, &[][..]), Subcommand::Setup { profile: ref path } => ( Kind::Setup, diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index 3574f11189ee..2f1a1649c852 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -236,7 +236,7 @@ mod defaults { fn doc_default() { let mut config = configure("doc", &["A"], &["A"]); config.compiler_docs = true; - config.cmd = Subcommand::Doc { paths: Vec::new(), open: false, json: false }; + config.cmd = Subcommand::Doc { open: false, json: false }; let mut cache = run_build(&[], config); let a = TargetSelection::from_user("A"); @@ -545,12 +545,13 @@ mod dist { fn test_with_no_doc_stage0() { let mut config = configure(&["A"], &["A"]); config.stage = 0; + config.paths = vec!["library/std".into()]; config.cmd = Subcommand::Test { - paths: vec!["library/std".into()], test_args: vec![], rustc_args: vec![], - fail_fast: true, - doc_tests: DocTests::No, + no_fail_fast: false, + no_doc: true, + doc: false, bless: false, force_rerun: false, compare_mode: None, @@ -558,6 +559,7 @@ mod dist { pass: None, run: None, only_modified: false, + skip: vec![], }; let build = Build::new(config); @@ -588,7 +590,7 @@ mod dist { fn doc_ci() { let mut config = configure(&["A"], &["A"]); config.compiler_docs = true; - config.cmd = Subcommand::Doc { paths: Vec::new(), open: false, json: false }; + config.cmd = Subcommand::Doc { open: false, json: false }; let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]); @@ -617,11 +619,11 @@ mod dist { // Behavior of `x.py test` doing various documentation tests. let mut config = configure(&["A"], &["A"]); config.cmd = Subcommand::Test { - paths: vec![], test_args: vec![], rustc_args: vec![], - fail_fast: true, - doc_tests: DocTests::Yes, + no_fail_fast: false, + doc: true, + no_doc: false, bless: false, force_rerun: false, compare_mode: None, @@ -629,6 +631,7 @@ mod dist { pass: None, run: None, only_modified: false, + skip: vec![], }; // Make sure rustfmt binary not being found isn't an error. config.channel = "beta".to_string(); diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index cd19667139ab..4f6f98ae6033 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -20,15 +20,7 @@ fn args(builder: &Builder<'_>) -> Vec { arr.iter().copied().map(String::from) } - if let Subcommand::Clippy { - fix, - clippy_lint_allow, - clippy_lint_deny, - clippy_lint_warn, - clippy_lint_forbid, - .. - } = &builder.config.cmd - { + if let Subcommand::Clippy { fix, allow, deny, warn, forbid, .. } = &builder.config.cmd { // disable the most spammy clippy lints let ignored_lints = vec![ "many_single_char_names", // there are a lot in stdarch @@ -53,10 +45,10 @@ fn args(builder: &Builder<'_>) -> Vec { args.extend(strings(&["--", "--cap-lints", "warn"])); args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint))); let mut clippy_lint_levels: Vec = Vec::new(); - clippy_lint_allow.iter().for_each(|v| clippy_lint_levels.push(format!("-A{}", v))); - clippy_lint_deny.iter().for_each(|v| clippy_lint_levels.push(format!("-D{}", v))); - clippy_lint_warn.iter().for_each(|v| clippy_lint_levels.push(format!("-W{}", v))); - clippy_lint_forbid.iter().for_each(|v| clippy_lint_levels.push(format!("-F{}", v))); + allow.iter().for_each(|v| clippy_lint_levels.push(format!("-A{}", v))); + deny.iter().for_each(|v| clippy_lint_levels.push(format!("-D{}", v))); + warn.iter().for_each(|v| clippy_lint_levels.push(format!("-W{}", v))); + forbid.iter().for_each(|v| clippy_lint_levels.push(format!("-F{}", v))); args.extend(clippy_lint_levels); args.extend(builder.config.free_args.clone()); args diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index bf1aff7b72f4..82d991735c30 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -232,6 +232,8 @@ pub struct Config { pub initial_rustfmt: RefCell, pub out: PathBuf, pub rust_info: channel::GitInfo, + + pub paths: Vec, } #[derive(Default, Deserialize)] @@ -367,6 +369,12 @@ pub struct TargetSelection { file: Option>, } +impl<'a> From<&'a str> for TargetSelection { + fn from(s: &'a str) -> Self { + Self::from_user(s) + } +} + impl TargetSelection { pub fn from_user(selection: &str) -> Self { let path = Path::new(selection); @@ -855,25 +863,27 @@ impl Config { } fn parse_inner<'a>(args: &[String], get_toml: impl 'a + Fn(&Path) -> TomlConfig) -> Config { - let flags = Flags::parse(&args); + let mut flags = Flags::parse(&args); let mut config = Config::default_opts(); // Set flags. + config.paths = std::mem::take(&mut flags.paths); + config.free_args = std::mem::take(&mut flags.free_args); 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.rustc_error_format = flags.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 = flags.free_args.clone(); + if matches!(flags.deny_warnings, crate::flags::Warnings::Deny) { + config.deny_warnings = true; } config.llvm_profile_use = flags.llvm_profile_use; config.llvm_profile_generate = flags.llvm_profile_generate; @@ -1046,7 +1056,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); @@ -1119,7 +1129,6 @@ 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.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; diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs index 5a105007f21f..e3587073ad99 100644 --- a/src/bootstrap/config/tests.rs +++ b/src/bootstrap/config/tests.rs @@ -1,4 +1,6 @@ -use super::{Config, TomlConfig}; +use clap::CommandFactory; + +use super::{Config, Flags, TomlConfig}; use std::path::Path; fn toml(config: &str) -> impl '_ + Fn(&Path) -> TomlConfig { @@ -33,4 +35,9 @@ fn download_ci_llvm() { )); } +#[test] +fn clap_verify() { + Flags::command().debug_assert(); +} + // FIXME: add test for detecting `src` and `out` diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 2b0b772a6181..3755d18a2767 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -3,719 +3,385 @@ //! This module implements the command-line parsing of the build system which //! has various flags to configure how it's run. +use clap::{Parser, ValueEnum}; use std::path::PathBuf; -use getopts::Options; - use crate::builder::{Builder, Kind}; -use crate::config::{Config, TargetSelection}; use crate::setup::Profile; -use crate::util::t; -use crate::{Build, DocTests}; +use crate::{Build, Config, DocTests, TargetSelection}; -#[derive(Copy, Clone)] +/// Whether or not color should be present in the output +#[derive(Copy, Clone, Default, Debug, ValueEnum)] pub enum Color { Always, Never, + #[default] Auto, } -impl Default for Color { - fn default() -> Self { - Self::Auto - } +/// Whether to deny warnings or use the default behavior +#[derive(Copy, Clone, Default, Debug, ValueEnum)] +pub enum Warnings { + Deny, + #[default] + Default, } +#[derive(Debug, Parser)] +/// Parsed version of command line flags using [`clap::Parser`] +#[clap( + override_usage = "x.py [options] [...]", + disable_help_subcommand(true), + about = "", + next_line_help(false) +)] +pub struct Flags { + #[command(subcommand)] + pub cmd: Subcommand, -impl std::str::FromStr for Color { - type Err = (); - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "always" => Ok(Self::Always), - "never" => Ok(Self::Never), - "auto" => Ok(Self::Auto), - _ => Err(()), - } - } -} + #[arg(global(true), short, long, action = clap::ArgAction::Count)] + /// use verbose output (-vv for very verbose) + pub verbose: u8, // each extra -v after the first is passed to Cargo + #[arg(global(true), short, long)] + /// use incremental compilation + pub incremental: bool, + #[arg(global(true), long, value_hint = clap::ValueHint::FilePath, value_name = "FILE")] + /// TOML configuration file for build + pub config: Option, + #[arg(global(true), long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")] + /// Build directory, overrides `build.build-dir` in `config.toml` + pub build_dir: Option, -/// Deserialized version of all flags for this compile. -pub struct Flags { - pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo - pub on_fail: Option, - pub stage: Option, - pub keep_stage: Vec, - pub keep_stage_std: Vec, + #[arg(global(true), long, value_name = "BUILD")] + /// build target of the stage0 compiler + pub build: Option, + #[arg(global(true), long, value_name = "HOST")] + /// host targets to build pub host: Option>, + + #[arg(global(true), long, value_name = "TARGET")] + /// target targets to build pub target: Option>, - pub config: Option, - pub build_dir: Option, - pub jobs: Option, - pub cmd: Subcommand, - pub incremental: bool, + + #[arg(global(true), long, value_name = "PATH", value_hint = clap::ValueHint::DirPath)] + /// build paths to exclude pub exclude: Vec, + #[arg(global(true), long)] + /// include default paths in addition to the provided ones pub include_default_paths: bool, - pub rustc_error_format: Option, - pub json_output: bool, + + #[arg(global(true), long, value_hint = clap::ValueHint::CommandString, value_name = "CMD")] + /// command to run on failure + pub on_fail: Option, + #[arg(global(true), long)] + /// dry run; don't build anything pub dry_run: bool, - pub color: Color, + #[arg(global(true), long, value_name = "N")] + /// stage to build (indicates compiler to use/test, e.g., stage 0 uses the + /// bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.) + pub stage: Option, + #[arg(global(true), long, value_name = "N")] + /// stage(s) to keep without recompiling + /// (pass multiple times to keep e.g., both stages 0 and 1) + pub keep_stage: Vec, + #[arg(global(true), long, value_name = "N")] + /// stage(s) of the standard library to keep without recompiling + /// (pass multiple times to keep e.g., both stages 0 and 1) + pub keep_stage_std: Vec, + #[arg(global(true), long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")] + /// path to the root of the rust checkout + pub src: Option, + + #[arg( + global(true), + short, + long, + default_value_t = std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get), + value_name = "JOBS" + )] + /// number of jobs to run in parallel + pub jobs: usize, // This overrides the deny-warnings configuration option, // which passes -Dwarnings to the compiler invocations. - // - // true => deny, false => warn - pub deny_warnings: Option, + #[arg(global(true), long)] + #[clap(value_enum, default_value_t=Warnings::Default, value_name = "deny")] + /// if value is deny, will deny warnings, otherwise use default + pub deny_warnings: Warnings, + + #[arg(global(true), long, value_name = "FORMAT")] + /// rustc error format + pub error_format: Option, + #[arg(global(true), long)] + /// use message-format=json + pub json_output: bool, - pub rust_profile_use: Option, - pub rust_profile_generate: Option, + #[arg(global(true), long, value_name = "STYLE")] + #[clap(value_enum, default_value_t = Color::Auto)] + /// whether to use color in cargo and rustc output + pub color: Color, + /// whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml + #[arg(global(true), long, value_name = "VALUE")] + pub llvm_skip_rebuild: Option, + /// generate PGO profile with rustc build + #[arg(global(true), long, value_name = "PROFILE")] + pub rust_profile_generate: Option, + /// use PGO profile for rustc build + #[arg(global(true), long, value_name = "PROFILE")] + pub rust_profile_use: Option, + /// use PGO profile for LLVM build + #[arg(global(true), long, value_name = "PROFILE")] pub llvm_profile_use: Option, // LLVM doesn't support a custom location for generating profile // information. // // llvm_out/build/profiles/ is the location this writes to. + /// generate PGO profile with llvm built for rustc + #[arg(global(true), long)] pub llvm_profile_generate: bool, + /// generate BOLT profile for LLVM build + #[arg(global(true), long)] pub llvm_bolt_profile_generate: bool, + /// use BOLT profile for LLVM build + #[arg(global(true), long, value_name = "PROFILE")] pub llvm_bolt_profile_use: Option, + #[arg(global(true))] + /// paths for the subcommand + pub paths: Vec, + /// arguments passed to subcommands + #[arg(global(true), last(true), value_name = "ARGS")] + pub free_args: Vec, +} - /// Arguments appearing after `--` to be forwarded to tools, - /// e.g. `--fix-broken` or test arguments. - pub free_args: Option>, +impl Flags { + pub fn parse(args: &[String]) -> Self { + let first = String::from("x.py"); + let it = std::iter::once(&first).chain(args.iter()); + // We need to check for ` -h -v`, in which case we list the paths + #[derive(Parser)] + #[clap(disable_help_flag(true))] + struct HelpVerboseOnly { + #[arg(short, long)] + help: bool, + #[arg(global(true), short, long, action = clap::ArgAction::Count)] + pub verbose: u8, + #[arg(value_enum)] + cmd: Kind, + } + if let Ok(HelpVerboseOnly { help: true, verbose: 1.., cmd: subcommand }) = + HelpVerboseOnly::try_parse_from(it.clone()) + { + println!("note: updating submodules before printing available paths"); + let config = Config::parse(&[String::from("build")]); + let build = Build::new(config); + let paths = Builder::get_help(&build, subcommand); + if let Some(s) = paths { + println!("{}", s); + } else { + panic!("No paths available for subcommand `{}`", subcommand.as_str()); + } + crate::detail_exit(0); + } + + Flags::parse_from(it) + } } -#[derive(Debug)] -#[cfg_attr(test, derive(Clone))] +#[derive(Debug, Clone, clap::Subcommand)] pub enum Subcommand { - Build { - paths: Vec, - }, + #[clap(aliases = ["b"], long_about = "\n + Arguments: + This subcommand accepts a number of paths to directories to the crates + and/or artifacts to compile. For example, for a quick build of a usable + compiler: + ./x.py build --stage 1 library/std + This will build a compiler and standard library from the local source code. + Once this is done, build/$ARCH/stage1 contains a usable compiler. + If no arguments are passed then the default artifacts for that stage are + compiled. For example: + ./x.py build --stage 0 + ./x.py build ")] + /// Compile either the compiler or libraries + Build, + #[clap(aliases = ["c"], long_about = "\n + Arguments: + This subcommand accepts a number of paths to directories to the crates + and/or artifacts to compile. For example: + ./x.py check library/std + If no arguments are passed then many artifacts are checked.")] + /// Compile either the compiler or libraries, using cargo check Check { - paths: Vec, + #[arg(long)] + /// Check all targets + all_targets: bool, }, + /// Run clippy (uses rustup/cargo-installed clippy binary) + #[clap(long_about = "\n + Arguments: + This subcommand accepts a number of paths to directories to the crates + and/or artifacts to run clippy against. For example: + ./x.py clippy library/core + ./x.py clippy library/core library/proc_macro")] Clippy { + #[arg(long)] fix: bool, - paths: Vec, - clippy_lint_allow: Vec, - clippy_lint_deny: Vec, - clippy_lint_warn: Vec, - clippy_lint_forbid: Vec, - }, - Fix { - paths: Vec, + /// clippy lints to allow + #[arg(global(true), short = 'A', action = clap::ArgAction::Append, value_name = "LINT")] + allow: Vec, + /// clippy lints to deny + #[arg(global(true), short = 'D', action = clap::ArgAction::Append, value_name = "LINT")] + deny: Vec, + /// clippy lints to warn on + #[arg(global(true), short = 'W', action = clap::ArgAction::Append, value_name = "LINT")] + warn: Vec, + /// clippy lints to forbid + #[arg(global(true), short = 'F', action = clap::ArgAction::Append, value_name = "LINT")] + forbid: Vec, }, + /// Run cargo fix + #[clap(long_about = "\n + Arguments: + This subcommand accepts a number of paths to directories to the crates + and/or artifacts to run `cargo fix` against. For example: + ./x.py fix library/core + ./x.py fix library/core library/proc_macro")] + Fix, + #[clap( + name = "fmt", + long_about = "\n + Arguments: + This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and + fails if it is not. For example: + ./x.py fmt + ./x.py fmt --check" + )] + /// Run rustfmt Format { - paths: Vec, + /// check formatting instead of applying + #[arg(long)] check: bool, }, + #[clap(aliases = ["d"], long_about = "\n + Arguments: + This subcommand accepts a number of paths to directories of documentation + to build. For example: + ./x.py doc src/doc/book + ./x.py doc src/doc/nomicon + ./x.py doc src/doc/book library/std + ./x.py doc library/std --json + ./x.py doc library/std --open + If no arguments are passed then everything is documented: + ./x.py doc + ./x.py doc --stage 1")] + /// Build documentation Doc { - paths: Vec, + #[arg(long)] + /// open the docs in a browser open: bool, + #[arg(long)] + /// render the documentation in JSON format in addition to the usual HTML format json: bool, }, + #[clap(aliases = ["t"], long_about = "\n + Arguments: + This subcommand accepts a number of paths to test directories that + should be compiled and run. For example: + ./x.py test tests/ui + ./x.py test library/std --test-args hash_map + ./x.py test library/std --stage 0 --no-doc + ./x.py test tests/ui --bless + ./x.py test tests/ui --compare-mode chalk + Note that `test tests/* --stage N` does NOT depend on `build compiler/rustc --stage N`; + just like `build library/std --stage N` it tests the compiler produced by the previous + stage. + Execute tool tests with a tool name argument: + ./x.py test tidy + If no arguments are passed then the complete artifacts for that stage are + compiled and tested. + ./x.py test + ./x.py test --stage 1")] + /// Build and run some test suites Test { - paths: Vec, + #[arg(long)] + /// Run all tests regardless of failure + no_fail_fast: bool, + #[arg(long, value_name = "SUBSTRING")] + /// skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times + skip: Vec, + #[arg(long, value_name = "ARGS", allow_hyphen_values(true))] + /// extra arguments to be passed for the test tool being used + /// (e.g. libtest, compiletest or rustdoc) + test_args: Vec, + /// extra options to pass the compiler when running tests + #[arg(long, value_name = "ARGS", allow_hyphen_values(true))] + rustc_args: Vec, + #[arg(long)] + /// do not run doc tests + no_doc: bool, + #[arg(long)] + /// only run doc tests + doc: bool, + #[arg(long)] /// Whether to automatically update stderr/stdout files bless: bool, + #[arg(long)] + /// rerun tests even if the inputs are unchanged force_rerun: bool, + #[arg(long)] + /// only run tests that result has been changed + only_modified: bool, + #[arg(long, value_name = "COMPARE MODE")] + /// mode describing what file the actual ui output will be compared to compare_mode: Option, + #[arg(long, value_name = "check | build | run")] + /// force {check,build,run}-pass tests to this mode. pass: Option, + #[arg(long, value_name = "auto | always | never")] + /// whether to execute run-* tests run: Option, - test_args: Vec, - rustc_args: Vec, - fail_fast: bool, - doc_tests: DocTests, + #[arg(long)] + /// enable this to generate a Rustfix coverage file, which is saved in + /// `//rustfix_missing_coverage.txt` rustfix_coverage: bool, - only_modified: bool, }, + /// Build and run some benchmarks Bench { - paths: Vec, + #[arg(long, allow_hyphen_values(true))] test_args: Vec, }, + /// Clean out build directories Clean { - paths: Vec, + #[arg(long)] all: bool, }, - Dist { - paths: Vec, - }, - Install { - paths: Vec, - }, + /// Build distribution artifacts + Dist, + /// Install distribution artifacts + Install, + #[clap(aliases = ["r"], long_about = "\n + Arguments: + This subcommand accepts a number of paths to tools to build and run. For + example: + ./x.py run src/tools/expand-yaml-anchors + At least a tool needs to be called.")] + /// Run tools contained in this repository Run { - paths: Vec, + /// arguments for the tool + #[arg(long, allow_hyphen_values(true))] args: Vec, }, - Setup { - profile: Option, - }, -} - -impl Default for Subcommand { - fn default() -> Subcommand { - Subcommand::Build { paths: vec![PathBuf::from("nowhere")] } - } -} - -impl Flags { - pub fn parse(args: &[String]) -> Flags { - let (args, free_args) = if let Some(pos) = args.iter().position(|s| s == "--") { - let (args, free) = args.split_at(pos); - (args, Some(free[1..].to_vec())) - } else { - (args, None) - }; - let mut subcommand_help = String::from( - "\ -Usage: x.py [options] [...] - -Subcommands: - build, b Compile either the compiler or libraries - check, c Compile either the compiler or libraries, using cargo check - clippy Run clippy (uses rustup/cargo-installed clippy binary) - fix Run cargo fix - fmt Run rustfmt - test, t Build and run some test suites - bench Build and run some benchmarks - doc, d Build documentation - clean Clean out build directories - dist Build distribution artifacts - install Install distribution artifacts - run, r Run tools contained in this repository - setup Create a config.toml (making it easier to use `x.py` itself) - -To learn more about a subcommand, run `./x.py -h`", - ); - - let mut opts = Options::new(); - // Options common to all subcommands - opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)"); - opts.optflag("i", "incremental", "use incremental compilation"); - opts.optopt("", "config", "TOML configuration file for build", "FILE"); - opts.optopt( - "", - "build-dir", - "Build directory, overrides `build.build-dir` in `config.toml`", - "DIR", - ); - opts.optopt("", "build", "build target of the stage0 compiler", "BUILD"); - opts.optmulti("", "host", "host targets to build", "HOST"); - opts.optmulti("", "target", "target targets to build", "TARGET"); - opts.optmulti("", "exclude", "build paths to exclude", "PATH"); - opts.optflag( - "", - "include-default-paths", - "include default paths in addition to the provided ones", - ); - opts.optopt("", "on-fail", "command to run on failure", "CMD"); - opts.optflag("", "dry-run", "dry run; don't build anything"); - opts.optopt( - "", - "stage", - "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \ - bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)", - "N", - ); - opts.optmulti( - "", - "keep-stage", - "stage(s) to keep without recompiling \ - (pass multiple times to keep e.g., both stages 0 and 1)", - "N", - ); - opts.optmulti( - "", - "keep-stage-std", - "stage(s) of the standard library to keep without recompiling \ - (pass multiple times to keep e.g., both stages 0 and 1)", - "N", - ); - opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); - let j_msg = format!( - "number of jobs to run in parallel; \ - defaults to {} (this host's logical CPU count)", - std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) - ); - opts.optopt("j", "jobs", &j_msg, "JOBS"); - opts.optflag("h", "help", "print this help message"); - opts.optopt( - "", - "warnings", - "if value is deny, will deny warnings, otherwise use default", - "VALUE", - ); - opts.optopt("", "error-format", "rustc error format", "FORMAT"); - opts.optflag("", "json-output", "use message-format=json"); - opts.optopt("", "color", "whether to use color in cargo and rustc output", "STYLE"); - opts.optopt( - "", - "rust-profile-generate", - "generate PGO profile with rustc build", - "PROFILE", - ); - opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "PROFILE"); - opts.optflag("", "llvm-profile-generate", "generate PGO profile with llvm built for rustc"); - opts.optopt("", "llvm-profile-use", "use PGO profile for llvm build", "PROFILE"); - opts.optmulti("A", "", "allow certain clippy lints", "OPT"); - opts.optmulti("D", "", "deny certain clippy lints", "OPT"); - opts.optmulti("W", "", "warn about certain clippy lints", "OPT"); - opts.optmulti("F", "", "forbid certain clippy lints", "OPT"); - opts.optflag("", "llvm-bolt-profile-generate", "generate BOLT profile for LLVM build"); - opts.optopt("", "llvm-bolt-profile-use", "use BOLT profile for LLVM build", "PROFILE"); - - // We can't use getopt to parse the options until we have completed specifying which - // options are valid, but under the current implementation, some options are conditional on - // the subcommand. Therefore we must manually identify the subcommand first, so that we can - // complete the definition of the options. Then we can use the getopt::Matches object from - // there on out. - let subcommand = match args.iter().find_map(|s| Kind::parse(&s)) { - Some(s) => s, - None => { - // No or an invalid subcommand -- show the general usage and subcommand help - // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid - // subcommand. - println!("{}\n", subcommand_help); - let exit_code = if args.is_empty() { 0 } else { 1 }; - crate::detail_exit(exit_code); - } - }; - - // Some subcommands get extra options - match subcommand { - Kind::Test => { - opts.optflag("", "no-fail-fast", "Run all tests regardless of failure"); - opts.optmulti("", "skip", "skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times", "SUBSTRING"); - opts.optmulti( - "", - "test-args", - "extra arguments to be passed for the test tool being used \ - (e.g. libtest, compiletest or rustdoc)", - "ARGS", - ); - opts.optmulti( - "", - "rustc-args", - "extra options to pass the compiler when running tests", - "ARGS", - ); - opts.optflag("", "no-doc", "do not run doc tests"); - opts.optflag("", "doc", "only run doc tests"); - opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests"); - opts.optflag("", "force-rerun", "rerun tests even if the inputs are unchanged"); - opts.optflag("", "only-modified", "only run tests that result has been changed"); - opts.optopt( - "", - "compare-mode", - "mode describing what file the actual ui output will be compared to", - "COMPARE MODE", - ); - opts.optopt( - "", - "pass", - "force {check,build,run}-pass tests to this mode.", - "check | build | run", - ); - opts.optopt("", "run", "whether to execute run-* tests", "auto | always | never"); - opts.optflag( - "", - "rustfix-coverage", - "enable this to generate a Rustfix coverage file, which is saved in \ - `//rustfix_missing_coverage.txt`", - ); - } - Kind::Check => { - opts.optflag("", "all-targets", "Check all targets"); - } - Kind::Bench => { - opts.optmulti("", "test-args", "extra arguments", "ARGS"); - } - Kind::Clippy => { - opts.optflag("", "fix", "automatically apply lint suggestions"); - } - Kind::Doc => { - opts.optflag("", "open", "open the docs in a browser"); - opts.optflag( - "", - "json", - "render the documentation in JSON format in addition to the usual HTML format", - ); - } - Kind::Clean => { - opts.optflag("", "all", "clean all build artifacts"); - } - Kind::Format => { - opts.optflag("", "check", "check formatting instead of applying."); - } - Kind::Run => { - opts.optmulti("", "args", "arguments for the tool", "ARGS"); - } - _ => {} - }; - - // fn usage() - let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! { - println!("{}", opts.usage(subcommand_help)); - if verbose { - // We have an unfortunate situation here: some Steps use `builder.in_tree_crates` to determine their paths. - // To determine those crates, we need to run `cargo metadata`, which means we need all submodules to be checked out. - // That takes a while to run, so only do it when paths were explicitly requested, not on all CLI errors. - // `Build::new` won't load submodules for the `setup` command. - let cmd = if verbose { - println!("note: updating submodules before printing available paths"); - "build" - } else { - "setup" - }; - let config = Config::parse(&[cmd.to_string()]); - let build = Build::new(config); - let paths = Builder::get_help(&build, subcommand); - - if let Some(s) = paths { - println!("{}", s); - } else { - panic!("No paths available for subcommand `{}`", subcommand.as_str()); - } - } else { - println!( - "Run `./x.py {} -h -v` to see a list of available paths.", - subcommand.as_str() - ); - } - crate::detail_exit(exit_code); - }; - - // Done specifying what options are possible, so do the getopts parsing - let matches = opts.parse(args).unwrap_or_else(|e| { - // Invalid argument/option format - println!("\n{}\n", e); - usage(1, &opts, false, &subcommand_help); - }); - - // Extra sanity check to make sure we didn't hit this crazy corner case: - // - // ./x.py --frobulate clean build - // ^-- option ^ ^- actual subcommand - // \_ arg to option could be mistaken as subcommand - let mut pass_sanity_check = true; - match matches.free.get(0).and_then(|s| Kind::parse(&s)) { - Some(check_subcommand) => { - if check_subcommand != subcommand { - pass_sanity_check = false; - } - } - None => { - pass_sanity_check = false; - } - } - if !pass_sanity_check { - eprintln!("{}\n", subcommand_help); - eprintln!( - "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\ - You may need to move some options to after the subcommand.\n" - ); - crate::detail_exit(1); - } - // Extra help text for some commands - match subcommand { - Kind::Build => { - subcommand_help.push_str( - "\n -Arguments: - This subcommand accepts a number of paths to directories to the crates - and/or artifacts to compile. For example, for a quick build of a usable - compiler: - - ./x.py build --stage 1 library/std - - This will build a compiler and standard library from the local source code. - Once this is done, build/$ARCH/stage1 contains a usable compiler. - - If no arguments are passed then the default artifacts for that stage are - compiled. For example: - - ./x.py build --stage 0 - ./x.py build ", - ); - } - Kind::Check => { - subcommand_help.push_str( - "\n -Arguments: - This subcommand accepts a number of paths to directories to the crates - and/or artifacts to compile. For example: - - ./x.py check library/std - - If no arguments are passed then many artifacts are checked.", - ); - } - Kind::Clippy => { - subcommand_help.push_str( - "\n -Arguments: - This subcommand accepts a number of paths to directories to the crates - and/or artifacts to run clippy against. For example: - - ./x.py clippy library/core - ./x.py clippy library/core library/proc_macro", - ); - } - Kind::Fix => { - subcommand_help.push_str( - "\n -Arguments: - This subcommand accepts a number of paths to directories to the crates - and/or artifacts to run `cargo fix` against. For example: - - ./x.py fix library/core - ./x.py fix library/core library/proc_macro", - ); - } - Kind::Format => { - subcommand_help.push_str( - "\n -Arguments: - This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and - fails if it is not. For example: - - ./x.py fmt - ./x.py fmt --check", - ); - } - Kind::Test => { - subcommand_help.push_str( - "\n -Arguments: - This subcommand accepts a number of paths to test directories that - should be compiled and run. For example: - - ./x.py test tests/ui - ./x.py test library/std --test-args hash_map - ./x.py test library/std --stage 0 --no-doc - ./x.py test tests/ui --bless - ./x.py test tests/ui --compare-mode chalk - - Note that `test tests/* --stage N` does NOT depend on `build compiler/rustc --stage N`; - just like `build library/std --stage N` it tests the compiler produced by the previous - stage. - - Execute tool tests with a tool name argument: - - ./x.py test tidy - - If no arguments are passed then the complete artifacts for that stage are - compiled and tested. - - ./x.py test - ./x.py test --stage 1", - ); - } - Kind::Doc => { - subcommand_help.push_str( - "\n -Arguments: - This subcommand accepts a number of paths to directories of documentation - to build. For example: - - ./x.py doc src/doc/book - ./x.py doc src/doc/nomicon - ./x.py doc src/doc/book library/std - ./x.py doc library/std --json - ./x.py doc library/std --open - - If no arguments are passed then everything is documented: - - ./x.py doc - ./x.py doc --stage 1", - ); - } - Kind::Run => { - subcommand_help.push_str( - "\n -Arguments: - This subcommand accepts a number of paths to tools to build and run. For - example: - - ./x.py run src/tools/expand-yaml-anchors - - At least a tool needs to be called.", - ); - } - Kind::Setup => { - subcommand_help.push_str(&format!( - "\n -x.py setup creates a `config.toml` which changes the defaults for x.py itself, -as well as setting up a git pre-push hook, VS code config and toolchain link. - + /// Create a config.toml (making it easier to use `x.py` itself) + #[clap(long_about = format!( + "\n +x.py setup creates a `config.toml` which changes the defaults for x.py itself. Arguments: This subcommand accepts a 'profile' to use for builds. For example: - - ./x.py setup library - + ./x.py setup library The profile is optional and you will be prompted interactively if it is not given. The following profiles are available: - -{} - - To only set up the git hook, VS code or toolchain link, you may use - ./x.py setup hook - ./x.py setup vscode - ./x.py setup link -", - Profile::all_for_help(" ").trim_end() - )); - } - Kind::Bench | Kind::Clean | Kind::Dist | Kind::Install => {} - }; - // Get any optional paths which occur after the subcommand - let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::>(); - - let verbose = matches.opt_present("verbose"); - - // User passed in -h/--help? - if matches.opt_present("help") { - usage(0, &opts, verbose, &subcommand_help); - } - - let cmd = match subcommand { - Kind::Build => Subcommand::Build { paths }, - Kind::Check => { - if matches.opt_present("all-targets") { - println!( - "Warning: --all-targets is now on by default and does not need to be passed explicitly." - ); - } - Subcommand::Check { paths } - } - Kind::Clippy => Subcommand::Clippy { - paths, - fix: matches.opt_present("fix"), - clippy_lint_allow: matches.opt_strs("A"), - clippy_lint_warn: matches.opt_strs("W"), - clippy_lint_deny: matches.opt_strs("D"), - clippy_lint_forbid: matches.opt_strs("F"), - }, - Kind::Fix => Subcommand::Fix { paths }, - Kind::Test => Subcommand::Test { - paths, - bless: matches.opt_present("bless"), - force_rerun: matches.opt_present("force-rerun"), - compare_mode: matches.opt_str("compare-mode"), - pass: matches.opt_str("pass"), - run: matches.opt_str("run"), - test_args: matches.opt_strs("test-args"), - rustc_args: matches.opt_strs("rustc-args"), - fail_fast: !matches.opt_present("no-fail-fast"), - rustfix_coverage: matches.opt_present("rustfix-coverage"), - only_modified: matches.opt_present("only-modified"), - doc_tests: if matches.opt_present("doc") { - DocTests::Only - } else if matches.opt_present("no-doc") { - DocTests::No - } else { - DocTests::Yes - }, - }, - Kind::Bench => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") }, - Kind::Doc => Subcommand::Doc { - paths, - open: matches.opt_present("open"), - json: matches.opt_present("json"), - }, - Kind::Clean => Subcommand::Clean { all: matches.opt_present("all"), paths }, - Kind::Format => Subcommand::Format { check: matches.opt_present("check"), paths }, - Kind::Dist => Subcommand::Dist { paths }, - Kind::Install => Subcommand::Install { paths }, - Kind::Run => { - if paths.is_empty() { - println!("\nrun requires at least a path!\n"); - usage(1, &opts, verbose, &subcommand_help); - } - Subcommand::Run { paths, args: matches.opt_strs("args") } - } - Kind::Setup => { - let profile = if paths.len() > 1 { - eprintln!("\nerror: At most one option 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( - |path| format!("{} is not a valid UTF8 string", path.to_string_lossy()) - )); - - let profile = profile_string.parse().unwrap_or_else(|err| { - eprintln!("error: {}", err); - eprintln!("help: the available profiles are:"); - eprint!("{}", Profile::all_for_help("- ")); - crate::detail_exit(1); - }); - Some(profile) - } else { - None - }; - Subcommand::Setup { profile } - } - }; - - Flags { - verbose: matches.opt_count("verbose"), - stage: matches.opt_str("stage").map(|j| j.parse().expect("`stage` should be a number")), - dry_run: matches.opt_present("dry-run"), - on_fail: matches.opt_str("on-fail"), - rustc_error_format: matches.opt_str("error-format"), - json_output: matches.opt_present("json-output"), - keep_stage: matches - .opt_strs("keep-stage") - .into_iter() - .map(|j| j.parse().expect("`keep-stage` should be a number")) - .collect(), - keep_stage_std: matches - .opt_strs("keep-stage-std") - .into_iter() - .map(|j| j.parse().expect("`keep-stage-std` should be a number")) - .collect(), - host: if matches.opt_present("host") { - Some( - split(&matches.opt_strs("host")) - .into_iter() - .map(|x| TargetSelection::from_user(&x)) - .collect::>(), - ) - } else { - None - }, - target: if matches.opt_present("target") { - Some( - split(&matches.opt_strs("target")) - .into_iter() - .map(|x| TargetSelection::from_user(&x)) - .collect::>(), - ) - } else { - None - }, - config: matches.opt_str("config").map(PathBuf::from), - build_dir: matches.opt_str("build-dir").map(PathBuf::from), - jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")), - cmd, - incremental: matches.opt_present("incremental"), - exclude: split(&matches.opt_strs("exclude")) - .into_iter() - .map(|p| p.into()) - .collect::>(), - include_default_paths: matches.opt_present("include-default-paths"), - deny_warnings: parse_deny_warnings(&matches), - color: matches - .opt_get_default("color", Color::Auto) - .expect("`color` should be `always`, `never`, or `auto`"), - rust_profile_use: matches.opt_str("rust-profile-use"), - rust_profile_generate: matches.opt_str("rust-profile-generate"), - llvm_profile_use: matches.opt_str("llvm-profile-use"), - llvm_profile_generate: matches.opt_present("llvm-profile-generate"), - llvm_bolt_profile_generate: matches.opt_present("llvm-bolt-profile-generate"), - llvm_bolt_profile_use: matches.opt_str("llvm-bolt-profile-use"), - free_args, - } - } +{}", Profile::all_for_help(" ").trim_end()))] + Setup { profile: Option }, } impl Subcommand { @@ -766,14 +432,22 @@ impl Subcommand { pub fn fail_fast(&self) -> bool { match *self { - Subcommand::Test { fail_fast, .. } => fail_fast, + Subcommand::Test { no_fail_fast, .. } => !no_fail_fast, _ => false, } } pub fn doc_tests(&self) -> DocTests { match *self { - Subcommand::Test { doc_tests, .. } => doc_tests, + Subcommand::Test { doc, no_doc, .. } => { + if doc { + DocTests::Only + } else if no_doc { + DocTests::No + } else { + DocTests::Yes + } + } _ => DocTests::Yes, } } @@ -842,18 +516,8 @@ impl Subcommand { } } -fn split(s: &[String]) -> Vec { - s.iter().flat_map(|s| s.split(',')).filter(|s| !s.is_empty()).map(|s| s.to_string()).collect() -} - -fn parse_deny_warnings(matches: &getopts::Matches) -> Option { - match matches.opt_str("warnings").as_deref() { - Some("deny") => Some(true), - Some("warn") => Some(false), - Some(value) => { - eprintln!(r#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,); - crate::detail_exit(1); - } - None => None, +impl Default for Subcommand { + fn default() -> Subcommand { + Subcommand::Build } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index f4abdf1cc575..41f008d23abc 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -28,6 +28,7 @@ use std::str; use build_helper::ci::CiEnv; use channel::GitInfo; +use clap::ValueEnum; use config::{DryRun, Target}; use filetime::FileTime; use once_cell::sync::OnceCell; @@ -157,7 +158,7 @@ pub struct Compiler { host: TargetSelection, } -#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[derive(PartialEq, Eq, Copy, Clone, Debug, ValueEnum)] pub enum DocTests { /// Run normal tests and doc tests (default). Yes, @@ -651,8 +652,8 @@ impl Build { job::setup(self); } - if let Subcommand::Format { check, paths } = &self.config.cmd { - return format::format(&builder::Builder::new(&self), *check, &paths); + if let Subcommand::Format { check } = &self.config.cmd { + return format::format(&builder::Builder::new(&self), *check, &self.config.paths); } // Download rustfmt early so that it can be used in rust-analyzer configs. diff --git a/src/bootstrap/run.rs b/src/bootstrap/run.rs index e14440f57a8a..0e1d619f5022 100644 --- a/src/bootstrap/run.rs +++ b/src/bootstrap/run.rs @@ -184,7 +184,6 @@ impl Step for Miri { miri.arg("--").arg("--target").arg(target.rustc_target_arg()); miri.args(builder.config.cmd.args()); miri.args(&builder.config.free_args); - // miri tests need to know about the stage sysroot miri.env("MIRI_SYSROOT", &miri_sysroot); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index b4f1506dc8f3..a881225b5d9b 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1589,7 +1589,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the // Get paths from cmd args let paths = match &builder.config.cmd { - Subcommand::Test { ref paths, .. } => &paths[..], + Subcommand::Test { .. } => &builder.config.paths[..], _ => &[], };