1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Command-line interface of the rustbuild build system.
13 //! This module implements the command-line parsing of the build system which
14 //! has various flags to configure how it's run.
17 use std::path::PathBuf;
25 use {Build, DocTests};
27 use cache::{Interned, INTERNER};
29 /// Deserialized version of all flags for this compile.
31 pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
32 pub on_fail: Option<String>,
33 pub stage: Option<u32>,
34 pub keep_stage: Vec<u32>,
36 pub host: Vec<Interned<String>>,
37 pub target: Vec<Interned<String>>,
38 pub config: Option<PathBuf>,
39 pub jobs: Option<u32>,
41 pub incremental: bool,
42 pub exclude: Vec<PathBuf>,
43 pub rustc_error_format: Option<String>,
47 pub warnings: Option<bool>,
62 /// Whether to automatically update stderr/stdout files
64 compare_mode: Option<String>,
65 test_args: Vec<String>,
66 rustc_args: Vec<String>,
72 test_args: Vec<String>,
85 impl Default for Subcommand {
86 fn default() -> Subcommand {
88 paths: vec![PathBuf::from("nowhere")],
94 pub fn parse(args: &[String]) -> Flags {
95 let mut extra_help = String::new();
96 let mut subcommand_help = String::from("\
97 Usage: x.py <subcommand> [options] [<paths>...]
100 build Compile either the compiler or libraries
101 check Compile either the compiler or libraries, using cargo check
102 test Build and run some test suites
103 bench Build and run some benchmarks
104 doc Build documentation
105 clean Clean out build directories
106 dist Build distribution artifacts
107 install Install distribution artifacts
109 To learn more about a subcommand, run `./x.py <subcommand> -h`"
112 let mut opts = Options::new();
113 // Options common to all subcommands
114 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
115 opts.optflag("i", "incremental", "use incremental compilation");
116 opts.optopt("", "config", "TOML configuration file for build", "FILE");
117 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
118 opts.optmulti("", "host", "host targets to build", "HOST");
119 opts.optmulti("", "target", "target targets to build", "TARGET");
120 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
121 opts.optopt("", "on-fail", "command to run on failure", "CMD");
122 opts.optflag("", "dry-run", "dry run; don't build anything");
123 opts.optopt("", "stage",
124 "stage to build (indicates compiler to use/test, e.g. stage 0 uses the \
125 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
127 opts.optmulti("", "keep-stage", "stage(s) to keep without recompiling \
128 (pass multiple times to keep e.g. both stages 0 and 1)", "N");
129 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
130 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
131 opts.optflag("h", "help", "print this help message");
135 "if value is deny, will deny warnings, otherwise use default",
138 opts.optopt("", "error-format", "rustc error format", "FORMAT");
142 |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
143 println!("{}", opts.usage(subcommand_help));
144 if !extra_help.is_empty() {
145 println!("{}", extra_help);
147 process::exit(exit_code);
150 // We can't use getopt to parse the options until we have completed specifying which
151 // options are valid, but under the current implementation, some options are conditional on
152 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
153 // complete the definition of the options. Then we can use the getopt::Matches object from
155 let subcommand = args.iter().find(|&s| {
165 let subcommand = match subcommand {
168 // No or an invalid subcommand -- show the general usage and subcommand help
169 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
171 println!("{}\n", subcommand_help);
172 let exit_code = if args.is_empty() { 0 } else { 1 };
173 process::exit(exit_code);
177 // Some subcommands get extra options
178 match subcommand.as_str() {
180 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
181 opts.optmulti("", "test-args", "extra arguments", "ARGS");
185 "extra options to pass the compiler when running tests",
188 opts.optflag("", "no-doc", "do not run doc tests");
189 opts.optflag("", "doc", "only run doc tests");
193 "update all stderr/stdout files of failing ui tests",
198 "mode describing what file the actual ui output will be compared to",
203 opts.optmulti("", "test-args", "extra arguments", "ARGS");
206 opts.optflag("", "all", "clean all build artifacts");
211 // Done specifying what options are possible, so do the getopts parsing
212 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
213 // Invalid argument/option format
214 println!("\n{}\n", e);
215 usage(1, &opts, &subcommand_help, &extra_help);
217 // Extra sanity check to make sure we didn't hit this crazy corner case:
219 // ./x.py --frobulate clean build
220 // ^-- option ^ ^- actual subcommand
221 // \_ arg to option could be mistaken as subcommand
222 let mut pass_sanity_check = true;
223 match matches.free.get(0) {
224 Some(check_subcommand) => {
225 if check_subcommand != subcommand {
226 pass_sanity_check = false;
230 pass_sanity_check = false;
233 if !pass_sanity_check {
234 println!("{}\n", subcommand_help);
236 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
237 You may need to move some options to after the subcommand.\n"
241 // Extra help text for some commands
242 match subcommand.as_str() {
244 subcommand_help.push_str(
247 This subcommand accepts a number of paths to directories to the crates
248 and/or artifacts to compile. For example:
250 ./x.py build src/libcore
251 ./x.py build src/libcore src/libproc_macro
252 ./x.py build src/libstd --stage 1
254 If no arguments are passed then the complete artifacts for that stage are
258 ./x.py build --stage 1
260 For a quick build of a usable compiler, you can pass:
262 ./x.py build --stage 1 src/libtest
264 This will first build everything once (like `--stage 0` without further
265 arguments would), and then use the compiler built in stage 0 to build
266 src/libtest and its dependencies.
267 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
271 subcommand_help.push_str(
274 This subcommand accepts a number of paths to directories to the crates
275 and/or artifacts to compile. For example:
277 ./x.py check src/libcore
278 ./x.py check src/libcore src/libproc_macro
280 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
281 also that since we use `cargo check`, by default this will automatically enable incremental
282 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
283 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
288 subcommand_help.push_str(
291 This subcommand accepts a number of paths to directories to tests that
292 should be compiled and run. For example:
294 ./x.py test src/test/run-pass
295 ./x.py test src/libstd --test-args hash_map
296 ./x.py test src/libstd --stage 0 --no-doc
297 ./x.py test src/test/ui --bless
298 ./x.py test src/test/ui --compare-mode nll
300 Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
301 just like `build src/libstd --stage N` it tests the compiler produced by the previous
304 If no arguments are passed then the complete artifacts for that stage are
308 ./x.py test --stage 1",
312 subcommand_help.push_str(
315 This subcommand accepts a number of paths to directories of documentation
316 to build. For example:
318 ./x.py doc src/doc/book
319 ./x.py doc src/doc/nomicon
320 ./x.py doc src/doc/book src/libstd
322 If no arguments are passed then everything is documented:
325 ./x.py doc --stage 1",
330 // Get any optional paths which occur after the subcommand
331 let paths = matches.free[1..]
334 .collect::<Vec<PathBuf>>();
336 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
337 if fs::metadata("config.toml").is_ok() {
338 Some(PathBuf::from("config.toml"))
344 // All subcommands except `clean` can have an optional "Available paths" section
345 if matches.opt_present("verbose") {
346 let config = Config::parse(&["build".to_string()]);
347 let mut build = Build::new(config);
348 metadata::build(&mut build);
350 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
351 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
352 } else if subcommand.as_str() != "clean" {
355 "Run `./x.py {} -h -v` to see a list of available paths.",
361 // User passed in -h/--help?
362 if matches.opt_present("help") {
363 usage(0, &opts, &subcommand_help, &extra_help);
366 let cmd = match subcommand.as_str() {
367 "build" => Subcommand::Build { paths },
368 "check" => Subcommand::Check { paths },
369 "test" => Subcommand::Test {
371 bless: matches.opt_present("bless"),
372 compare_mode: matches.opt_str("compare-mode"),
373 test_args: matches.opt_strs("test-args"),
374 rustc_args: matches.opt_strs("rustc-args"),
375 fail_fast: !matches.opt_present("no-fail-fast"),
376 doc_tests: if matches.opt_present("doc") {
378 } else if matches.opt_present("no-doc") {
384 "bench" => Subcommand::Bench {
386 test_args: matches.opt_strs("test-args"),
388 "doc" => Subcommand::Doc { paths },
390 if !paths.is_empty() {
391 println!("\nclean does not take a path argument\n");
392 usage(1, &opts, &subcommand_help, &extra_help);
396 all: matches.opt_present("all"),
399 "dist" => Subcommand::Dist { paths },
400 "install" => Subcommand::Install { paths },
402 usage(1, &opts, &subcommand_help, &extra_help);
407 verbose: matches.opt_count("verbose"),
408 stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
409 dry_run: matches.opt_present("dry-run"),
410 on_fail: matches.opt_str("on-fail"),
411 rustc_error_format: matches.opt_str("error-format"),
412 keep_stage: matches.opt_strs("keep-stage")
413 .into_iter().map(|j| j.parse().unwrap())
415 host: split(&matches.opt_strs("host"))
417 .map(|x| INTERNER.intern_string(x))
418 .collect::<Vec<_>>(),
419 target: split(&matches.opt_strs("target"))
421 .map(|x| INTERNER.intern_string(x))
422 .collect::<Vec<_>>(),
424 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
426 incremental: matches.opt_present("incremental"),
427 exclude: split(&matches.opt_strs("exclude"))
430 .collect::<Vec<_>>(),
431 warnings: matches.opt_str("warnings").map(|v| v == "deny"),
437 pub fn test_args(&self) -> Vec<&str> {
439 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
442 .flat_map(|s| s.split_whitespace())
449 pub fn rustc_args(&self) -> Vec<&str> {
451 Subcommand::Test { ref rustc_args, .. } => rustc_args
453 .flat_map(|s| s.split_whitespace())
459 pub fn fail_fast(&self) -> bool {
461 Subcommand::Test { fail_fast, .. } => fail_fast,
466 pub fn doc_tests(&self) -> DocTests {
468 Subcommand::Test { doc_tests, .. } => doc_tests,
473 pub fn bless(&self) -> bool {
475 Subcommand::Test { bless, .. } => bless,
480 pub fn compare_mode(&self) -> Option<&str> {
484 } => compare_mode.as_ref().map(|s| &s[..]),
490 fn split(s: &[String]) -> Vec<String> {
492 .flat_map(|s| s.split(','))
493 .map(|s| s.to_string())