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;
27 /// Deserialized version of all flags for this compile.
29 pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
30 pub on_fail: Option<String>,
31 pub stage: Option<u32>,
32 pub keep_stage: Option<u32>,
34 pub host: Vec<String>,
35 pub target: Vec<String>,
36 pub config: Option<PathBuf>,
38 pub jobs: Option<u32>,
40 pub incremental: bool,
52 test_args: Vec<String>,
57 test_args: Vec<String>,
69 pub fn parse(args: &[String]) -> Flags {
70 let mut extra_help = String::new();
71 let mut subcommand_help = format!("\
72 Usage: x.py <subcommand> [options] [<paths>...]
75 build Compile either the compiler or libraries
76 test Build and run some test suites
77 bench Build and run some benchmarks
78 doc Build documentation
79 clean Clean out build directories
80 dist Build distribution artifacts
81 install Install distribution artifacts
83 To learn more about a subcommand, run `./x.py <subcommand> -h`");
85 let mut opts = Options::new();
86 // Options common to all subcommands
87 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
88 opts.optflag("i", "incremental", "use incremental compilation");
89 opts.optopt("", "config", "TOML configuration file for build", "FILE");
90 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
91 opts.optmulti("", "host", "host targets to build", "HOST");
92 opts.optmulti("", "target", "target targets to build", "TARGET");
93 opts.optopt("", "on-fail", "command to run on failure", "CMD");
94 opts.optopt("", "stage", "stage to build", "N");
95 opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
96 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
97 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
98 opts.optflag("h", "help", "print this help message");
101 let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
102 println!("{}", opts.usage(subcommand_help));
103 if !extra_help.is_empty() {
104 println!("{}", extra_help);
106 process::exit(exit_code);
109 // We can't use getopt to parse the options until we have completed specifying which
110 // options are valid, but under the current implementation, some options are conditional on
111 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
112 // complete the definition of the options. Then we can use the getopt::Matches object from
114 let subcommand = args.iter().find(|&s|
121 || (s == "install"));
122 let subcommand = match subcommand {
125 // No subcommand -- show the general usage and subcommand help
126 println!("{}\n", subcommand_help);
131 // Some subcommands get extra options
132 match subcommand.as_str() {
134 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
135 opts.optmulti("", "test-args", "extra arguments", "ARGS");
137 "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
141 // Done specifying what options are possible, so do the getopts parsing
142 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
143 // Invalid argument/option format
144 println!("\n{}\n", e);
145 usage(1, &opts, &subcommand_help, &extra_help);
147 // Extra sanity check to make sure we didn't hit this crazy corner case:
149 // ./x.py --frobulate clean build
150 // ^-- option ^ ^- actual subcommand
151 // \_ arg to option could be mistaken as subcommand
152 let mut pass_sanity_check = true;
153 match matches.free.get(0) {
154 Some(check_subcommand) => {
155 if check_subcommand != subcommand {
156 pass_sanity_check = false;
160 pass_sanity_check = false;
163 if !pass_sanity_check {
164 println!("{}\n", subcommand_help);
165 println!("Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
166 You may need to move some options to after the subcommand.\n");
169 // Extra help text for some commands
170 match subcommand.as_str() {
172 subcommand_help.push_str("\n
174 This subcommand accepts a number of paths to directories to the crates
175 and/or artifacts to compile. For example:
177 ./x.py build src/libcore
178 ./x.py build src/libcore src/libproc_macro
179 ./x.py build src/libstd --stage 1
181 If no arguments are passed then the complete artifacts for that stage are
185 ./x.py build --stage 1
187 For a quick build of a usable compiler, you can pass:
189 ./x.py build --stage 1 src/libtest
191 This will first build everything once (like --stage 0 without further
192 arguments would), and then use the compiler built in stage 0 to build
193 src/libtest and its dependencies.
194 Once this is done, build/$ARCH/stage1 contains a usable compiler.");
197 subcommand_help.push_str("\n
199 This subcommand accepts a number of paths to directories to tests that
200 should be compiled and run. For example:
202 ./x.py test src/test/run-pass
203 ./x.py test src/libstd --test-args hash_map
204 ./x.py test src/libstd --stage 0
206 If no arguments are passed then the complete artifacts for that stage are
210 ./x.py test --stage 1");
213 subcommand_help.push_str("\n
215 This subcommand accepts a number of paths to directories of documentation
216 to build. For example:
218 ./x.py doc src/doc/book
219 ./x.py doc src/doc/nomicon
220 ./x.py doc src/doc/book src/libstd
222 If no arguments are passed then everything is documented:
225 ./x.py doc --stage 1");
229 // Get any optional paths which occur after the subcommand
230 let cwd = t!(env::current_dir());
231 let paths = matches.free[1..].iter().map(|p| cwd.join(p)).collect::<Vec<_>>();
233 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
234 if fs::metadata("config.toml").is_ok() {
235 Some(PathBuf::from("config.toml"))
241 // All subcommands can have an optional "Available paths" section
242 if matches.opt_present("verbose") {
243 let flags = Flags::parse(&["build".to_string()]);
244 let mut config = Config::parse(&flags.build, cfg_file.clone());
245 config.build = flags.build.clone();
246 let mut build = Build::new(flags, config);
247 metadata::build(&mut build);
249 // FIXME: How should this happen now? Not super clear...
250 // let maybe_rules_help = step::build_rules(&build).get_help(subcommand);
251 // if maybe_rules_help.is_some() {
252 // extra_help.push_str(maybe_rules_help.unwrap().as_str());
255 extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
256 subcommand).as_str());
259 // User passed in -h/--help?
260 if matches.opt_present("help") {
261 usage(0, &opts, &subcommand_help, &extra_help);
264 let cmd = match subcommand.as_str() {
266 Subcommand::Build { paths: paths }
271 test_args: matches.opt_strs("test-args"),
272 fail_fast: !matches.opt_present("no-fail-fast"),
278 test_args: matches.opt_strs("test-args"),
282 Subcommand::Doc { paths: paths }
286 println!("\nclean takes no arguments\n");
287 usage(1, &opts, &subcommand_help, &extra_help);
297 Subcommand::Install {
302 usage(1, &opts, &subcommand_help, &extra_help);
307 let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
309 if matches.opt_present("incremental") && stage.is_none() {
313 let cwd = t!(env::current_dir());
314 let src = matches.opt_str("src").map(PathBuf::from)
315 .or_else(|| env::var_os("SRC").map(PathBuf::from))
319 verbose: matches.opt_count("verbose"),
321 on_fail: matches.opt_str("on-fail"),
322 keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
323 build: matches.opt_str("build").unwrap_or_else(|| {
324 env::var("BUILD").unwrap()
326 host: split(matches.opt_strs("host")),
327 target: split(matches.opt_strs("target")),
330 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
332 incremental: matches.opt_present("incremental"),
338 pub fn test_args(&self) -> Vec<&str> {
340 Subcommand::Test { ref test_args, .. } |
341 Subcommand::Bench { ref test_args, .. } => {
342 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
348 pub fn fail_fast(&self) -> bool {
350 Subcommand::Test { fail_fast, .. } => fail_fast,
356 fn split(s: Vec<String>) -> Vec<String> {
357 s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()