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::{Build, DocTests};
17 /// Deserialized version of all flags for this compile.
19 pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
20 pub on_fail: Option<String>,
21 pub stage: Option<u32>,
22 pub keep_stage: Vec<u32>,
23 pub keep_stage_std: Vec<u32>,
25 pub host: Option<Vec<TargetSelection>>,
26 pub target: Option<Vec<TargetSelection>>,
27 pub config: Option<PathBuf>,
28 pub jobs: Option<u32>,
30 pub incremental: bool,
31 pub exclude: Vec<PathBuf>,
32 pub rustc_error_format: Option<String>,
33 pub json_output: bool,
36 // This overrides the deny-warnings configuration option,
37 // which passes -Dwarnings to the compiler invocations.
39 // true => deny, false => warn
40 pub deny_warnings: Option<bool>,
42 pub llvm_skip_rebuild: Option<bool>,
50 // Whether to run checking over all targets (e.g., unit / integration
70 /// Whether to automatically update stderr/stdout files
72 compare_mode: Option<String>,
74 test_args: Vec<String>,
75 rustc_args: Vec<String>,
78 rustfix_coverage: bool,
82 test_args: Vec<String>,
101 impl Default for Subcommand {
102 fn default() -> Subcommand {
103 Subcommand::Build { paths: vec![PathBuf::from("nowhere")] }
108 pub fn parse(args: &[String]) -> Flags {
109 let mut subcommand_help = String::from(
111 Usage: x.py <subcommand> [options] [<paths>...]
114 build, b Compile either the compiler or libraries
115 check, c Compile either the compiler or libraries, using cargo check
116 clippy Run clippy (uses rustup/cargo-installed clippy binary)
119 test, t Build and run some test suites
120 bench Build and run some benchmarks
121 doc Build documentation
122 clean Clean out build directories
123 dist Build distribution artifacts
124 install Install distribution artifacts
125 run, r Run tools contained in this repository
127 To learn more about a subcommand, run `./x.py <subcommand> -h`",
130 let mut opts = Options::new();
131 // Options common to all subcommands
132 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
133 opts.optflag("i", "incremental", "use incremental compilation");
134 opts.optopt("", "config", "TOML configuration file for build", "FILE");
135 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
136 opts.optmulti("", "host", "host targets to build", "HOST");
137 opts.optmulti("", "target", "target targets to build", "TARGET");
138 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
139 opts.optopt("", "on-fail", "command to run on failure", "CMD");
140 opts.optflag("", "dry-run", "dry run; don't build anything");
144 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
145 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
151 "stage(s) to keep without recompiling \
152 (pass multiple times to keep e.g., both stages 0 and 1)",
158 "stage(s) of the standard library to keep without recompiling \
159 (pass multiple times to keep e.g., both stages 0 and 1)",
162 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
164 "number of jobs to run in parallel; \
165 defaults to {} (this host's logical CPU count)",
168 opts.optopt("j", "jobs", &j_msg, "JOBS");
169 opts.optflag("h", "help", "print this help message");
173 "if value is deny, will deny warnings, otherwise use default",
176 opts.optopt("", "error-format", "rustc error format", "FORMAT");
177 opts.optflag("", "json-output", "use message-format=json");
181 "whether rebuilding llvm should be skipped \
182 a VALUE of TRUE indicates that llvm will not be rebuilt \
183 VALUE overrides the skip-rebuild option in config.toml.",
187 // We can't use getopt to parse the options until we have completed specifying which
188 // options are valid, but under the current implementation, some options are conditional on
189 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
190 // complete the definition of the options. Then we can use the getopt::Matches object from
192 let subcommand = args.iter().find(|&s| {
211 let subcommand = match subcommand {
214 // No or an invalid subcommand -- show the general usage and subcommand help
215 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
217 println!("{}\n", subcommand_help);
218 let exit_code = if args.is_empty() { 0 } else { 1 };
219 process::exit(exit_code);
223 // Some subcommands get extra options
224 match subcommand.as_str() {
226 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
227 opts.optmulti("", "test-args", "extra arguments", "ARGS");
231 "extra options to pass the compiler when running tests",
234 opts.optflag("", "no-doc", "do not run doc tests");
235 opts.optflag("", "doc", "only run doc tests");
236 opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests");
240 "mode describing what file the actual ui output will be compared to",
246 "force {check,build,run}-pass tests to this mode.",
247 "check | build | run",
252 "enable this to generate a Rustfix coverage file, which is saved in \
253 `/<build_base>/rustfix_missing_coverage.txt`",
257 opts.optflag("", "all-targets", "Check all targets");
260 opts.optmulti("", "test-args", "extra arguments", "ARGS");
263 opts.optflag("", "open", "open the docs in a browser");
266 opts.optflag("", "all", "clean all build artifacts");
269 opts.optflag("", "check", "check formatting instead of applying.");
275 let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
276 let mut extra_help = String::new();
278 // All subcommands except `clean` can have an optional "Available paths" section
280 let config = Config::parse(&["build".to_string()]);
281 let build = Build::new(config);
283 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
284 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
285 } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") {
287 format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand)
292 println!("{}", opts.usage(subcommand_help));
293 if !extra_help.is_empty() {
294 println!("{}", extra_help);
296 process::exit(exit_code);
299 // Done specifying what options are possible, so do the getopts parsing
300 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
301 // Invalid argument/option format
302 println!("\n{}\n", e);
303 usage(1, &opts, false, &subcommand_help);
306 // Extra sanity check to make sure we didn't hit this crazy corner case:
308 // ./x.py --frobulate clean build
309 // ^-- option ^ ^- actual subcommand
310 // \_ arg to option could be mistaken as subcommand
311 let mut pass_sanity_check = true;
312 match matches.free.get(0) {
313 Some(check_subcommand) => {
314 if check_subcommand != subcommand {
315 pass_sanity_check = false;
319 pass_sanity_check = false;
322 if !pass_sanity_check {
323 println!("{}\n", subcommand_help);
325 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
326 You may need to move some options to after the subcommand.\n"
330 // Extra help text for some commands
331 match subcommand.as_str() {
333 subcommand_help.push_str(
336 This subcommand accepts a number of paths to directories to the crates
337 and/or artifacts to compile. For example:
339 ./x.py build library/core
340 ./x.py build library/core library/proc_macro
341 ./x.py build library/std --stage 1
343 If no arguments are passed then the complete artifacts for that stage are
347 ./x.py build --stage 1
349 For a quick build of a usable compiler, you can pass:
351 ./x.py build --stage 1 library/test
353 This will first build everything once (like `--stage 0` without further
354 arguments would), and then use the compiler built in stage 0 to build
355 library/test and its dependencies.
356 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
360 subcommand_help.push_str(
363 This subcommand accepts a number of paths to directories to the crates
364 and/or artifacts to compile. For example:
366 ./x.py check library/core
367 ./x.py check library/core library/proc_macro
369 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
370 also that since we use `cargo check`, by default this will automatically enable incremental
371 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
372 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
377 subcommand_help.push_str(
380 This subcommand accepts a number of paths to directories to the crates
381 and/or artifacts to run clippy against. For example:
383 ./x.py clippy library/core
384 ./x.py clippy library/core library/proc_macro",
388 subcommand_help.push_str(
391 This subcommand accepts a number of paths to directories to the crates
392 and/or artifacts to run `cargo fix` against. For example:
394 ./x.py fix library/core
395 ./x.py fix library/core library/proc_macro",
399 subcommand_help.push_str(
402 This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and
403 fails if it is not. For example:
410 subcommand_help.push_str(
413 This subcommand accepts a number of paths to test directories that
414 should be compiled and run. For example:
416 ./x.py test src/test/ui
417 ./x.py test library/std --test-args hash_map
418 ./x.py test library/std --stage 0 --no-doc
419 ./x.py test src/test/ui --bless
420 ./x.py test src/test/ui --compare-mode nll
422 Note that `test src/test/* --stage N` does NOT depend on `build compiler/rustc --stage N`;
423 just like `build library/std --stage N` it tests the compiler produced by the previous
426 Execute tool tests with a tool name argument:
430 If no arguments are passed then the complete artifacts for that stage are
434 ./x.py test --stage 1",
438 subcommand_help.push_str(
441 This subcommand accepts a number of paths to directories of documentation
442 to build. For example:
444 ./x.py doc src/doc/book
445 ./x.py doc src/doc/nomicon
446 ./x.py doc src/doc/book library/std
447 ./x.py doc library/std --open
449 If no arguments are passed then everything is documented:
452 ./x.py doc --stage 1",
456 subcommand_help.push_str(
459 This subcommand accepts a number of paths to tools to build and run. For
462 ./x.py run src/tools/expand-yaml-anchors
464 At least a tool needs to be called.",
468 subcommand_help.push_str(
471 This subcommand accepts a 'profile' to use for builds. For example:
475 The profile is optional and you will be prompted interactively if it is not given.",
480 // Get any optional paths which occur after the subcommand
481 let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
483 let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);
484 let verbose = matches.opt_present("verbose");
486 // User passed in -h/--help?
487 if matches.opt_present("help") {
488 usage(0, &opts, verbose, &subcommand_help);
491 let cmd = match subcommand.as_str() {
492 "build" | "b" => Subcommand::Build { paths },
494 Subcommand::Check { paths, all_targets: matches.opt_present("all-targets") }
496 "clippy" => Subcommand::Clippy { paths },
497 "fix" => Subcommand::Fix { paths },
498 "test" | "t" => Subcommand::Test {
500 bless: matches.opt_present("bless"),
501 compare_mode: matches.opt_str("compare-mode"),
502 pass: matches.opt_str("pass"),
503 test_args: matches.opt_strs("test-args"),
504 rustc_args: matches.opt_strs("rustc-args"),
505 fail_fast: !matches.opt_present("no-fail-fast"),
506 rustfix_coverage: matches.opt_present("rustfix-coverage"),
507 doc_tests: if matches.opt_present("doc") {
509 } else if matches.opt_present("no-doc") {
515 "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") },
516 "doc" => Subcommand::Doc { paths, open: matches.opt_present("open") },
518 if !paths.is_empty() {
519 println!("\nclean does not take a path argument\n");
520 usage(1, &opts, verbose, &subcommand_help);
523 Subcommand::Clean { all: matches.opt_present("all") }
525 "fmt" => Subcommand::Format { check: matches.opt_present("check") },
526 "dist" => Subcommand::Dist { paths },
527 "install" => Subcommand::Install { paths },
529 if paths.is_empty() {
530 println!("\nrun requires at least a path!\n");
531 usage(1, &opts, verbose, &subcommand_help);
533 Subcommand::Run { paths }
536 let path = if paths.len() > 1 {
537 println!("\nat most one profile can be passed to setup\n");
538 usage(1, &opts, verbose, &subcommand_help)
539 } else if let Some(path) = paths.pop() {
540 t!(path.into_os_string().into_string().map_err(|path| format!(
541 "{} is not a valid UTF8 string",
542 path.to_string_lossy()
545 t!(crate::setup::interactive_path())
547 Subcommand::Setup { path }
550 usage(1, &opts, verbose, &subcommand_help);
554 if let Subcommand::Check { .. } = &cmd {
555 if matches.opt_str("stage").is_some() {
556 println!("--stage not supported for x.py check, always treated as stage 0");
559 if matches.opt_str("keep-stage").is_some()
560 || matches.opt_str("keep-stage-std").is_some()
562 println!("--keep-stage not supported for x.py check, only one stage available");
568 verbose: matches.opt_count("verbose"),
569 stage: matches.opt_str("stage").map(|j| j.parse().expect("`stage` should be a number")),
570 dry_run: matches.opt_present("dry-run"),
571 on_fail: matches.opt_str("on-fail"),
572 rustc_error_format: matches.opt_str("error-format"),
573 json_output: matches.opt_present("json-output"),
575 .opt_strs("keep-stage")
577 .map(|j| j.parse().expect("`keep-stage` should be a number"))
579 keep_stage_std: matches
580 .opt_strs("keep-stage-std")
582 .map(|j| j.parse().expect("`keep-stage-std` should be a number"))
584 host: if matches.opt_present("host") {
586 split(&matches.opt_strs("host"))
588 .map(|x| TargetSelection::from_user(&x))
589 .collect::<Vec<_>>(),
594 target: if matches.opt_present("target") {
596 split(&matches.opt_strs("target"))
598 .map(|x| TargetSelection::from_user(&x))
599 .collect::<Vec<_>>(),
605 jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")),
607 incremental: matches.opt_present("incremental"),
608 exclude: split(&matches.opt_strs("exclude"))
611 .collect::<Vec<_>>(),
612 deny_warnings: parse_deny_warnings(&matches),
613 llvm_skip_rebuild: matches.opt_str("llvm-skip-rebuild").map(|s| s.to_lowercase()).map(
614 |s| s.parse::<bool>().expect("`llvm-skip-rebuild` should be either true or false"),
621 pub fn test_args(&self) -> Vec<&str> {
623 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
624 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
630 pub fn rustc_args(&self) -> Vec<&str> {
632 Subcommand::Test { ref rustc_args, .. } => {
633 rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
639 pub fn fail_fast(&self) -> bool {
641 Subcommand::Test { fail_fast, .. } => fail_fast,
646 pub fn doc_tests(&self) -> DocTests {
648 Subcommand::Test { doc_tests, .. } => doc_tests,
653 pub fn bless(&self) -> bool {
655 Subcommand::Test { bless, .. } => bless,
660 pub fn rustfix_coverage(&self) -> bool {
662 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
667 pub fn compare_mode(&self) -> Option<&str> {
669 Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]),
674 pub fn pass(&self) -> Option<&str> {
676 Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]),
681 pub fn open(&self) -> bool {
683 Subcommand::Doc { open, .. } => open,
689 fn split(s: &[String]) -> Vec<String> {
690 s.iter().flat_map(|s| s.split(',')).filter(|s| !s.is_empty()).map(|s| s.to_string()).collect()
693 fn parse_deny_warnings(matches: &getopts::Matches) -> Option<bool> {
694 match matches.opt_str("warnings").as_deref() {
695 Some("deny") => Some(true),
696 Some("warn") => Some(false),
698 eprintln!(r#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,);