diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 8b1775178c915..4dd465edb0df9 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1379,11 +1379,26 @@ def main(): sys.argv[1] = "-h" args = parse_args(sys.argv) - help_triggered = args.help or len(sys.argv) == 1 - # If the user is asking for help, let them know that the whole download-and-build + # Root help (e.g., x.py --help) prints help from the saved file to save the time + if len(sys.argv) == 1 or sys.argv[1] in ["-h", "--help"]: + try: + with open( + os.path.join(os.path.dirname(__file__), "../etc/xhelp"), "r" + ) as f: + # The file from bootstrap func already has newline. + print(f.read(), end="") + sys.exit(0) + except Exception as error: + eprint( + f"ERROR: unable to run help: {error}\n", + "x.py run generate-help may solve the problem.", + ) + sys.exit(1) + + # If the user is asking for other helps, let them know that the whole download-and-build # process has to happen before anything is printed out. - if help_triggered: + if args.help: eprint( "INFO: Downloading and building bootstrap before processing --help command.\n" " See src/bootstrap/README.md for help with common commands." @@ -1401,13 +1416,14 @@ def main(): eprint(error) success_word = "unsuccessfully" - if not help_triggered: + if not args.help: eprint( "Build completed", success_word, "in", format_build_time(time() - start_time), ) + sys.exit(exit_code) diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index b9a4c1bf9b44b..bd5ba89229117 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -15,7 +15,7 @@ use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, To use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata}; use crate::core::config::TargetSelection; -use crate::core::config::flags::get_completion; +use crate::core::config::flags::{get_completion, top_level_help}; use crate::utils::exec::command; use crate::{Mode, t}; @@ -512,3 +512,30 @@ impl Step for Rustfmt { rustfmt.into_cmd().run(builder); } } + +/// Return the path of x.py's help. +pub fn get_help_path(builder: &Builder<'_>) -> PathBuf { + builder.src.join("src/etc/xhelp") +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenerateHelp; + +impl Step for GenerateHelp { + type Output = (); + + fn run(self, builder: &Builder<'_>) { + let help = top_level_help(); + let path = get_help_path(builder); + std::fs::write(&path, help) + .unwrap_or_else(|e| panic!("writing help into {} failed: {e:?}", path.display())); + } + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("generate-help") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(GenerateHelp) + } +} diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 154b209b20252..4babd32d5d4c4 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -15,7 +15,7 @@ use crate::core::build_steps::compile::{Std, run_cargo}; use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler}; use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; -use crate::core::build_steps::run::get_completion_paths; +use crate::core::build_steps::run::{get_completion_paths, get_help_path}; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, @@ -28,7 +28,7 @@ use crate::core::builder::{ crate_description, }; use crate::core::config::TargetSelection; -use crate::core::config::flags::{Subcommand, get_completion}; +use crate::core::config::flags::{Subcommand, get_completion, top_level_help}; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ @@ -1292,6 +1292,23 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to ); crate::exit!(1); } + + builder.info("x.py help check"); + if builder.config.cmd.bless() { + builder.ensure(crate::core::build_steps::run::GenerateHelp); + } else { + let help_path = get_help_path(builder); + let cur_help = std::fs::read_to_string(&help_path).unwrap_or_else(|err| { + eprintln!("couldn't read {}: {}", help_path.display(), err); + crate::exit!(1); + }); + let new_help = top_level_help(); + + if new_help != cur_help { + eprintln!("x.py help was changed; run `x.py run generate-help` to update it"); + crate::exit!(1); + } + } } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 99fb62ea31c9d..d660680942751 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1239,6 +1239,7 @@ impl<'a> Builder<'a> { run::CyclicStep, run::CoverageDump, run::Rustfmt, + run::GenerateHelp, ), Kind::Setup => { describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor) diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index c01b71b926068..9e408505933d3 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -701,3 +701,9 @@ pub fn get_completion(shell: &dyn Generator, path: &Path) -> Option { } Some(String::from_utf8(buf).expect("completion script should be UTF-8")) } + +/// Return the top level help of the bootstrap. +pub fn top_level_help() -> String { + let mut cmd = Flags::command(); + cmd.render_help().to_string() +} diff --git a/src/etc/xhelp b/src/etc/xhelp new file mode 100644 index 0000000000000..5880d070d3d66 --- /dev/null +++ b/src/etc/xhelp @@ -0,0 +1,96 @@ + +Usage: x.py [options] [...] + +Commands: + build Compile either the compiler or libraries + check 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 + doc Build documentation + test Build and run some test suites + miri Build and run some test suites *in Miri* + bench Build and run some benchmarks + clean Clean out build directories + dist Build distribution artifacts + install Install distribution artifacts + run Run tools contained in this repository + setup Set up the environment for development + vendor Vendor dependencies + perf Perform profiling and benchmarking of the compiler using `rustc-perf` + +Arguments: + [PATHS]... paths for the subcommand + [ARGS]... arguments passed to subcommands + +Options: + -v, --verbose... + use verbose output (-vv for very verbose) + -i, --incremental + use incremental compilation + --config + TOML configuration file for build + --build-dir + Build directory, overrides `build.build-dir` in `bootstrap.toml` + --build + host target of the stage0 compiler + --host + host targets to build + --target + target targets to build + --exclude + build paths to exclude + --skip + build paths to skip + --include-default-paths + include default paths in addition to the provided ones + --rustc-error-format + rustc error format + --on-fail + command to run on failure + --dry-run + dry run; don't build anything + --dump-bootstrap-shims + Indicates whether to dump the work done from bootstrap shims + --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.) + --keep-stage + stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1) + --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) + --src + path to the root of the rust checkout + -j, --jobs + number of jobs to run in parallel + --warnings + if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour [default: default] [possible values: deny, warn, default] + --json-output + use message-format=json + --compile-time-deps + only build proc-macros and build scripts (for rust-analyzer) + --color