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: Option<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", "stage to build", "N");
125 opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
126 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
127 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
128 opts.optflag("h", "help", "print this help message");
132 "if value is deny, will deny warnings, otherwise use default",
135 opts.optopt("", "error-format", "rustc error format", "FORMAT");
139 |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
140 println!("{}", opts.usage(subcommand_help));
141 if !extra_help.is_empty() {
142 println!("{}", extra_help);
144 process::exit(exit_code);
147 // We can't use getopt to parse the options until we have completed specifying which
148 // options are valid, but under the current implementation, some options are conditional on
149 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
150 // complete the definition of the options. Then we can use the getopt::Matches object from
152 let subcommand = args.iter().find(|&s| {
162 let subcommand = match subcommand {
165 // No or an invalid subcommand -- show the general usage and subcommand help
166 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
168 println!("{}\n", subcommand_help);
169 let exit_code = if args.is_empty() { 0 } else { 1 };
170 process::exit(exit_code);
174 // Some subcommands get extra options
175 match subcommand.as_str() {
177 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
178 opts.optmulti("", "test-args", "extra arguments", "ARGS");
182 "extra options to pass the compiler when running tests",
185 opts.optflag("", "no-doc", "do not run doc tests");
186 opts.optflag("", "doc", "only run doc tests");
190 "update all stderr/stdout files of failing ui tests",
195 "mode describing what file the actual ui output will be compared to",
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
294 ./x.py test src/test/ui --bless
295 ./x.py test src/test/ui --compare-mode nll
297 If no arguments are passed then the complete artifacts for that stage are
301 ./x.py test --stage 1",
305 subcommand_help.push_str(
308 This subcommand accepts a number of paths to directories of documentation
309 to build. For example:
311 ./x.py doc src/doc/book
312 ./x.py doc src/doc/nomicon
313 ./x.py doc src/doc/book src/libstd
315 If no arguments are passed then everything is documented:
318 ./x.py doc --stage 1",
323 // Get any optional paths which occur after the subcommand
324 let paths = matches.free[1..]
327 .collect::<Vec<PathBuf>>();
329 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
330 if fs::metadata("config.toml").is_ok() {
331 Some(PathBuf::from("config.toml"))
337 // All subcommands except `clean` can have an optional "Available paths" section
338 if matches.opt_present("verbose") {
339 let config = Config::parse(&["build".to_string()]);
340 let mut build = Build::new(config);
341 metadata::build(&mut build);
343 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
344 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
345 } else if subcommand.as_str() != "clean" {
348 "Run `./x.py {} -h -v` to see a list of available paths.",
354 // User passed in -h/--help?
355 if matches.opt_present("help") {
356 usage(0, &opts, &subcommand_help, &extra_help);
359 let cmd = match subcommand.as_str() {
360 "build" => Subcommand::Build { paths: paths },
361 "check" => Subcommand::Check { paths: paths },
362 "test" => Subcommand::Test {
364 bless: matches.opt_present("bless"),
365 compare_mode: matches.opt_str("compare-mode"),
366 test_args: matches.opt_strs("test-args"),
367 rustc_args: matches.opt_strs("rustc-args"),
368 fail_fast: !matches.opt_present("no-fail-fast"),
369 doc_tests: if matches.opt_present("doc") {
371 } else if matches.opt_present("no-doc") {
377 "bench" => Subcommand::Bench {
379 test_args: matches.opt_strs("test-args"),
381 "doc" => Subcommand::Doc { paths: paths },
384 println!("\nclean does not take a path argument\n");
385 usage(1, &opts, &subcommand_help, &extra_help);
389 all: matches.opt_present("all"),
392 "dist" => Subcommand::Dist { paths },
393 "install" => Subcommand::Install { paths },
395 usage(1, &opts, &subcommand_help, &extra_help);
400 verbose: matches.opt_count("verbose"),
401 stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
402 dry_run: matches.opt_present("dry-run"),
403 on_fail: matches.opt_str("on-fail"),
404 rustc_error_format: matches.opt_str("error-format"),
405 keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
406 host: split(matches.opt_strs("host"))
408 .map(|x| INTERNER.intern_string(x))
409 .collect::<Vec<_>>(),
410 target: split(matches.opt_strs("target"))
412 .map(|x| INTERNER.intern_string(x))
413 .collect::<Vec<_>>(),
415 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
417 incremental: matches.opt_present("incremental"),
418 exclude: split(matches.opt_strs("exclude"))
421 .collect::<Vec<_>>(),
422 warnings: matches.opt_str("warnings").map(|v| v == "deny"),
428 pub fn test_args(&self) -> Vec<&str> {
430 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
433 .flat_map(|s| s.split_whitespace())
440 pub fn rustc_args(&self) -> Vec<&str> {
442 Subcommand::Test { ref rustc_args, .. } => rustc_args
444 .flat_map(|s| s.split_whitespace())
450 pub fn fail_fast(&self) -> bool {
452 Subcommand::Test { fail_fast, .. } => fail_fast,
457 pub fn doc_tests(&self) -> DocTests {
459 Subcommand::Test { doc_tests, .. } => doc_tests,
464 pub fn bless(&self) -> bool {
466 Subcommand::Test { bless, .. } => bless,
471 pub fn compare_mode(&self) -> Option<&str> {
475 } => compare_mode.as_ref().map(|s| &s[..]),
481 fn split(s: Vec<String>) -> Vec<String> {
483 .flat_map(|s| s.split(','))
484 .map(|s| s.to_string())