Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/bootstrap/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second part of this seems like it's not quite right, it needs (sys.argv[1] in ["-h", "--help"] and len(sys.argv) == 2), right? Otherwise x.py --help check wouldn't do the right thing?

Copy link
Contributor Author

@Shunpoco Shunpoco Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently x.py --help check prints the top level help, not check's help. So I think we don't need len(sys.argv) == 2 for it.
Or, do you think x.py --help check should be identical with x.py check --help?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, do you think~

I guess if we dispatch x.py --help check to bootstrap binary. Clap returns the top level help for it unless we modify the behaviour.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, do you think x.py --help check should be identical with x.py check --help?

Yeah, that's what I'd expect. Printing the top-level output for x.py --help check seems quite odd to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if clap has this behavior, then I'm OK sticking with that decision (would be interesting to see if that's intentional). It's probably a rare case either way.

Copy link
Contributor Author

@Shunpoco Shunpoco Oct 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I checked that:

  • clap checks arguments from left to right
  • If an argument is help (--help or -h), it prints help of the current level (e.g., ./x --help is for top level, and ./x check --help is for check level) and exit immediately

So x.py --help check prints the top level help, and argument check is just ignored.

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."
Expand All @@ -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)


Expand Down
29 changes: 28 additions & 1 deletion src/bootstrap/src/core/build_steps/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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)
}
}
21 changes: 19 additions & 2 deletions src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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::{
Expand Down Expand Up @@ -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<'_> {
Expand Down
1 change: 1 addition & 0 deletions src/bootstrap/src/core/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions src/bootstrap/src/core/config/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,3 +701,9 @@ pub fn get_completion(shell: &dyn Generator, path: &Path) -> Option<String> {
}
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()
}
96 changes: 96 additions & 0 deletions src/etc/xhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

Usage: x.py <subcommand> [options] [<paths>...]

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 <FILE>
TOML configuration file for build
--build-dir <DIR>
Build directory, overrides `build.build-dir` in `bootstrap.toml`
--build <BUILD>
host target of the stage0 compiler
--host <HOST>
host targets to build
--target <TARGET>
target targets to build
--exclude <PATH>
build paths to exclude
--skip <PATH>
build paths to skip
--include-default-paths
include default paths in addition to the provided ones
--rustc-error-format <RUSTC_ERROR_FORMAT>
rustc error format
--on-fail <CMD>
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 <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.)
--keep-stage <N>
stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)
--keep-stage-std <N>
stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)
--src <DIR>
path to the root of the rust checkout
-j, --jobs <JOBS>
number of jobs to run in parallel
--warnings <deny|warn>
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 <STYLE>
whether to use color in cargo and rustc output [default: auto] [possible values: always, never, auto]
--bypass-bootstrap-lock
Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)
--rust-profile-generate <PROFILE>
generate PGO profile with rustc build
--rust-profile-use <PROFILE>
use PGO profile for rustc build
--llvm-profile-use <PROFILE>
use PGO profile for LLVM build
--llvm-profile-generate
generate PGO profile with llvm built for rustc
--enable-bolt-settings
Enable BOLT link flags
--skip-stage0-validation
Skip stage0 compiler validation
--reproducible-artifact <REPRODUCIBLE_ARTIFACT>
Additional reproducible artifacts that should be added to the reproducible artifacts archive
--set <section.option=value>
override options in bootstrap.toml
--ci <bool>
Make bootstrap to behave as it's running on the CI environment or not [possible values: true, false]
--skip-std-check-if-no-download-rustc
Skip checking the standard library if `rust.download-rustc` isn't available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers
-h, --help
Print help (see more with '--help')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not relevant to this PR, but I wonder if we should really be showing all of this in help... e.g., the PGO options don't feel relevant to 99% of x.py users, and there's enough options here that we end up scrolling off screen for the actually-useful collection of subcommands test/doc/check/etc.

It might mean that we want a verbose and non-verbose help for the top level?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to me for both > not relevant to this PR and > want a verbose and non-verbose help for the top level

Although I don't know if Clap supports such detailed/brief help, can I make an issue ticket for it (Check Clap's features, then modify the top-level help if we can)

Loading