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 = format!(
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
103 test Build and run some test suites
104 bench Build and run some benchmarks
105 doc Build documentation
106 clean Clean out build directories
107 dist Build distribution artifacts
108 install Install distribution artifacts
110 To learn more about a subcommand, run `./x.py <subcommand> -h`"
113 let mut opts = Options::new();
114 // Options common to all subcommands
115 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
116 opts.optflag("i", "incremental", "use incremental compilation");
117 opts.optopt("", "config", "TOML configuration file for build", "FILE");
118 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
119 opts.optmulti("", "host", "host targets to build", "HOST");
120 opts.optmulti("", "target", "target targets to build", "TARGET");
121 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
122 opts.optopt("", "on-fail", "command to run on failure", "CMD");
123 opts.optflag("", "dry-run", "dry run; don't build anything");
124 opts.optopt("", "stage",
125 "stage to build (indicates compiler to use/test, e.g. stage 0 uses the \
126 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
128 opts.optmulti("", "keep-stage", "stage(s) to keep without recompiling \
129 (pass multiple times to keep e.g. both stages 0 and 1)", "N");
130 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
131 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
132 opts.optflag("h", "help", "print this help message");
136 "if value is deny, will deny warnings, otherwise use default",
139 opts.optopt("", "error-format", "rustc error format", "FORMAT");
143 |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
144 println!("{}", opts.usage(subcommand_help));
145 if !extra_help.is_empty() {
146 println!("{}", extra_help);
148 process::exit(exit_code);
151 // We can't use getopt to parse the options until we have completed specifying which
152 // options are valid, but under the current implementation, some options are conditional on
153 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
154 // complete the definition of the options. Then we can use the getopt::Matches object from
156 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",
204 opts.optmulti("", "test-args", "extra arguments", "ARGS");
207 opts.optflag("", "all", "clean all build artifacts");
212 // Done specifying what options are possible, so do the getopts parsing
213 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
214 // Invalid argument/option format
215 println!("\n{}\n", e);
216 usage(1, &opts, &subcommand_help, &extra_help);
218 // Extra sanity check to make sure we didn't hit this crazy corner case:
220 // ./x.py --frobulate clean build
221 // ^-- option ^ ^- actual subcommand
222 // \_ arg to option could be mistaken as subcommand
223 let mut pass_sanity_check = true;
224 match matches.free.get(0) {
225 Some(check_subcommand) => {
226 if check_subcommand != subcommand {
227 pass_sanity_check = false;
231 pass_sanity_check = false;
234 if !pass_sanity_check {
235 println!("{}\n", subcommand_help);
237 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
238 You may need to move some options to after the subcommand.\n"
242 // Extra help text for some commands
243 match subcommand.as_str() {
245 subcommand_help.push_str(
248 This subcommand accepts a number of paths to directories to the crates
249 and/or artifacts to compile. For example:
251 ./x.py build src/libcore
252 ./x.py build src/libcore src/libproc_macro
253 ./x.py build src/libstd --stage 1
255 If no arguments are passed then the complete artifacts for that stage are
259 ./x.py build --stage 1
261 For a quick build of a usable compiler, you can pass:
263 ./x.py build --stage 1 src/libtest
265 This will first build everything once (like `--stage 0` without further
266 arguments would), and then use the compiler built in stage 0 to build
267 src/libtest and its dependencies.
268 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
272 subcommand_help.push_str(
275 This subcommand accepts a number of paths to directories to the crates
276 and/or artifacts to compile. For example:
278 ./x.py check src/libcore
279 ./x.py check src/libcore src/libproc_macro
281 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
282 also that since we use `cargo check`, by default this will automatically enable incremental
283 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
284 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
289 subcommand_help.push_str(
292 This subcommand accepts a number of paths to directories to tests that
293 should be compiled and run. For example:
295 ./x.py test src/test/run-pass
296 ./x.py test src/libstd --test-args hash_map
297 ./x.py test src/libstd --stage 0 --no-doc
298 ./x.py test src/test/ui --bless
299 ./x.py test src/test/ui --compare-mode nll
301 Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
302 just like `build src/libstd --stage N` it tests the compiler produced by the previous
305 If no arguments are passed then the complete artifacts for that stage are
309 ./x.py test --stage 1",
313 subcommand_help.push_str(
316 This subcommand accepts a number of paths to directories of documentation
317 to build. For example:
319 ./x.py doc src/doc/book
320 ./x.py doc src/doc/nomicon
321 ./x.py doc src/doc/book src/libstd
323 If no arguments are passed then everything is documented:
326 ./x.py doc --stage 1",
331 // Get any optional paths which occur after the subcommand
332 let paths = matches.free[1..]
335 .collect::<Vec<PathBuf>>();
337 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
338 if fs::metadata("config.toml").is_ok() {
339 Some(PathBuf::from("config.toml"))
345 // All subcommands except `clean` can have an optional "Available paths" section
346 if matches.opt_present("verbose") {
347 let config = Config::parse(&["build".to_string()]);
348 let mut build = Build::new(config);
349 metadata::build(&mut build);
351 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
352 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
353 } else if subcommand.as_str() != "clean" {
356 "Run `./x.py {} -h -v` to see a list of available paths.",
362 // User passed in -h/--help?
363 if matches.opt_present("help") {
364 usage(0, &opts, &subcommand_help, &extra_help);
367 let cmd = match subcommand.as_str() {
368 "build" => Subcommand::Build { paths: paths },
369 "check" => Subcommand::Check { paths: paths },
370 "test" => Subcommand::Test {
372 bless: matches.opt_present("bless"),
373 compare_mode: matches.opt_str("compare-mode"),
374 test_args: matches.opt_strs("test-args"),
375 rustc_args: matches.opt_strs("rustc-args"),
376 fail_fast: !matches.opt_present("no-fail-fast"),
377 doc_tests: if matches.opt_present("doc") {
379 } else if matches.opt_present("no-doc") {
385 "bench" => Subcommand::Bench {
387 test_args: matches.opt_strs("test-args"),
389 "doc" => Subcommand::Doc { paths: paths },
392 println!("\nclean does not take a path argument\n");
393 usage(1, &opts, &subcommand_help, &extra_help);
397 all: matches.opt_present("all"),
400 "dist" => Subcommand::Dist { paths },
401 "install" => Subcommand::Install { paths },
403 usage(1, &opts, &subcommand_help, &extra_help);
408 verbose: matches.opt_count("verbose"),
409 stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
410 dry_run: matches.opt_present("dry-run"),
411 on_fail: matches.opt_str("on-fail"),
412 rustc_error_format: matches.opt_str("error-format"),
413 keep_stage: matches.opt_strs("keep-stage")
414 .into_iter().map(|j| j.parse().unwrap())
416 host: split(matches.opt_strs("host"))
418 .map(|x| INTERNER.intern_string(x))
419 .collect::<Vec<_>>(),
420 target: split(matches.opt_strs("target"))
422 .map(|x| INTERNER.intern_string(x))
423 .collect::<Vec<_>>(),
425 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
427 incremental: matches.opt_present("incremental"),
428 exclude: split(matches.opt_strs("exclude"))
431 .collect::<Vec<_>>(),
432 warnings: matches.opt_str("warnings").map(|v| v == "deny"),
438 pub fn test_args(&self) -> Vec<&str> {
440 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
443 .flat_map(|s| s.split_whitespace())
450 pub fn rustc_args(&self) -> Vec<&str> {
452 Subcommand::Test { ref rustc_args, .. } => rustc_args
454 .flat_map(|s| s.split_whitespace())
460 pub fn fail_fast(&self) -> bool {
462 Subcommand::Test { fail_fast, .. } => fail_fast,
467 pub fn doc_tests(&self) -> DocTests {
469 Subcommand::Test { doc_tests, .. } => doc_tests,
474 pub fn bless(&self) -> bool {
476 Subcommand::Test { bless, .. } => bless,
481 pub fn compare_mode(&self) -> Option<&str> {
485 } => compare_mode.as_ref().map(|s| &s[..]),
491 fn split(s: Vec<String>) -> Vec<String> {
493 .flat_map(|s| s.split(','))
494 .map(|s| s.to_string())