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;
12 use crate::builder::Builder;
13 use crate::config::{Config, TargetSelection};
14 use crate::{Build, DocTests};
16 /// Deserialized version of all flags for this compile.
18 pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
19 pub on_fail: Option<String>,
20 pub stage: Option<u32>,
21 pub keep_stage: Vec<u32>,
22 pub keep_stage_std: Vec<u32>,
24 pub host: Option<Vec<TargetSelection>>,
25 pub target: Option<Vec<TargetSelection>>,
26 pub config: Option<PathBuf>,
27 pub jobs: Option<u32>,
29 pub incremental: bool,
30 pub exclude: Vec<PathBuf>,
31 pub rustc_error_format: Option<String>,
32 pub json_output: bool,
35 // This overrides the deny-warnings configuration option,
36 // which passes -Dwarnings to the compiler invocations.
38 // true => deny, false => warn
39 pub deny_warnings: Option<bool>,
41 pub llvm_skip_rebuild: Option<bool>,
66 /// Whether to automatically update stderr/stdout files
68 compare_mode: Option<String>,
70 test_args: Vec<String>,
71 rustc_args: Vec<String>,
74 rustfix_coverage: bool,
78 test_args: Vec<String>,
94 impl Default for Subcommand {
95 fn default() -> Subcommand {
96 Subcommand::Build { paths: vec![PathBuf::from("nowhere")] }
101 pub fn parse(args: &[String]) -> Flags {
102 let mut subcommand_help = String::from(
104 Usage: x.py <subcommand> [options] [<paths>...]
107 build, b Compile either the compiler or libraries
108 check, c Compile either the compiler or libraries, using cargo check
109 clippy Run clippy (uses rustup/cargo-installed clippy binary)
112 test, t Build and run some test suites
113 bench Build and run some benchmarks
114 doc Build documentation
115 clean Clean out build directories
116 dist Build distribution artifacts
117 install Install distribution artifacts
118 run, r Run tools contained in this repository
120 To learn more about a subcommand, run `./x.py <subcommand> -h`",
123 let mut opts = Options::new();
124 // Options common to all subcommands
125 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
126 opts.optflag("i", "incremental", "use incremental compilation");
127 opts.optopt("", "config", "TOML configuration file for build", "FILE");
128 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
129 opts.optmulti("", "host", "host targets to build", "HOST");
130 opts.optmulti("", "target", "target targets to build", "TARGET");
131 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
132 opts.optopt("", "on-fail", "command to run on failure", "CMD");
133 opts.optflag("", "dry-run", "dry run; don't build anything");
137 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
138 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
144 "stage(s) to keep without recompiling \
145 (pass multiple times to keep e.g., both stages 0 and 1)",
151 "stage(s) of the standard library to keep without recompiling \
152 (pass multiple times to keep e.g., both stages 0 and 1)",
155 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
157 "number of jobs to run in parallel; \
158 defaults to {} (this host's logical CPU count)",
161 opts.optopt("j", "jobs", &j_msg, "JOBS");
162 opts.optflag("h", "help", "print this help message");
166 "if value is deny, will deny warnings, otherwise use default",
169 opts.optopt("", "error-format", "rustc error format", "FORMAT");
170 opts.optflag("", "json-output", "use message-format=json");
174 "whether rebuilding llvm should be skipped \
175 a VALUE of TRUE indicates that llvm will not be rebuilt \
176 VALUE overrides the skip-rebuild option in config.toml.",
180 // We can't use getopt to parse the options until we have completed specifying which
181 // options are valid, but under the current implementation, some options are conditional on
182 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
183 // complete the definition of the options. Then we can use the getopt::Matches object from
185 let subcommand = args.iter().find(|&s| {
203 let subcommand = match subcommand {
206 // No or an invalid subcommand -- show the general usage and subcommand help
207 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
209 println!("{}\n", subcommand_help);
210 let exit_code = if args.is_empty() { 0 } else { 1 };
211 process::exit(exit_code);
215 // Some subcommands get extra options
216 match subcommand.as_str() {
218 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
219 opts.optmulti("", "test-args", "extra arguments", "ARGS");
223 "extra options to pass the compiler when running tests",
226 opts.optflag("", "no-doc", "do not run doc tests");
227 opts.optflag("", "doc", "only run doc tests");
228 opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests");
232 "mode describing what file the actual ui output will be compared to",
238 "force {check,build,run}-pass tests to this mode.",
239 "check | build | run",
244 "enable this to generate a Rustfix coverage file, which is saved in \
245 `/<build_base>/rustfix_missing_coverage.txt`",
249 opts.optmulti("", "test-args", "extra arguments", "ARGS");
252 opts.optflag("", "open", "open the docs in a browser");
255 opts.optflag("", "all", "clean all build artifacts");
258 opts.optflag("", "check", "check formatting instead of applying.");
264 let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
265 let mut extra_help = String::new();
267 // All subcommands except `clean` can have an optional "Available paths" section
269 let config = Config::parse(&["build".to_string()]);
270 let build = Build::new(config);
272 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
273 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
274 } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") {
276 format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand)
281 println!("{}", opts.usage(subcommand_help));
282 if !extra_help.is_empty() {
283 println!("{}", extra_help);
285 process::exit(exit_code);
288 // Done specifying what options are possible, so do the getopts parsing
289 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
290 // Invalid argument/option format
291 println!("\n{}\n", e);
292 usage(1, &opts, false, &subcommand_help);
295 // Extra sanity check to make sure we didn't hit this crazy corner case:
297 // ./x.py --frobulate clean build
298 // ^-- option ^ ^- actual subcommand
299 // \_ arg to option could be mistaken as subcommand
300 let mut pass_sanity_check = true;
301 match matches.free.get(0) {
302 Some(check_subcommand) => {
303 if check_subcommand != subcommand {
304 pass_sanity_check = false;
308 pass_sanity_check = false;
311 if !pass_sanity_check {
312 println!("{}\n", subcommand_help);
314 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
315 You may need to move some options to after the subcommand.\n"
319 // Extra help text for some commands
320 match subcommand.as_str() {
322 subcommand_help.push_str(
325 This subcommand accepts a number of paths to directories to the crates
326 and/or artifacts to compile. For example:
328 ./x.py build library/core
329 ./x.py build library/core library/proc_macro
330 ./x.py build library/std --stage 1
332 If no arguments are passed then the complete artifacts for that stage are
336 ./x.py build --stage 1
338 For a quick build of a usable compiler, you can pass:
340 ./x.py build --stage 1 library/test
342 This will first build everything once (like `--stage 0` without further
343 arguments would), and then use the compiler built in stage 0 to build
344 library/test and its dependencies.
345 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
349 subcommand_help.push_str(
352 This subcommand accepts a number of paths to directories to the crates
353 and/or artifacts to compile. For example:
355 ./x.py check library/core
356 ./x.py check library/core library/proc_macro
358 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
359 also that since we use `cargo check`, by default this will automatically enable incremental
360 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
361 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
366 subcommand_help.push_str(
369 This subcommand accepts a number of paths to directories to the crates
370 and/or artifacts to run clippy against. For example:
372 ./x.py clippy library/core
373 ./x.py clippy library/core library/proc_macro",
377 subcommand_help.push_str(
380 This subcommand accepts a number of paths to directories to the crates
381 and/or artifacts to run `cargo fix` against. For example:
383 ./x.py fix library/core
384 ./x.py fix library/core library/proc_macro",
388 subcommand_help.push_str(
391 This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and
392 fails if it is not. For example:
399 subcommand_help.push_str(
402 This subcommand accepts a number of paths to test directories that
403 should be compiled and run. For example:
405 ./x.py test src/test/ui
406 ./x.py test library/std --test-args hash_map
407 ./x.py test library/std --stage 0 --no-doc
408 ./x.py test src/test/ui --bless
409 ./x.py test src/test/ui --compare-mode nll
411 Note that `test src/test/* --stage N` does NOT depend on `build compiler/rustc --stage N`;
412 just like `build library/std --stage N` it tests the compiler produced by the previous
415 Execute tool tests with a tool name argument:
419 If no arguments are passed then the complete artifacts for that stage are
423 ./x.py test --stage 1",
427 subcommand_help.push_str(
430 This subcommand accepts a number of paths to directories of documentation
431 to build. For example:
433 ./x.py doc src/doc/book
434 ./x.py doc src/doc/nomicon
435 ./x.py doc src/doc/book library/std
436 ./x.py doc library/std --open
438 If no arguments are passed then everything is documented:
441 ./x.py doc --stage 1",
445 subcommand_help.push_str(
448 This subcommand accepts a number of paths to tools to build and run. For
451 ./x.py run src/tools/expand-yaml-anchors
453 At least a tool needs to be called.",
458 // Get any optional paths which occur after the subcommand
459 let paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
461 let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);
462 let verbose = matches.opt_present("verbose");
464 // User passed in -h/--help?
465 if matches.opt_present("help") {
466 usage(0, &opts, verbose, &subcommand_help);
469 let cmd = match subcommand.as_str() {
470 "build" | "b" => Subcommand::Build { paths },
471 "check" | "c" => Subcommand::Check { paths },
472 "clippy" => Subcommand::Clippy { paths },
473 "fix" => Subcommand::Fix { paths },
474 "test" | "t" => Subcommand::Test {
476 bless: matches.opt_present("bless"),
477 compare_mode: matches.opt_str("compare-mode"),
478 pass: matches.opt_str("pass"),
479 test_args: matches.opt_strs("test-args"),
480 rustc_args: matches.opt_strs("rustc-args"),
481 fail_fast: !matches.opt_present("no-fail-fast"),
482 rustfix_coverage: matches.opt_present("rustfix-coverage"),
483 doc_tests: if matches.opt_present("doc") {
485 } else if matches.opt_present("no-doc") {
491 "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") },
492 "doc" => Subcommand::Doc { paths, open: matches.opt_present("open") },
494 if !paths.is_empty() {
495 println!("\nclean does not take a path argument\n");
496 usage(1, &opts, verbose, &subcommand_help);
499 Subcommand::Clean { all: matches.opt_present("all") }
501 "fmt" => Subcommand::Format { check: matches.opt_present("check") },
502 "dist" => Subcommand::Dist { paths },
503 "install" => Subcommand::Install { paths },
505 if paths.is_empty() {
506 println!("\nrun requires at least a path!\n");
507 usage(1, &opts, verbose, &subcommand_help);
509 Subcommand::Run { paths }
512 usage(1, &opts, verbose, &subcommand_help);
516 if let Subcommand::Check { .. } = &cmd {
517 if matches.opt_str("stage").is_some() {
518 println!("--stage not supported for x.py check, always treated as stage 0");
521 if matches.opt_str("keep-stage").is_some()
522 || matches.opt_str("keep-stage-std").is_some()
524 println!("--keep-stage not supported for x.py check, only one stage available");
530 verbose: matches.opt_count("verbose"),
531 stage: matches.opt_str("stage").map(|j| j.parse().expect("`stage` should be a number")),
532 dry_run: matches.opt_present("dry-run"),
533 on_fail: matches.opt_str("on-fail"),
534 rustc_error_format: matches.opt_str("error-format"),
535 json_output: matches.opt_present("json-output"),
537 .opt_strs("keep-stage")
539 .map(|j| j.parse().expect("`keep-stage` should be a number"))
541 keep_stage_std: matches
542 .opt_strs("keep-stage-std")
544 .map(|j| j.parse().expect("`keep-stage-std` should be a number"))
546 host: if matches.opt_present("host") {
548 split(&matches.opt_strs("host"))
550 .map(|x| TargetSelection::from_user(&x))
551 .collect::<Vec<_>>(),
556 target: if matches.opt_present("target") {
558 split(&matches.opt_strs("target"))
560 .map(|x| TargetSelection::from_user(&x))
561 .collect::<Vec<_>>(),
567 jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")),
569 incremental: matches.opt_present("incremental"),
570 exclude: split(&matches.opt_strs("exclude"))
573 .collect::<Vec<_>>(),
574 deny_warnings: parse_deny_warnings(&matches),
575 llvm_skip_rebuild: matches.opt_str("llvm-skip-rebuild").map(|s| s.to_lowercase()).map(
576 |s| s.parse::<bool>().expect("`llvm-skip-rebuild` should be either true or false"),
583 pub fn test_args(&self) -> Vec<&str> {
585 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
586 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
592 pub fn rustc_args(&self) -> Vec<&str> {
594 Subcommand::Test { ref rustc_args, .. } => {
595 rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
601 pub fn fail_fast(&self) -> bool {
603 Subcommand::Test { fail_fast, .. } => fail_fast,
608 pub fn doc_tests(&self) -> DocTests {
610 Subcommand::Test { doc_tests, .. } => doc_tests,
615 pub fn bless(&self) -> bool {
617 Subcommand::Test { bless, .. } => bless,
622 pub fn rustfix_coverage(&self) -> bool {
624 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
629 pub fn compare_mode(&self) -> Option<&str> {
631 Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]),
636 pub fn pass(&self) -> Option<&str> {
638 Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]),
643 pub fn open(&self) -> bool {
645 Subcommand::Doc { open, .. } => open,
651 fn split(s: &[String]) -> Vec<String> {
652 s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
655 fn parse_deny_warnings(matches: &getopts::Matches) -> Option<bool> {
656 match matches.opt_str("warnings").as_deref() {
657 Some("deny") => Some(true),
658 Some("warn") => Some(false),
660 eprintln!(r#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,);