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.
18 use std::path::PathBuf;
28 /// Deserialized version of all flags for this compile.
30 pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
31 pub on_fail: Option<String>,
32 pub stage: Option<u32>,
33 pub keep_stage: Option<u32>,
35 pub host: Vec<String>,
36 pub target: Vec<String>,
37 pub config: Option<PathBuf>,
39 pub jobs: Option<u32>,
41 pub incremental: bool,
53 test_args: Vec<String>,
58 test_args: Vec<String>,
70 pub fn parse(args: &[String]) -> Flags {
71 let mut extra_help = String::new();
72 let mut subcommand_help = format!("\
73 Usage: x.py <subcommand> [options] [<paths>...]
76 build Compile either the compiler or libraries
77 test Build and run some test suites
78 bench Build and run some benchmarks
79 doc Build documentation
80 clean Clean out build directories
81 dist Build distribution artifacts
82 install Install distribution artifacts
84 To learn more about a subcommand, run `./x.py <subcommand> -h`");
86 let mut opts = Options::new();
87 // Options common to all subcommands
88 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
89 opts.optflag("i", "incremental", "use incremental compilation");
90 opts.optopt("", "config", "TOML configuration file for build", "FILE");
91 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
92 opts.optmulti("", "host", "host targets to build", "HOST");
93 opts.optmulti("", "target", "target targets to build", "TARGET");
94 opts.optopt("", "on-fail", "command to run on failure", "CMD");
95 opts.optopt("", "stage", "stage to build", "N");
96 opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
97 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
98 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
99 opts.optflag("h", "help", "print this help message");
102 let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
103 println!("{}", opts.usage(subcommand_help));
104 if !extra_help.is_empty() {
105 println!("{}", extra_help);
107 process::exit(exit_code);
110 // We can't use getopt to parse the options until we have completed specifying which
111 // options are valid, but under the current implementation, some options are conditional on
112 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
113 // complete the definition of the options. Then we can use the getopt::Matches object from
115 let subcommand = args.iter().find(|&s|
122 || (s == "install"));
123 let subcommand = match subcommand {
126 // No subcommand -- show the general usage and subcommand help
127 println!("{}\n", subcommand_help);
132 // Some subcommands get extra options
133 match subcommand.as_str() {
135 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
136 opts.optmulti("", "test-args", "extra arguments", "ARGS");
138 "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
142 // Done specifying what options are possible, so do the getopts parsing
143 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
144 // Invalid argument/option format
145 println!("\n{}\n", e);
146 usage(1, &opts, &subcommand_help, &extra_help);
148 // Extra sanity check to make sure we didn't hit this crazy corner case:
150 // ./x.py --frobulate clean build
151 // ^-- option ^ ^- actual subcommand
152 // \_ arg to option could be mistaken as subcommand
153 let mut pass_sanity_check = true;
154 match matches.free.get(0) {
155 Some(check_subcommand) => {
156 if check_subcommand != subcommand {
157 pass_sanity_check = false;
161 pass_sanity_check = false;
164 if !pass_sanity_check {
165 println!("{}\n", subcommand_help);
166 println!("Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
167 You may need to move some options to after the subcommand.\n");
170 // Extra help text for some commands
171 match subcommand.as_str() {
173 subcommand_help.push_str("\n
175 This subcommand accepts a number of paths to directories to the crates
176 and/or artifacts to compile. For example:
178 ./x.py build src/libcore
179 ./x.py build src/libcore src/libproc_macro
180 ./x.py build src/libstd --stage 1
182 If no arguments are passed then the complete artifacts for that stage are
186 ./x.py build --stage 1
188 For a quick build of a usable compiler, you can pass:
190 ./x.py build --stage 1 src/libtest
192 This will first build everything once (like --stage 0 without further
193 arguments would), and then use the compiler built in stage 0 to build
194 src/libtest and its dependencies.
195 Once this is done, build/$ARCH/stage1 contains a usable compiler.");
198 subcommand_help.push_str("\n
200 This subcommand accepts a number of paths to directories to tests that
201 should be compiled and run. For example:
203 ./x.py test src/test/run-pass
204 ./x.py test src/libstd --test-args hash_map
205 ./x.py test src/libstd --stage 0
207 If no arguments are passed then the complete artifacts for that stage are
211 ./x.py test --stage 1");
214 subcommand_help.push_str("\n
216 This subcommand accepts a number of paths to directories of documentation
217 to build. For example:
219 ./x.py doc src/doc/book
220 ./x.py doc src/doc/nomicon
221 ./x.py doc src/doc/book src/libstd
223 If no arguments are passed then everything is documented:
226 ./x.py doc --stage 1");
230 // Get any optional paths which occur after the subcommand
231 let cwd = t!(env::current_dir());
232 let paths = matches.free[1..].iter().map(|p| cwd.join(p)).collect::<Vec<_>>();
234 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
235 if fs::metadata("config.toml").is_ok() {
236 Some(PathBuf::from("config.toml"))
242 // All subcommands can have an optional "Available paths" section
243 if matches.opt_present("verbose") {
244 let flags = Flags::parse(&["build".to_string()]);
245 let mut config = Config::parse(&flags.build, cfg_file.clone());
246 config.build = flags.build.clone();
247 let mut build = Build::new(flags, config);
248 metadata::build(&mut build);
249 let maybe_rules_help = step::build_rules(&build).get_help(subcommand);
250 if maybe_rules_help.is_some() {
251 extra_help.push_str(maybe_rules_help.unwrap().as_str());
254 extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
255 subcommand).as_str());
258 // User passed in -h/--help?
259 if matches.opt_present("help") {
260 usage(0, &opts, &subcommand_help, &extra_help);
263 let cmd = match subcommand.as_str() {
265 Subcommand::Build { paths: paths }
270 test_args: matches.opt_strs("test-args"),
271 fail_fast: !matches.opt_present("no-fail-fast"),
277 test_args: matches.opt_strs("test-args"),
281 Subcommand::Doc { paths: paths }
285 println!("\nclean takes no arguments\n");
286 usage(1, &opts, &subcommand_help, &extra_help);
296 Subcommand::Install {
301 usage(1, &opts, &subcommand_help, &extra_help);
306 let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
308 if matches.opt_present("incremental") && stage.is_none() {
312 let cwd = t!(env::current_dir());
313 let src = matches.opt_str("src").map(PathBuf::from)
314 .or_else(|| env::var_os("SRC").map(PathBuf::from))
318 verbose: matches.opt_count("verbose"),
320 on_fail: matches.opt_str("on-fail"),
321 keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
322 build: matches.opt_str("build").unwrap_or_else(|| {
323 env::var("BUILD").unwrap()
325 host: split(matches.opt_strs("host")),
326 target: split(matches.opt_strs("target")),
329 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
331 incremental: matches.opt_present("incremental"),
337 pub fn test_args(&self) -> Vec<&str> {
339 Subcommand::Test { ref test_args, .. } |
340 Subcommand::Bench { ref test_args, .. } => {
341 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
347 pub fn fail_fast(&self) -> bool {
349 Subcommand::Test { fail_fast, .. } => fail_fast,
355 fn split(s: Vec<String>) -> Vec<String> {
356 s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()