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>,
37 pub warnings: Option<bool>,
58 /// Whether to automatically update stderr/stdout files
60 compare_mode: Option<String>,
61 test_args: Vec<String>,
62 rustc_args: Vec<String>,
65 rustfix_coverage: bool,
69 test_args: Vec<String>,
82 impl Default for Subcommand {
83 fn default() -> Subcommand {
85 paths: vec![PathBuf::from("nowhere")],
91 pub fn parse(args: &[String]) -> Flags {
92 let mut extra_help = String::new();
93 let mut subcommand_help = String::from("\
94 Usage: x.py <subcommand> [options] [<paths>...]
97 build Compile either the compiler or libraries
98 check Compile either the compiler or libraries, using cargo check
101 test Build and run some test suites
102 bench Build and run some benchmarks
103 doc Build documentation
104 clean Clean out build directories
105 dist Build distribution artifacts
106 install Install distribution artifacts
108 To learn more about a subcommand, run `./x.py <subcommand> -h`"
111 let mut opts = Options::new();
112 // Options common to all subcommands
113 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
114 opts.optflag("i", "incremental", "use incremental compilation");
115 opts.optopt("", "config", "TOML configuration file for build", "FILE");
116 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
117 opts.optmulti("", "host", "host targets to build", "HOST");
118 opts.optmulti("", "target", "target targets to build", "TARGET");
119 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
120 opts.optopt("", "on-fail", "command to run on failure", "CMD");
121 opts.optflag("", "dry-run", "dry run; don't build anything");
122 opts.optopt("", "stage",
123 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
124 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
126 opts.optmulti("", "keep-stage", "stage(s) to keep without recompiling \
127 (pass multiple times to keep e.g., both stages 0 and 1)", "N");
128 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
129 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
130 opts.optflag("h", "help", "print this help message");
134 "if value is deny, will deny warnings, otherwise use default",
137 opts.optopt("", "error-format", "rustc error format", "FORMAT");
141 |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
142 println!("{}", opts.usage(subcommand_help));
143 if !extra_help.is_empty() {
144 println!("{}", extra_help);
146 process::exit(exit_code);
149 // We can't use getopt to parse the options until we have completed specifying which
150 // options are valid, but under the current implementation, some options are conditional on
151 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
152 // complete the definition of the options. Then we can use the getopt::Matches object from
154 let subcommand = args.iter().find(|&s| {
166 let subcommand = match subcommand {
169 // No or an invalid subcommand -- show the general usage and subcommand help
170 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
172 println!("{}\n", subcommand_help);
173 let exit_code = if args.is_empty() { 0 } else { 1 };
174 process::exit(exit_code);
178 // Some subcommands get extra options
179 match subcommand.as_str() {
181 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
182 opts.optmulti("", "test-args", "extra arguments", "ARGS");
186 "extra options to pass the compiler when running tests",
189 opts.optflag("", "no-doc", "do not run doc tests");
190 opts.optflag("", "doc", "only run doc tests");
194 "update all stderr/stdout files of failing ui tests",
199 "mode describing what file the actual ui output will be compared to",
205 "enable this to generate a Rustfix coverage file, which is saved in \
206 `/<build_base>/rustfix_missing_coverage.txt`",
210 opts.optmulti("", "test-args", "extra arguments", "ARGS");
213 opts.optflag("", "all", "clean all build artifacts");
218 // Done specifying what options are possible, so do the getopts parsing
219 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
220 // Invalid argument/option format
221 println!("\n{}\n", e);
222 usage(1, &opts, &subcommand_help, &extra_help);
224 // Extra sanity check to make sure we didn't hit this crazy corner case:
226 // ./x.py --frobulate clean build
227 // ^-- option ^ ^- actual subcommand
228 // \_ arg to option could be mistaken as subcommand
229 let mut pass_sanity_check = true;
230 match matches.free.get(0) {
231 Some(check_subcommand) => {
232 if check_subcommand != subcommand {
233 pass_sanity_check = false;
237 pass_sanity_check = false;
240 if !pass_sanity_check {
241 println!("{}\n", subcommand_help);
243 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
244 You may need to move some options to after the subcommand.\n"
248 // Extra help text for some commands
249 match subcommand.as_str() {
251 subcommand_help.push_str(
254 This subcommand accepts a number of paths to directories to the crates
255 and/or artifacts to compile. For example:
257 ./x.py build src/libcore
258 ./x.py build src/libcore src/libproc_macro
259 ./x.py build src/libstd --stage 1
261 If no arguments are passed then the complete artifacts for that stage are
265 ./x.py build --stage 1
267 For a quick build of a usable compiler, you can pass:
269 ./x.py build --stage 1 src/libtest
271 This will first build everything once (like `--stage 0` without further
272 arguments would), and then use the compiler built in stage 0 to build
273 src/libtest and its dependencies.
274 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
278 subcommand_help.push_str(
281 This subcommand accepts a number of paths to directories to the crates
282 and/or artifacts to compile. For example:
284 ./x.py check src/libcore
285 ./x.py check src/libcore src/libproc_macro
287 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
288 also that since we use `cargo check`, by default this will automatically enable incremental
289 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
290 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
295 subcommand_help.push_str(
298 This subcommand accepts a number of paths to directories to the crates
299 and/or artifacts to run clippy against. For example:
301 ./x.py clippy src/libcore
302 ./x.py clippy src/libcore src/libproc_macro",
306 subcommand_help.push_str(
309 This subcommand accepts a number of paths to directories to the crates
310 and/or artifacts to run `cargo fix` against. For example:
312 ./x.py fix src/libcore
313 ./x.py fix src/libcore src/libproc_macro",
317 subcommand_help.push_str(
320 This subcommand accepts a number of paths to directories to tests that
321 should be compiled and run. For example:
323 ./x.py test src/test/run-pass
324 ./x.py test src/libstd --test-args hash_map
325 ./x.py test src/libstd --stage 0 --no-doc
326 ./x.py test src/test/ui --bless
327 ./x.py test src/test/ui --compare-mode nll
329 Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
330 just like `build src/libstd --stage N` it tests the compiler produced by the previous
333 If no arguments are passed then the complete artifacts for that stage are
337 ./x.py test --stage 1",
341 subcommand_help.push_str(
344 This subcommand accepts a number of paths to directories of documentation
345 to build. For example:
347 ./x.py doc src/doc/book
348 ./x.py doc src/doc/nomicon
349 ./x.py doc src/doc/book src/libstd
351 If no arguments are passed then everything is documented:
354 ./x.py doc --stage 1",
359 // Get any optional paths which occur after the subcommand
360 let paths = matches.free[1..]
363 .collect::<Vec<PathBuf>>();
365 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
366 if fs::metadata("config.toml").is_ok() {
367 Some(PathBuf::from("config.toml"))
373 // All subcommands except `clean` can have an optional "Available paths" section
374 if matches.opt_present("verbose") {
375 let config = Config::parse(&["build".to_string()]);
376 let mut build = Build::new(config);
377 metadata::build(&mut build);
379 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
380 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
381 } else if subcommand.as_str() != "clean" {
384 "Run `./x.py {} -h -v` to see a list of available paths.",
390 // User passed in -h/--help?
391 if matches.opt_present("help") {
392 usage(0, &opts, &subcommand_help, &extra_help);
395 let cmd = match subcommand.as_str() {
396 "build" => Subcommand::Build { paths },
397 "check" => Subcommand::Check { paths },
398 "clippy" => Subcommand::Clippy { paths },
399 "fix" => Subcommand::Fix { paths },
400 "test" => Subcommand::Test {
402 bless: matches.opt_present("bless"),
403 compare_mode: matches.opt_str("compare-mode"),
404 test_args: matches.opt_strs("test-args"),
405 rustc_args: matches.opt_strs("rustc-args"),
406 fail_fast: !matches.opt_present("no-fail-fast"),
407 rustfix_coverage: matches.opt_present("rustfix-coverage"),
408 doc_tests: if matches.opt_present("doc") {
410 } else if matches.opt_present("no-doc") {
416 "bench" => Subcommand::Bench {
418 test_args: matches.opt_strs("test-args"),
420 "doc" => Subcommand::Doc { paths },
422 if !paths.is_empty() {
423 println!("\nclean does not take a path argument\n");
424 usage(1, &opts, &subcommand_help, &extra_help);
428 all: matches.opt_present("all"),
431 "dist" => Subcommand::Dist { paths },
432 "install" => Subcommand::Install { paths },
434 usage(1, &opts, &subcommand_help, &extra_help);
439 verbose: matches.opt_count("verbose"),
440 stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
441 dry_run: matches.opt_present("dry-run"),
442 on_fail: matches.opt_str("on-fail"),
443 rustc_error_format: matches.opt_str("error-format"),
444 keep_stage: matches.opt_strs("keep-stage")
445 .into_iter().map(|j| j.parse().unwrap())
447 host: split(&matches.opt_strs("host"))
449 .map(|x| INTERNER.intern_string(x))
450 .collect::<Vec<_>>(),
451 target: split(&matches.opt_strs("target"))
453 .map(|x| INTERNER.intern_string(x))
454 .collect::<Vec<_>>(),
456 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
458 incremental: matches.opt_present("incremental"),
459 exclude: split(&matches.opt_strs("exclude"))
462 .collect::<Vec<_>>(),
463 warnings: matches.opt_str("warnings").map(|v| v == "deny"),
469 pub fn test_args(&self) -> Vec<&str> {
471 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
474 .flat_map(|s| s.split_whitespace())
481 pub fn rustc_args(&self) -> Vec<&str> {
483 Subcommand::Test { ref rustc_args, .. } => rustc_args
485 .flat_map(|s| s.split_whitespace())
491 pub fn fail_fast(&self) -> bool {
493 Subcommand::Test { fail_fast, .. } => fail_fast,
498 pub fn doc_tests(&self) -> DocTests {
500 Subcommand::Test { doc_tests, .. } => doc_tests,
505 pub fn bless(&self) -> bool {
507 Subcommand::Test { bless, .. } => bless,
512 pub fn rustfix_coverage(&self) -> bool {
514 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
519 pub fn compare_mode(&self) -> Option<&str> {
523 } => compare_mode.as_ref().map(|s| &s[..]),
529 fn split(s: &[String]) -> Vec<String> {
531 .flat_map(|s| s.split(','))
532 .map(|s| s.to_string())