1 //! Command-line interface of the rustbuild build system.
3 //! This module implements the command-line parsing of the build system which
4 //! has various flags to configure how it's run.
7 use std::path::PathBuf;
13 use crate::builder::Builder;
14 use crate::config::{Config, TargetSelection};
15 use crate::setup::Profile;
16 use crate::{Build, DocTests};
24 impl Default for Color {
25 fn default() -> Self {
30 impl std::str::FromStr for Color {
33 fn from_str(s: &str) -> Result<Self, Self::Err> {
34 match s.to_lowercase().as_str() {
35 "always" => Ok(Self::Always),
36 "never" => Ok(Self::Never),
37 "auto" => Ok(Self::Auto),
43 /// Deserialized version of all flags for this compile.
45 pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
46 pub on_fail: Option<String>,
47 pub stage: Option<u32>,
48 pub keep_stage: Vec<u32>,
49 pub keep_stage_std: Vec<u32>,
51 pub host: Option<Vec<TargetSelection>>,
52 pub target: Option<Vec<TargetSelection>>,
53 pub config: Option<PathBuf>,
54 pub jobs: Option<u32>,
56 pub incremental: bool,
57 pub exclude: Vec<PathBuf>,
58 pub include_default_paths: bool,
59 pub rustc_error_format: Option<String>,
60 pub json_output: bool,
64 // This overrides the deny-warnings configuration option,
65 // which passes -Dwarnings to the compiler invocations.
67 // true => deny, false => warn
68 pub deny_warnings: Option<bool>,
70 pub llvm_skip_rebuild: Option<bool>,
72 pub rust_profile_use: Option<String>,
73 pub rust_profile_generate: Option<String>,
81 // Whether to run checking over all targets (e.g., unit / integration
103 /// Whether to automatically update stderr/stdout files
106 compare_mode: Option<String>,
107 pass: Option<String>,
109 test_args: Vec<String>,
110 rustc_args: Vec<String>,
113 rustfix_coverage: bool,
117 test_args: Vec<String>,
136 impl Default for Subcommand {
137 fn default() -> Subcommand {
138 Subcommand::Build { paths: vec![PathBuf::from("nowhere")] }
143 pub fn parse(args: &[String]) -> Flags {
144 let mut subcommand_help = String::from(
146 Usage: x.py <subcommand> [options] [<paths>...]
149 build, b Compile either the compiler or libraries
150 check, c Compile either the compiler or libraries, using cargo check
151 clippy Run clippy (uses rustup/cargo-installed clippy binary)
154 test, t Build and run some test suites
155 bench Build and run some benchmarks
156 doc, d Build documentation
157 clean Clean out build directories
158 dist Build distribution artifacts
159 install Install distribution artifacts
160 run, r Run tools contained in this repository
161 setup Create a config.toml (making it easier to use `x.py` itself)
163 To learn more about a subcommand, run `./x.py <subcommand> -h`",
166 let mut opts = Options::new();
167 // Options common to all subcommands
168 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
169 opts.optflag("i", "incremental", "use incremental compilation");
170 opts.optopt("", "config", "TOML configuration file for build", "FILE");
171 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
172 opts.optmulti("", "host", "host targets to build", "HOST");
173 opts.optmulti("", "target", "target targets to build", "TARGET");
174 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
177 "include-default-paths",
178 "include default paths in addition to the provided ones",
180 opts.optopt("", "on-fail", "command to run on failure", "CMD");
181 opts.optflag("", "dry-run", "dry run; don't build anything");
185 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
186 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
192 "stage(s) to keep without recompiling \
193 (pass multiple times to keep e.g., both stages 0 and 1)",
199 "stage(s) of the standard library to keep without recompiling \
200 (pass multiple times to keep e.g., both stages 0 and 1)",
203 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
205 "number of jobs to run in parallel; \
206 defaults to {} (this host's logical CPU count)",
209 opts.optopt("j", "jobs", &j_msg, "JOBS");
210 opts.optflag("h", "help", "print this help message");
214 "if value is deny, will deny warnings, otherwise use default",
217 opts.optopt("", "error-format", "rustc error format", "FORMAT");
218 opts.optflag("", "json-output", "use message-format=json");
219 opts.optopt("", "color", "whether to use color in cargo and rustc output", "STYLE");
223 "whether rebuilding llvm should be skipped \
224 a VALUE of TRUE indicates that llvm will not be rebuilt \
225 VALUE overrides the skip-rebuild option in config.toml.",
228 opts.optopt("", "rust-profile-generate", "generate PGO profile with rustc build", "FORMAT");
229 opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "FORMAT");
231 // We can't use getopt to parse the options until we have completed specifying which
232 // options are valid, but under the current implementation, some options are conditional on
233 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
234 // complete the definition of the options. Then we can use the getopt::Matches object from
236 let subcommand = args.iter().find(|&s| {
256 let subcommand = match subcommand {
259 // No or an invalid subcommand -- show the general usage and subcommand help
260 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
262 println!("{}\n", subcommand_help);
263 let exit_code = if args.is_empty() { 0 } else { 1 };
264 process::exit(exit_code);
268 // Some subcommands get extra options
269 match subcommand.as_str() {
271 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
275 "extra arguments to be passed for the test tool being used \
276 (e.g. libtest, compiletest or rustdoc)",
282 "extra options to pass the compiler when running tests",
285 opts.optflag("", "no-doc", "do not run doc tests");
286 opts.optflag("", "doc", "only run doc tests");
287 opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests");
288 opts.optflag("", "force-rerun", "rerun tests even if the inputs are unchanged");
292 "mode describing what file the actual ui output will be compared to",
298 "force {check,build,run}-pass tests to this mode.",
299 "check | build | run",
301 opts.optopt("", "run", "whether to execute run-* tests", "auto | always | never");
305 "enable this to generate a Rustfix coverage file, which is saved in \
306 `/<build_base>/rustfix_missing_coverage.txt`",
310 opts.optflag("", "all-targets", "Check all targets");
313 opts.optmulti("", "test-args", "extra arguments", "ARGS");
316 opts.optflag("", "fix", "automatically apply lint suggestions");
319 opts.optflag("", "open", "open the docs in a browser");
322 opts.optflag("", "all", "clean all build artifacts");
325 opts.optflag("", "check", "check formatting instead of applying.");
331 let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
332 let mut extra_help = String::new();
334 // All subcommands except `clean` can have an optional "Available paths" section
336 let config = Config::parse(&["build".to_string()]);
337 let build = Build::new(config);
339 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
340 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
341 } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") {
343 format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand)
348 println!("{}", opts.usage(subcommand_help));
349 if !extra_help.is_empty() {
350 println!("{}", extra_help);
352 process::exit(exit_code);
355 // Done specifying what options are possible, so do the getopts parsing
356 let matches = opts.parse(args).unwrap_or_else(|e| {
357 // Invalid argument/option format
358 println!("\n{}\n", e);
359 usage(1, &opts, false, &subcommand_help);
362 // Extra sanity check to make sure we didn't hit this crazy corner case:
364 // ./x.py --frobulate clean build
365 // ^-- option ^ ^- actual subcommand
366 // \_ arg to option could be mistaken as subcommand
367 let mut pass_sanity_check = true;
368 match matches.free.get(0) {
369 Some(check_subcommand) => {
370 if check_subcommand != subcommand {
371 pass_sanity_check = false;
375 pass_sanity_check = false;
378 if !pass_sanity_check {
379 println!("{}\n", subcommand_help);
381 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
382 You may need to move some options to after the subcommand.\n"
386 // Extra help text for some commands
387 match subcommand.as_str() {
389 subcommand_help.push_str(
392 This subcommand accepts a number of paths to directories to the crates
393 and/or artifacts to compile. For example:
395 ./x.py build library/core
396 ./x.py build library/core library/proc_macro
397 ./x.py build library/std --stage 1
399 If no arguments are passed then the complete artifacts for that stage are
403 ./x.py build --stage 1
405 For a quick build of a usable compiler, you can pass:
407 ./x.py build --stage 1 library/test
409 This will first build everything once (like `--stage 0` without further
410 arguments would), and then use the compiler built in stage 0 to build
411 library/test and its dependencies.
412 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
416 subcommand_help.push_str(
419 This subcommand accepts a number of paths to directories to the crates
420 and/or artifacts to compile. For example:
422 ./x.py check library/core
423 ./x.py check library/core library/proc_macro
425 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
426 also that since we use `cargo check`, by default this will automatically enable incremental
427 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
428 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
433 subcommand_help.push_str(
436 This subcommand accepts a number of paths to directories to the crates
437 and/or artifacts to run clippy against. For example:
439 ./x.py clippy library/core
440 ./x.py clippy library/core library/proc_macro",
444 subcommand_help.push_str(
447 This subcommand accepts a number of paths to directories to the crates
448 and/or artifacts to run `cargo fix` against. For example:
450 ./x.py fix library/core
451 ./x.py fix library/core library/proc_macro",
455 subcommand_help.push_str(
458 This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and
459 fails if it is not. For example:
466 subcommand_help.push_str(
469 This subcommand accepts a number of paths to test directories that
470 should be compiled and run. For example:
472 ./x.py test src/test/ui
473 ./x.py test library/std --test-args hash_map
474 ./x.py test library/std --stage 0 --no-doc
475 ./x.py test src/test/ui --bless
476 ./x.py test src/test/ui --compare-mode nll
478 Note that `test src/test/* --stage N` does NOT depend on `build compiler/rustc --stage N`;
479 just like `build library/std --stage N` it tests the compiler produced by the previous
482 Execute tool tests with a tool name argument:
486 If no arguments are passed then the complete artifacts for that stage are
490 ./x.py test --stage 1",
494 subcommand_help.push_str(
497 This subcommand accepts a number of paths to directories of documentation
498 to build. For example:
500 ./x.py doc src/doc/book
501 ./x.py doc src/doc/nomicon
502 ./x.py doc src/doc/book library/std
503 ./x.py doc library/std --open
505 If no arguments are passed then everything is documented:
508 ./x.py doc --stage 1",
512 subcommand_help.push_str(
515 This subcommand accepts a number of paths to tools to build and run. For
518 ./x.py run src/tools/expand-yaml-anchors
520 At least a tool needs to be called.",
524 subcommand_help.push_str(&format!(
526 x.py setup creates a `config.toml` which changes the defaults for x.py itself.
529 This subcommand accepts a 'profile' to use for builds. For example:
533 The profile is optional and you will be prompted interactively if it is not given.
534 The following profiles are available:
537 Profile::all_for_help(" ").trim_end()
542 // Get any optional paths which occur after the subcommand
543 let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
545 let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);
546 let verbose = matches.opt_present("verbose");
548 // User passed in -h/--help?
549 if matches.opt_present("help") {
550 usage(0, &opts, verbose, &subcommand_help);
553 let cmd = match subcommand.as_str() {
554 "build" | "b" => Subcommand::Build { paths },
556 Subcommand::Check { paths, all_targets: matches.opt_present("all-targets") }
558 "clippy" => Subcommand::Clippy { paths, fix: matches.opt_present("fix") },
559 "fix" => Subcommand::Fix { paths },
560 "test" | "t" => Subcommand::Test {
562 bless: matches.opt_present("bless"),
563 force_rerun: matches.opt_present("force-rerun"),
564 compare_mode: matches.opt_str("compare-mode"),
565 pass: matches.opt_str("pass"),
566 run: matches.opt_str("run"),
567 test_args: matches.opt_strs("test-args"),
568 rustc_args: matches.opt_strs("rustc-args"),
569 fail_fast: !matches.opt_present("no-fail-fast"),
570 rustfix_coverage: matches.opt_present("rustfix-coverage"),
571 doc_tests: if matches.opt_present("doc") {
573 } else if matches.opt_present("no-doc") {
579 "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") },
580 "doc" | "d" => Subcommand::Doc { paths, open: matches.opt_present("open") },
582 if !paths.is_empty() {
583 println!("\nclean does not take a path argument\n");
584 usage(1, &opts, verbose, &subcommand_help);
587 Subcommand::Clean { all: matches.opt_present("all") }
589 "fmt" => Subcommand::Format { check: matches.opt_present("check"), paths },
590 "dist" => Subcommand::Dist { paths },
591 "install" => Subcommand::Install { paths },
593 if paths.is_empty() {
594 println!("\nrun requires at least a path!\n");
595 usage(1, &opts, verbose, &subcommand_help);
597 Subcommand::Run { paths }
600 let profile = if paths.len() > 1 {
601 println!("\nat most one profile can be passed to setup\n");
602 usage(1, &opts, verbose, &subcommand_help)
603 } else if let Some(path) = paths.pop() {
604 let profile_string = t!(path.into_os_string().into_string().map_err(
605 |path| format!("{} is not a valid UTF8 string", path.to_string_lossy())
608 profile_string.parse().unwrap_or_else(|err| {
609 eprintln!("error: {}", err);
610 eprintln!("help: the available profiles are:");
611 eprint!("{}", Profile::all_for_help("- "));
612 std::process::exit(1);
615 t!(crate::setup::interactive_path())
617 Subcommand::Setup { profile }
620 usage(1, &opts, verbose, &subcommand_help);
624 if let Subcommand::Check { .. } = &cmd {
625 if matches.opt_str("keep-stage").is_some()
626 || matches.opt_str("keep-stage-std").is_some()
628 println!("--keep-stage not yet supported for x.py check");
634 verbose: matches.opt_count("verbose"),
635 stage: matches.opt_str("stage").map(|j| j.parse().expect("`stage` should be a number")),
636 dry_run: matches.opt_present("dry-run"),
637 on_fail: matches.opt_str("on-fail"),
638 rustc_error_format: matches.opt_str("error-format"),
639 json_output: matches.opt_present("json-output"),
641 .opt_strs("keep-stage")
643 .map(|j| j.parse().expect("`keep-stage` should be a number"))
645 keep_stage_std: matches
646 .opt_strs("keep-stage-std")
648 .map(|j| j.parse().expect("`keep-stage-std` should be a number"))
650 host: if matches.opt_present("host") {
652 split(&matches.opt_strs("host"))
654 .map(|x| TargetSelection::from_user(&x))
655 .collect::<Vec<_>>(),
660 target: if matches.opt_present("target") {
662 split(&matches.opt_strs("target"))
664 .map(|x| TargetSelection::from_user(&x))
665 .collect::<Vec<_>>(),
671 jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")),
673 incremental: matches.opt_present("incremental"),
674 exclude: split(&matches.opt_strs("exclude"))
677 .collect::<Vec<_>>(),
678 include_default_paths: matches.opt_present("include-default-paths"),
679 deny_warnings: parse_deny_warnings(&matches),
680 llvm_skip_rebuild: matches.opt_str("llvm-skip-rebuild").map(|s| s.to_lowercase()).map(
681 |s| s.parse::<bool>().expect("`llvm-skip-rebuild` should be either true or false"),
684 .opt_get_default("color", Color::Auto)
685 .expect("`color` should be `always`, `never`, or `auto`"),
686 rust_profile_use: matches.opt_str("rust-profile-use"),
687 rust_profile_generate: matches.opt_str("rust-profile-generate"),
693 pub fn test_args(&self) -> Vec<&str> {
695 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
696 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
702 pub fn rustc_args(&self) -> Vec<&str> {
704 Subcommand::Test { ref rustc_args, .. } => {
705 rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
711 pub fn fail_fast(&self) -> bool {
713 Subcommand::Test { fail_fast, .. } => fail_fast,
718 pub fn doc_tests(&self) -> DocTests {
720 Subcommand::Test { doc_tests, .. } => doc_tests,
725 pub fn bless(&self) -> bool {
727 Subcommand::Test { bless, .. } => bless,
732 pub fn force_rerun(&self) -> bool {
734 Subcommand::Test { force_rerun, .. } => force_rerun,
739 pub fn rustfix_coverage(&self) -> bool {
741 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
746 pub fn compare_mode(&self) -> Option<&str> {
748 Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]),
753 pub fn pass(&self) -> Option<&str> {
755 Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]),
760 pub fn run(&self) -> Option<&str> {
762 Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]),
767 pub fn open(&self) -> bool {
769 Subcommand::Doc { open, .. } => open,
775 fn split(s: &[String]) -> Vec<String> {
776 s.iter().flat_map(|s| s.split(',')).filter(|s| !s.is_empty()).map(|s| s.to_string()).collect()
779 fn parse_deny_warnings(matches: &getopts::Matches) -> Option<bool> {
780 match matches.opt_str("warnings").as_deref() {
781 Some("deny") => Some(true),
782 Some("warn") => Some(false),
784 eprintln!(r#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,);