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;
15 use crate::{Build, DocTests};
17 use crate::cache::{Interned, INTERNER};
19 /// Deserialized version of all flags for this compile.
21 pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
22 pub on_fail: Option<String>,
23 pub stage: Option<u32>,
24 pub keep_stage: Vec<u32>,
26 pub host: Vec<Interned<String>>,
27 pub target: Vec<Interned<String>>,
28 pub config: Option<PathBuf>,
29 pub jobs: Option<u32>,
31 pub incremental: bool,
32 pub exclude: Vec<PathBuf>,
33 pub rustc_error_format: Option<String>,
36 // This overrides the deny-warnings configuation option,
37 // which passes -Dwarnings to the compiler invocations.
39 // true => deny, false => warn
40 pub deny_warnings: Option<bool>,
64 /// Whether to automatically update stderr/stdout files
66 compare_mode: Option<String>,
68 test_args: Vec<String>,
69 rustc_args: Vec<String>,
72 rustfix_coverage: bool,
76 test_args: Vec<String>,
89 impl Default for Subcommand {
90 fn default() -> Subcommand {
92 paths: vec![PathBuf::from("nowhere")],
98 pub fn parse(args: &[String]) -> Flags {
99 let mut extra_help = String::new();
100 let mut subcommand_help = String::from("\
101 Usage: x.py <subcommand> [options] [<paths>...]
104 build Compile either the compiler or libraries
105 check Compile either the compiler or libraries, using cargo check
109 test Build and run some test suites
110 bench Build and run some benchmarks
111 doc Build documentation
112 clean Clean out build directories
113 dist Build distribution artifacts
114 install Install distribution artifacts
116 To learn more about a subcommand, run `./x.py <subcommand> -h`"
119 let mut opts = Options::new();
120 // Options common to all subcommands
121 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
122 opts.optflag("i", "incremental", "use incremental compilation");
123 opts.optopt("", "config", "TOML configuration file for build", "FILE");
124 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
125 opts.optmulti("", "host", "host targets to build", "HOST");
126 opts.optmulti("", "target", "target targets to build", "TARGET");
127 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
128 opts.optopt("", "on-fail", "command to run on failure", "CMD");
129 opts.optflag("", "dry-run", "dry run; don't build anything");
130 opts.optopt("", "stage",
131 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
132 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
134 opts.optmulti("", "keep-stage", "stage(s) to keep without recompiling \
135 (pass multiple times to keep e.g., both stages 0 and 1)", "N");
136 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
137 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
138 opts.optflag("h", "help", "print this help message");
142 "if value is deny, will deny warnings, otherwise use default",
145 opts.optopt("", "error-format", "rustc error format", "FORMAT");
149 |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
150 println!("{}", opts.usage(subcommand_help));
151 if !extra_help.is_empty() {
152 println!("{}", extra_help);
154 process::exit(exit_code);
157 // We can't use getopt to parse the options until we have completed specifying which
158 // options are valid, but under the current implementation, some options are conditional on
159 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
160 // complete the definition of the options. Then we can use the getopt::Matches object from
162 let subcommand = args.iter().find(|&s| {
175 let subcommand = match subcommand {
178 // No or an invalid subcommand -- show the general usage and subcommand help
179 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
181 println!("{}\n", subcommand_help);
182 let exit_code = if args.is_empty() { 0 } else { 1 };
183 process::exit(exit_code);
187 // Some subcommands get extra options
188 match subcommand.as_str() {
190 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
191 opts.optmulti("", "test-args", "extra arguments", "ARGS");
195 "extra options to pass the compiler when running tests",
198 opts.optflag("", "no-doc", "do not run doc tests");
199 opts.optflag("", "doc", "only run doc tests");
203 "update all stderr/stdout files of failing ui tests",
208 "mode describing what file the actual ui output will be compared to",
214 "force {check,build,run}-pass tests to this mode.",
215 "check | build | run"
220 "enable this to generate a Rustfix coverage file, which is saved in \
221 `/<build_base>/rustfix_missing_coverage.txt`",
225 opts.optmulti("", "test-args", "extra arguments", "ARGS");
228 opts.optflag("", "all", "clean all build artifacts");
231 opts.optflag("", "check", "check formatting instead of applying.");
236 // Done specifying what options are possible, so do the getopts parsing
237 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
238 // Invalid argument/option format
239 println!("\n{}\n", e);
240 usage(1, &opts, &subcommand_help, &extra_help);
242 // Extra sanity check to make sure we didn't hit this crazy corner case:
244 // ./x.py --frobulate clean build
245 // ^-- option ^ ^- actual subcommand
246 // \_ arg to option could be mistaken as subcommand
247 let mut pass_sanity_check = true;
248 match matches.free.get(0) {
249 Some(check_subcommand) => {
250 if check_subcommand != subcommand {
251 pass_sanity_check = false;
255 pass_sanity_check = false;
258 if !pass_sanity_check {
259 println!("{}\n", subcommand_help);
261 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
262 You may need to move some options to after the subcommand.\n"
266 // Extra help text for some commands
267 match subcommand.as_str() {
269 subcommand_help.push_str(
272 This subcommand accepts a number of paths to directories to the crates
273 and/or artifacts to compile. For example:
275 ./x.py build src/libcore
276 ./x.py build src/libcore src/libproc_macro
277 ./x.py build src/libstd --stage 1
279 If no arguments are passed then the complete artifacts for that stage are
283 ./x.py build --stage 1
285 For a quick build of a usable compiler, you can pass:
287 ./x.py build --stage 1 src/libtest
289 This will first build everything once (like `--stage 0` without further
290 arguments would), and then use the compiler built in stage 0 to build
291 src/libtest and its dependencies.
292 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
296 subcommand_help.push_str(
299 This subcommand accepts a number of paths to directories to the crates
300 and/or artifacts to compile. For example:
302 ./x.py check src/libcore
303 ./x.py check src/libcore src/libproc_macro
305 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
306 also that since we use `cargo check`, by default this will automatically enable incremental
307 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
308 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
313 subcommand_help.push_str(
316 This subcommand accepts a number of paths to directories to the crates
317 and/or artifacts to run clippy against. For example:
319 ./x.py clippy src/libcore
320 ./x.py clippy src/libcore src/libproc_macro",
324 subcommand_help.push_str(
327 This subcommand accepts a number of paths to directories to the crates
328 and/or artifacts to run `cargo fix` against. For example:
330 ./x.py fix src/libcore
331 ./x.py fix src/libcore src/libproc_macro",
335 subcommand_help.push_str(
338 This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and
339 fails if it is not. For example:
346 subcommand_help.push_str(
349 This subcommand accepts a number of paths to directories to tests that
350 should be compiled and run. For example:
352 ./x.py test src/test/ui
353 ./x.py test src/libstd --test-args hash_map
354 ./x.py test src/libstd --stage 0 --no-doc
355 ./x.py test src/test/ui --bless
356 ./x.py test src/test/ui --compare-mode nll
358 Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
359 just like `build src/libstd --stage N` it tests the compiler produced by the previous
362 If no arguments are passed then the complete artifacts for that stage are
366 ./x.py test --stage 1",
370 subcommand_help.push_str(
373 This subcommand accepts a number of paths to directories of documentation
374 to build. For example:
376 ./x.py doc src/doc/book
377 ./x.py doc src/doc/nomicon
378 ./x.py doc src/doc/book src/libstd
380 If no arguments are passed then everything is documented:
383 ./x.py doc --stage 1",
388 // Get any optional paths which occur after the subcommand
389 let paths = matches.free[1..]
392 .collect::<Vec<PathBuf>>();
394 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
395 if fs::metadata("config.toml").is_ok() {
396 Some(PathBuf::from("config.toml"))
402 // All subcommands except `clean` can have an optional "Available paths" section
403 if matches.opt_present("verbose") {
404 let config = Config::parse(&["build".to_string()]);
405 let mut build = Build::new(config);
406 metadata::build(&mut build);
408 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
409 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
410 } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") {
413 "Run `./x.py {} -h -v` to see a list of available paths.",
419 // User passed in -h/--help?
420 if matches.opt_present("help") {
421 usage(0, &opts, &subcommand_help, &extra_help);
424 let cmd = match subcommand.as_str() {
425 "build" => Subcommand::Build { paths },
426 "check" => Subcommand::Check { paths },
427 "clippy" => Subcommand::Clippy { paths },
428 "fix" => Subcommand::Fix { paths },
429 "test" => Subcommand::Test {
431 bless: matches.opt_present("bless"),
432 compare_mode: matches.opt_str("compare-mode"),
433 pass: matches.opt_str("pass"),
434 test_args: matches.opt_strs("test-args"),
435 rustc_args: matches.opt_strs("rustc-args"),
436 fail_fast: !matches.opt_present("no-fail-fast"),
437 rustfix_coverage: matches.opt_present("rustfix-coverage"),
438 doc_tests: if matches.opt_present("doc") {
440 } else if matches.opt_present("no-doc") {
446 "bench" => Subcommand::Bench {
448 test_args: matches.opt_strs("test-args"),
450 "doc" => Subcommand::Doc { paths },
452 if !paths.is_empty() {
453 println!("\nclean does not take a path argument\n");
454 usage(1, &opts, &subcommand_help, &extra_help);
458 all: matches.opt_present("all"),
463 check: matches.opt_present("check"),
466 "dist" => Subcommand::Dist { paths },
467 "install" => Subcommand::Install { paths },
469 usage(1, &opts, &subcommand_help, &extra_help);
474 verbose: matches.opt_count("verbose"),
475 stage: matches.opt_str("stage").map(|j| j.parse().expect("`stage` should be a number")),
476 dry_run: matches.opt_present("dry-run"),
477 on_fail: matches.opt_str("on-fail"),
478 rustc_error_format: matches.opt_str("error-format"),
479 keep_stage: matches.opt_strs("keep-stage")
480 .into_iter().map(|j| j.parse().expect("`keep-stage` should be a number"))
482 host: split(&matches.opt_strs("host"))
484 .map(|x| INTERNER.intern_string(x))
485 .collect::<Vec<_>>(),
486 target: split(&matches.opt_strs("target"))
488 .map(|x| INTERNER.intern_string(x))
489 .collect::<Vec<_>>(),
491 jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")),
493 incremental: matches.opt_present("incremental"),
494 exclude: split(&matches.opt_strs("exclude"))
497 .collect::<Vec<_>>(),
498 deny_warnings: parse_deny_warnings(&matches),
504 pub fn test_args(&self) -> Vec<&str> {
506 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
509 .flat_map(|s| s.split_whitespace())
516 pub fn rustc_args(&self) -> Vec<&str> {
518 Subcommand::Test { ref rustc_args, .. } => rustc_args
520 .flat_map(|s| s.split_whitespace())
526 pub fn fail_fast(&self) -> bool {
528 Subcommand::Test { fail_fast, .. } => fail_fast,
533 pub fn doc_tests(&self) -> DocTests {
535 Subcommand::Test { doc_tests, .. } => doc_tests,
540 pub fn bless(&self) -> bool {
542 Subcommand::Test { bless, .. } => bless,
547 pub fn rustfix_coverage(&self) -> bool {
549 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
554 pub fn compare_mode(&self) -> Option<&str> {
558 } => compare_mode.as_ref().map(|s| &s[..]),
563 pub fn pass(&self) -> Option<&str> {
567 } => pass.as_ref().map(|s| &s[..]),
573 fn split(s: &[String]) -> Vec<String> {
575 .flat_map(|s| s.split(','))
576 .map(|s| s.to_string())
580 fn parse_deny_warnings(matches: &getopts::Matches) -> Option<bool> {
581 match matches.opt_str("warnings").as_ref().map(|v| v.as_str()) {
582 Some("deny") => Some(true),
583 Some("warn") => Some(false),
586 r#"invalid value for --warnings: {:?}, expected "warn" or "deny""#,