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>,
62 test_args: Vec<String>,
75 impl Default for Subcommand {
76 fn default() -> Subcommand {
78 paths: vec![PathBuf::from("nowhere")],
84 pub fn parse(args: &[String]) -> Flags {
85 let mut extra_help = String::new();
86 let mut subcommand_help = String::from("\
87 Usage: x.py <subcommand> [options] [<paths>...]
90 build Compile either the compiler or libraries
91 check Compile either the compiler or libraries, using cargo check
92 test Build and run some test suites
93 bench Build and run some benchmarks
94 doc Build documentation
95 clean Clean out build directories
96 dist Build distribution artifacts
97 install Install distribution artifacts
99 To learn more about a subcommand, run `./x.py <subcommand> -h`"
102 let mut opts = Options::new();
103 // Options common to all subcommands
104 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
105 opts.optflag("i", "incremental", "use incremental compilation");
106 opts.optopt("", "config", "TOML configuration file for build", "FILE");
107 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
108 opts.optmulti("", "host", "host targets to build", "HOST");
109 opts.optmulti("", "target", "target targets to build", "TARGET");
110 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
111 opts.optopt("", "on-fail", "command to run on failure", "CMD");
112 opts.optflag("", "dry-run", "dry run; don't build anything");
113 opts.optopt("", "stage",
114 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
115 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
117 opts.optmulti("", "keep-stage", "stage(s) to keep without recompiling \
118 (pass multiple times to keep e.g., both stages 0 and 1)", "N");
119 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
120 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
121 opts.optflag("h", "help", "print this help message");
125 "if value is deny, will deny warnings, otherwise use default",
128 opts.optopt("", "error-format", "rustc error format", "FORMAT");
132 |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
133 println!("{}", opts.usage(subcommand_help));
134 if !extra_help.is_empty() {
135 println!("{}", extra_help);
137 process::exit(exit_code);
140 // We can't use getopt to parse the options until we have completed specifying which
141 // options are valid, but under the current implementation, some options are conditional on
142 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
143 // complete the definition of the options. Then we can use the getopt::Matches object from
145 let subcommand = args.iter().find(|&s| {
155 let subcommand = match subcommand {
158 // No or an invalid subcommand -- show the general usage and subcommand help
159 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
161 println!("{}\n", subcommand_help);
162 let exit_code = if args.is_empty() { 0 } else { 1 };
163 process::exit(exit_code);
167 // Some subcommands get extra options
168 match subcommand.as_str() {
170 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
171 opts.optmulti("", "test-args", "extra arguments", "ARGS");
175 "extra options to pass the compiler when running tests",
178 opts.optflag("", "no-doc", "do not run doc tests");
179 opts.optflag("", "doc", "only run doc tests");
183 "update all stderr/stdout files of failing ui tests",
188 "mode describing what file the actual ui output will be compared to",
193 opts.optmulti("", "test-args", "extra arguments", "ARGS");
196 opts.optflag("", "all", "clean all build artifacts");
201 // Done specifying what options are possible, so do the getopts parsing
202 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
203 // Invalid argument/option format
204 println!("\n{}\n", e);
205 usage(1, &opts, &subcommand_help, &extra_help);
207 // Extra sanity check to make sure we didn't hit this crazy corner case:
209 // ./x.py --frobulate clean build
210 // ^-- option ^ ^- actual subcommand
211 // \_ arg to option could be mistaken as subcommand
212 let mut pass_sanity_check = true;
213 match matches.free.get(0) {
214 Some(check_subcommand) => {
215 if check_subcommand != subcommand {
216 pass_sanity_check = false;
220 pass_sanity_check = false;
223 if !pass_sanity_check {
224 println!("{}\n", subcommand_help);
226 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
227 You may need to move some options to after the subcommand.\n"
231 // Extra help text for some commands
232 match subcommand.as_str() {
234 subcommand_help.push_str(
237 This subcommand accepts a number of paths to directories to the crates
238 and/or artifacts to compile. For example:
240 ./x.py build src/libcore
241 ./x.py build src/libcore src/libproc_macro
242 ./x.py build src/libstd --stage 1
244 If no arguments are passed then the complete artifacts for that stage are
248 ./x.py build --stage 1
250 For a quick build of a usable compiler, you can pass:
252 ./x.py build --stage 1 src/libtest
254 This will first build everything once (like `--stage 0` without further
255 arguments would), and then use the compiler built in stage 0 to build
256 src/libtest and its dependencies.
257 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
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 check src/libcore
268 ./x.py check src/libcore src/libproc_macro
270 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
271 also that since we use `cargo check`, by default this will automatically enable incremental
272 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
273 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
278 subcommand_help.push_str(
281 This subcommand accepts a number of paths to directories to tests that
282 should be compiled and run. For example:
284 ./x.py test src/test/run-pass
285 ./x.py test src/libstd --test-args hash_map
286 ./x.py test src/libstd --stage 0 --no-doc
287 ./x.py test src/test/ui --bless
288 ./x.py test src/test/ui --compare-mode nll
290 Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
291 just like `build src/libstd --stage N` it tests the compiler produced by the previous
294 If no arguments are passed then the complete artifacts for that stage are
298 ./x.py test --stage 1",
302 subcommand_help.push_str(
305 This subcommand accepts a number of paths to directories of documentation
306 to build. For example:
308 ./x.py doc src/doc/book
309 ./x.py doc src/doc/nomicon
310 ./x.py doc src/doc/book src/libstd
312 If no arguments are passed then everything is documented:
315 ./x.py doc --stage 1",
320 // Get any optional paths which occur after the subcommand
321 let paths = matches.free[1..]
324 .collect::<Vec<PathBuf>>();
326 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
327 if fs::metadata("config.toml").is_ok() {
328 Some(PathBuf::from("config.toml"))
334 // All subcommands except `clean` can have an optional "Available paths" section
335 if matches.opt_present("verbose") {
336 let config = Config::parse(&["build".to_string()]);
337 let mut build = Build::new(config);
338 metadata::build(&mut build);
340 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
341 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
342 } else if subcommand.as_str() != "clean" {
345 "Run `./x.py {} -h -v` to see a list of available paths.",
351 // User passed in -h/--help?
352 if matches.opt_present("help") {
353 usage(0, &opts, &subcommand_help, &extra_help);
356 let cmd = match subcommand.as_str() {
357 "build" => Subcommand::Build { paths },
358 "check" => Subcommand::Check { paths },
359 "test" => Subcommand::Test {
361 bless: matches.opt_present("bless"),
362 compare_mode: matches.opt_str("compare-mode"),
363 test_args: matches.opt_strs("test-args"),
364 rustc_args: matches.opt_strs("rustc-args"),
365 fail_fast: !matches.opt_present("no-fail-fast"),
366 doc_tests: if matches.opt_present("doc") {
368 } else if matches.opt_present("no-doc") {
374 "bench" => Subcommand::Bench {
376 test_args: matches.opt_strs("test-args"),
378 "doc" => Subcommand::Doc { paths },
380 if !paths.is_empty() {
381 println!("\nclean does not take a path argument\n");
382 usage(1, &opts, &subcommand_help, &extra_help);
386 all: matches.opt_present("all"),
389 "dist" => Subcommand::Dist { paths },
390 "install" => Subcommand::Install { paths },
392 usage(1, &opts, &subcommand_help, &extra_help);
397 verbose: matches.opt_count("verbose"),
398 stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
399 dry_run: matches.opt_present("dry-run"),
400 on_fail: matches.opt_str("on-fail"),
401 rustc_error_format: matches.opt_str("error-format"),
402 keep_stage: matches.opt_strs("keep-stage")
403 .into_iter().map(|j| j.parse().unwrap())
405 host: split(&matches.opt_strs("host"))
407 .map(|x| INTERNER.intern_string(x))
408 .collect::<Vec<_>>(),
409 target: split(&matches.opt_strs("target"))
411 .map(|x| INTERNER.intern_string(x))
412 .collect::<Vec<_>>(),
414 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
416 incremental: matches.opt_present("incremental"),
417 exclude: split(&matches.opt_strs("exclude"))
420 .collect::<Vec<_>>(),
421 warnings: matches.opt_str("warnings").map(|v| v == "deny"),
427 pub fn test_args(&self) -> Vec<&str> {
429 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
432 .flat_map(|s| s.split_whitespace())
439 pub fn rustc_args(&self) -> Vec<&str> {
441 Subcommand::Test { ref rustc_args, .. } => rustc_args
443 .flat_map(|s| s.split_whitespace())
449 pub fn fail_fast(&self) -> bool {
451 Subcommand::Test { fail_fast, .. } => fail_fast,
456 pub fn doc_tests(&self) -> DocTests {
458 Subcommand::Test { doc_tests, .. } => doc_tests,
463 pub fn bless(&self) -> bool {
465 Subcommand::Test { bless, .. } => bless,
470 pub fn compare_mode(&self) -> Option<&str> {
474 } => compare_mode.as_ref().map(|s| &s[..]),
480 fn split(s: &[String]) -> Vec<String> {
482 .flat_map(|s| s.split(','))
483 .map(|s| s.to_string())