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>,
52 /// Whether to automatically update stderr/stdout files
54 compare_mode: Option<String>,
55 test_args: Vec<String>,
56 rustc_args: Vec<String>,
59 rustfix_coverage: bool,
63 test_args: Vec<String>,
76 impl Default for Subcommand {
77 fn default() -> Subcommand {
79 paths: vec![PathBuf::from("nowhere")],
85 pub fn parse(args: &[String]) -> Flags {
86 let mut extra_help = String::new();
87 let mut subcommand_help = String::from("\
88 Usage: x.py <subcommand> [options] [<paths>...]
91 build Compile either the compiler or libraries
92 check Compile either the compiler or libraries, using cargo check
93 test Build and run some test suites
94 bench Build and run some benchmarks
95 doc Build documentation
96 clean Clean out build directories
97 dist Build distribution artifacts
98 install Install distribution artifacts
100 To learn more about a subcommand, run `./x.py <subcommand> -h`"
103 let mut opts = Options::new();
104 // Options common to all subcommands
105 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
106 opts.optflag("i", "incremental", "use incremental compilation");
107 opts.optopt("", "config", "TOML configuration file for build", "FILE");
108 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
109 opts.optmulti("", "host", "host targets to build", "HOST");
110 opts.optmulti("", "target", "target targets to build", "TARGET");
111 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
112 opts.optopt("", "on-fail", "command to run on failure", "CMD");
113 opts.optflag("", "dry-run", "dry run; don't build anything");
114 opts.optopt("", "stage",
115 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
116 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
118 opts.optmulti("", "keep-stage", "stage(s) to keep without recompiling \
119 (pass multiple times to keep e.g., both stages 0 and 1)", "N");
120 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
121 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
122 opts.optflag("h", "help", "print this help message");
126 "if value is deny, will deny warnings, otherwise use default",
129 opts.optopt("", "error-format", "rustc error format", "FORMAT");
133 |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
134 println!("{}", opts.usage(subcommand_help));
135 if !extra_help.is_empty() {
136 println!("{}", extra_help);
138 process::exit(exit_code);
141 // We can't use getopt to parse the options until we have completed specifying which
142 // options are valid, but under the current implementation, some options are conditional on
143 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
144 // complete the definition of the options. Then we can use the getopt::Matches object from
146 let subcommand = args.iter().find(|&s| {
156 let subcommand = match subcommand {
159 // No or an invalid subcommand -- show the general usage and subcommand help
160 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
162 println!("{}\n", subcommand_help);
163 let exit_code = if args.is_empty() { 0 } else { 1 };
164 process::exit(exit_code);
168 // Some subcommands get extra options
169 match subcommand.as_str() {
171 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
172 opts.optmulti("", "test-args", "extra arguments", "ARGS");
176 "extra options to pass the compiler when running tests",
179 opts.optflag("", "no-doc", "do not run doc tests");
180 opts.optflag("", "doc", "only run doc tests");
184 "update all stderr/stdout files of failing ui tests",
189 "mode describing what file the actual ui output will be compared to",
195 "enable this to generate a Rustfix coverage file, which is saved in \
196 `/<build_base>/rustfix_missing_coverage.txt`",
200 opts.optmulti("", "test-args", "extra arguments", "ARGS");
203 opts.optflag("", "all", "clean all build artifacts");
208 // Done specifying what options are possible, so do the getopts parsing
209 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
210 // Invalid argument/option format
211 println!("\n{}\n", e);
212 usage(1, &opts, &subcommand_help, &extra_help);
214 // Extra sanity check to make sure we didn't hit this crazy corner case:
216 // ./x.py --frobulate clean build
217 // ^-- option ^ ^- actual subcommand
218 // \_ arg to option could be mistaken as subcommand
219 let mut pass_sanity_check = true;
220 match matches.free.get(0) {
221 Some(check_subcommand) => {
222 if check_subcommand != subcommand {
223 pass_sanity_check = false;
227 pass_sanity_check = false;
230 if !pass_sanity_check {
231 println!("{}\n", subcommand_help);
233 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
234 You may need to move some options to after the subcommand.\n"
238 // Extra help text for some commands
239 match subcommand.as_str() {
241 subcommand_help.push_str(
244 This subcommand accepts a number of paths to directories to the crates
245 and/or artifacts to compile. For example:
247 ./x.py build src/libcore
248 ./x.py build src/libcore src/libproc_macro
249 ./x.py build src/libstd --stage 1
251 If no arguments are passed then the complete artifacts for that stage are
255 ./x.py build --stage 1
257 For a quick build of a usable compiler, you can pass:
259 ./x.py build --stage 1 src/libtest
261 This will first build everything once (like `--stage 0` without further
262 arguments would), and then use the compiler built in stage 0 to build
263 src/libtest and its dependencies.
264 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
268 subcommand_help.push_str(
271 This subcommand accepts a number of paths to directories to the crates
272 and/or artifacts to compile. For example:
274 ./x.py check src/libcore
275 ./x.py check src/libcore src/libproc_macro
277 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
278 also that since we use `cargo check`, by default this will automatically enable incremental
279 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
280 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
285 subcommand_help.push_str(
288 This subcommand accepts a number of paths to directories to tests that
289 should be compiled and run. For example:
291 ./x.py test src/test/run-pass
292 ./x.py test src/libstd --test-args hash_map
293 ./x.py test src/libstd --stage 0 --no-doc
294 ./x.py test src/test/ui --bless
295 ./x.py test src/test/ui --compare-mode nll
297 Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
298 just like `build src/libstd --stage N` it tests the compiler produced by the previous
301 If no arguments are passed then the complete artifacts for that stage are
305 ./x.py test --stage 1",
309 subcommand_help.push_str(
312 This subcommand accepts a number of paths to directories of documentation
313 to build. For example:
315 ./x.py doc src/doc/book
316 ./x.py doc src/doc/nomicon
317 ./x.py doc src/doc/book src/libstd
319 If no arguments are passed then everything is documented:
322 ./x.py doc --stage 1",
327 // Get any optional paths which occur after the subcommand
328 let paths = matches.free[1..]
331 .collect::<Vec<PathBuf>>();
333 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
334 if fs::metadata("config.toml").is_ok() {
335 Some(PathBuf::from("config.toml"))
341 // All subcommands except `clean` can have an optional "Available paths" section
342 if matches.opt_present("verbose") {
343 let config = Config::parse(&["build".to_string()]);
344 let mut build = Build::new(config);
345 metadata::build(&mut build);
347 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
348 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
349 } else if subcommand.as_str() != "clean" {
352 "Run `./x.py {} -h -v` to see a list of available paths.",
358 // User passed in -h/--help?
359 if matches.opt_present("help") {
360 usage(0, &opts, &subcommand_help, &extra_help);
363 let cmd = match subcommand.as_str() {
364 "build" => Subcommand::Build { paths },
365 "check" => Subcommand::Check { paths },
366 "test" => Subcommand::Test {
368 bless: matches.opt_present("bless"),
369 compare_mode: matches.opt_str("compare-mode"),
370 test_args: matches.opt_strs("test-args"),
371 rustc_args: matches.opt_strs("rustc-args"),
372 fail_fast: !matches.opt_present("no-fail-fast"),
373 rustfix_coverage: matches.opt_present("rustfix-coverage"),
374 doc_tests: if matches.opt_present("doc") {
376 } else if matches.opt_present("no-doc") {
382 "bench" => Subcommand::Bench {
384 test_args: matches.opt_strs("test-args"),
386 "doc" => Subcommand::Doc { paths },
388 if !paths.is_empty() {
389 println!("\nclean does not take a path argument\n");
390 usage(1, &opts, &subcommand_help, &extra_help);
394 all: matches.opt_present("all"),
397 "dist" => Subcommand::Dist { paths },
398 "install" => Subcommand::Install { paths },
400 usage(1, &opts, &subcommand_help, &extra_help);
405 verbose: matches.opt_count("verbose"),
406 stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
407 dry_run: matches.opt_present("dry-run"),
408 on_fail: matches.opt_str("on-fail"),
409 rustc_error_format: matches.opt_str("error-format"),
410 keep_stage: matches.opt_strs("keep-stage")
411 .into_iter().map(|j| j.parse().unwrap())
413 host: split(&matches.opt_strs("host"))
415 .map(|x| INTERNER.intern_string(x))
416 .collect::<Vec<_>>(),
417 target: split(&matches.opt_strs("target"))
419 .map(|x| INTERNER.intern_string(x))
420 .collect::<Vec<_>>(),
422 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
424 incremental: matches.opt_present("incremental"),
425 exclude: split(&matches.opt_strs("exclude"))
428 .collect::<Vec<_>>(),
429 warnings: matches.opt_str("warnings").map(|v| v == "deny"),
435 pub fn test_args(&self) -> Vec<&str> {
437 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
440 .flat_map(|s| s.split_whitespace())
447 pub fn rustc_args(&self) -> Vec<&str> {
449 Subcommand::Test { ref rustc_args, .. } => rustc_args
451 .flat_map(|s| s.split_whitespace())
457 pub fn fail_fast(&self) -> bool {
459 Subcommand::Test { fail_fast, .. } => fail_fast,
464 pub fn doc_tests(&self) -> DocTests {
466 Subcommand::Test { doc_tests, .. } => doc_tests,
471 pub fn bless(&self) -> bool {
473 Subcommand::Test { bless, .. } => bless,
478 pub fn rustfix_coverage(&self) -> bool {
480 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
485 pub fn compare_mode(&self) -> Option<&str> {
489 } => compare_mode.as_ref().map(|s| &s[..]),
495 fn split(s: &[String]) -> Vec<String> {
497 .flat_map(|s| s.split(','))
498 .map(|s| s.to_string())