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 => allow
40 pub deny_warnings: Option<bool>,
61 /// Whether to automatically update stderr/stdout files
63 compare_mode: Option<String>,
65 test_args: Vec<String>,
66 rustc_args: Vec<String>,
69 rustfix_coverage: bool,
73 test_args: Vec<String>,
86 impl Default for Subcommand {
87 fn default() -> Subcommand {
89 paths: vec![PathBuf::from("nowhere")],
95 pub fn parse(args: &[String]) -> Flags {
96 let mut extra_help = String::new();
97 let mut subcommand_help = String::from("\
98 Usage: x.py <subcommand> [options] [<paths>...]
101 build Compile either the compiler or libraries
102 check Compile either the compiler or libraries, using cargo check
105 test Build and run some test suites
106 bench Build and run some benchmarks
107 doc Build documentation
108 clean Clean out build directories
109 dist Build distribution artifacts
110 install Install distribution artifacts
112 To learn more about a subcommand, run `./x.py <subcommand> -h`"
115 let mut opts = Options::new();
116 // Options common to all subcommands
117 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
118 opts.optflag("i", "incremental", "use incremental compilation");
119 opts.optopt("", "config", "TOML configuration file for build", "FILE");
120 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
121 opts.optmulti("", "host", "host targets to build", "HOST");
122 opts.optmulti("", "target", "target targets to build", "TARGET");
123 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
124 opts.optopt("", "on-fail", "command to run on failure", "CMD");
125 opts.optflag("", "dry-run", "dry run; don't build anything");
126 opts.optopt("", "stage",
127 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
128 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
130 opts.optmulti("", "keep-stage", "stage(s) to keep without recompiling \
131 (pass multiple times to keep e.g., both stages 0 and 1)", "N");
132 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
133 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
134 opts.optflag("h", "help", "print this help message");
138 "if value is deny, will deny warnings, otherwise use default",
141 opts.optopt("", "error-format", "rustc error format", "FORMAT");
145 |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
146 println!("{}", opts.usage(subcommand_help));
147 if !extra_help.is_empty() {
148 println!("{}", extra_help);
150 process::exit(exit_code);
153 // We can't use getopt to parse the options until we have completed specifying which
154 // options are valid, but under the current implementation, some options are conditional on
155 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
156 // complete the definition of the options. Then we can use the getopt::Matches object from
158 let subcommand = args.iter().find(|&s| {
170 let subcommand = match subcommand {
173 // No or an invalid subcommand -- show the general usage and subcommand help
174 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
176 println!("{}\n", subcommand_help);
177 let exit_code = if args.is_empty() { 0 } else { 1 };
178 process::exit(exit_code);
182 // Some subcommands get extra options
183 match subcommand.as_str() {
185 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
186 opts.optmulti("", "test-args", "extra arguments", "ARGS");
190 "extra options to pass the compiler when running tests",
193 opts.optflag("", "no-doc", "do not run doc tests");
194 opts.optflag("", "doc", "only run doc tests");
198 "update all stderr/stdout files of failing ui tests",
203 "mode describing what file the actual ui output will be compared to",
209 "force {check,build,run}-pass tests to this mode.",
210 "check | build | run"
215 "enable this to generate a Rustfix coverage file, which is saved in \
216 `/<build_base>/rustfix_missing_coverage.txt`",
220 opts.optmulti("", "test-args", "extra arguments", "ARGS");
223 opts.optflag("", "all", "clean all build artifacts");
228 // Done specifying what options are possible, so do the getopts parsing
229 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
230 // Invalid argument/option format
231 println!("\n{}\n", e);
232 usage(1, &opts, &subcommand_help, &extra_help);
234 // Extra sanity check to make sure we didn't hit this crazy corner case:
236 // ./x.py --frobulate clean build
237 // ^-- option ^ ^- actual subcommand
238 // \_ arg to option could be mistaken as subcommand
239 let mut pass_sanity_check = true;
240 match matches.free.get(0) {
241 Some(check_subcommand) => {
242 if check_subcommand != subcommand {
243 pass_sanity_check = false;
247 pass_sanity_check = false;
250 if !pass_sanity_check {
251 println!("{}\n", subcommand_help);
253 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
254 You may need to move some options to after the subcommand.\n"
258 // Extra help text for some commands
259 match subcommand.as_str() {
261 subcommand_help.push_str(
264 This subcommand accepts a number of paths to directories to the crates
265 and/or artifacts to compile. For example:
267 ./x.py build src/libcore
268 ./x.py build src/libcore src/libproc_macro
269 ./x.py build src/libstd --stage 1
271 If no arguments are passed then the complete artifacts for that stage are
275 ./x.py build --stage 1
277 For a quick build of a usable compiler, you can pass:
279 ./x.py build --stage 1 src/libtest
281 This will first build everything once (like `--stage 0` without further
282 arguments would), and then use the compiler built in stage 0 to build
283 src/libtest and its dependencies.
284 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
288 subcommand_help.push_str(
291 This subcommand accepts a number of paths to directories to the crates
292 and/or artifacts to compile. For example:
294 ./x.py check src/libcore
295 ./x.py check src/libcore src/libproc_macro
297 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
298 also that since we use `cargo check`, by default this will automatically enable incremental
299 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
300 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
305 subcommand_help.push_str(
308 This subcommand accepts a number of paths to directories to the crates
309 and/or artifacts to run clippy against. For example:
311 ./x.py clippy src/libcore
312 ./x.py clippy src/libcore src/libproc_macro",
316 subcommand_help.push_str(
319 This subcommand accepts a number of paths to directories to the crates
320 and/or artifacts to run `cargo fix` against. For example:
322 ./x.py fix src/libcore
323 ./x.py fix src/libcore src/libproc_macro",
327 subcommand_help.push_str(
330 This subcommand accepts a number of paths to directories to tests that
331 should be compiled and run. For example:
333 ./x.py test src/test/ui
334 ./x.py test src/libstd --test-args hash_map
335 ./x.py test src/libstd --stage 0 --no-doc
336 ./x.py test src/test/ui --bless
337 ./x.py test src/test/ui --compare-mode nll
339 Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
340 just like `build src/libstd --stage N` it tests the compiler produced by the previous
343 If no arguments are passed then the complete artifacts for that stage are
347 ./x.py test --stage 1",
351 subcommand_help.push_str(
354 This subcommand accepts a number of paths to directories of documentation
355 to build. For example:
357 ./x.py doc src/doc/book
358 ./x.py doc src/doc/nomicon
359 ./x.py doc src/doc/book src/libstd
361 If no arguments are passed then everything is documented:
364 ./x.py doc --stage 1",
369 // Get any optional paths which occur after the subcommand
370 let paths = matches.free[1..]
373 .collect::<Vec<PathBuf>>();
375 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
376 if fs::metadata("config.toml").is_ok() {
377 Some(PathBuf::from("config.toml"))
383 // All subcommands except `clean` can have an optional "Available paths" section
384 if matches.opt_present("verbose") {
385 let config = Config::parse(&["build".to_string()]);
386 let mut build = Build::new(config);
387 metadata::build(&mut build);
389 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
390 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
391 } else if subcommand.as_str() != "clean" {
394 "Run `./x.py {} -h -v` to see a list of available paths.",
400 // User passed in -h/--help?
401 if matches.opt_present("help") {
402 usage(0, &opts, &subcommand_help, &extra_help);
405 let cmd = match subcommand.as_str() {
406 "build" => Subcommand::Build { paths },
407 "check" => Subcommand::Check { paths },
408 "clippy" => Subcommand::Clippy { paths },
409 "fix" => Subcommand::Fix { paths },
410 "test" => Subcommand::Test {
412 bless: matches.opt_present("bless"),
413 compare_mode: matches.opt_str("compare-mode"),
414 pass: matches.opt_str("pass"),
415 test_args: matches.opt_strs("test-args"),
416 rustc_args: matches.opt_strs("rustc-args"),
417 fail_fast: !matches.opt_present("no-fail-fast"),
418 rustfix_coverage: matches.opt_present("rustfix-coverage"),
419 doc_tests: if matches.opt_present("doc") {
421 } else if matches.opt_present("no-doc") {
427 "bench" => Subcommand::Bench {
429 test_args: matches.opt_strs("test-args"),
431 "doc" => Subcommand::Doc { paths },
433 if !paths.is_empty() {
434 println!("\nclean does not take a path argument\n");
435 usage(1, &opts, &subcommand_help, &extra_help);
439 all: matches.opt_present("all"),
442 "dist" => Subcommand::Dist { paths },
443 "install" => Subcommand::Install { paths },
445 usage(1, &opts, &subcommand_help, &extra_help);
450 verbose: matches.opt_count("verbose"),
451 stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
452 dry_run: matches.opt_present("dry-run"),
453 on_fail: matches.opt_str("on-fail"),
454 rustc_error_format: matches.opt_str("error-format"),
455 keep_stage: matches.opt_strs("keep-stage")
456 .into_iter().map(|j| j.parse().unwrap())
458 host: split(&matches.opt_strs("host"))
460 .map(|x| INTERNER.intern_string(x))
461 .collect::<Vec<_>>(),
462 target: split(&matches.opt_strs("target"))
464 .map(|x| INTERNER.intern_string(x))
465 .collect::<Vec<_>>(),
467 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
469 incremental: matches.opt_present("incremental"),
470 exclude: split(&matches.opt_strs("exclude"))
473 .collect::<Vec<_>>(),
474 deny_warnings: parse_deny_warnings(&matches),
480 pub fn test_args(&self) -> Vec<&str> {
482 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
485 .flat_map(|s| s.split_whitespace())
492 pub fn rustc_args(&self) -> Vec<&str> {
494 Subcommand::Test { ref rustc_args, .. } => rustc_args
496 .flat_map(|s| s.split_whitespace())
502 pub fn fail_fast(&self) -> bool {
504 Subcommand::Test { fail_fast, .. } => fail_fast,
509 pub fn doc_tests(&self) -> DocTests {
511 Subcommand::Test { doc_tests, .. } => doc_tests,
516 pub fn bless(&self) -> bool {
518 Subcommand::Test { bless, .. } => bless,
523 pub fn rustfix_coverage(&self) -> bool {
525 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
530 pub fn compare_mode(&self) -> Option<&str> {
534 } => compare_mode.as_ref().map(|s| &s[..]),
539 pub fn pass(&self) -> Option<&str> {
543 } => pass.as_ref().map(|s| &s[..]),
549 fn split(s: &[String]) -> Vec<String> {
551 .flat_map(|s| s.split(','))
552 .map(|s| s.to_string())
556 fn parse_deny_warnings(matches: &getopts::Matches) -> Option<bool> {
557 match matches.opt_str("warnings").as_ref().map(|v| v.as_str()) {
558 Some("deny") => Some(true),
559 Some("allow") => Some(false),
562 r#"invalid value for --warnings: {:?}, expected "allow" or "deny""#,