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 use cache::{Interned, INTERNER};
29 /// Deserialized version of all flags for this compile.
31 pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
32 pub on_fail: Option<String>,
33 pub stage: Option<u32>,
34 pub keep_stage: Option<u32>,
35 pub build: Interned<String>,
36 pub host: Vec<Interned<String>>,
37 pub target: Vec<Interned<String>>,
38 pub config: Option<PathBuf>,
40 pub jobs: Option<u32>,
42 pub incremental: bool,
54 test_args: Vec<String>,
59 test_args: Vec<String>,
71 pub fn parse(args: &[String]) -> Flags {
72 let mut extra_help = String::new();
73 let mut subcommand_help = format!("\
74 Usage: x.py <subcommand> [options] [<paths>...]
77 build Compile either the compiler or libraries
78 test Build and run some test suites
79 bench Build and run some benchmarks
80 doc Build documentation
81 clean Clean out build directories
82 dist Build distribution artifacts
83 install Install distribution artifacts
85 To learn more about a subcommand, run `./x.py <subcommand> -h`");
87 let mut opts = Options::new();
88 // Options common to all subcommands
89 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
90 opts.optflag("i", "incremental", "use incremental compilation");
91 opts.optopt("", "config", "TOML configuration file for build", "FILE");
92 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
93 opts.optmulti("", "host", "host targets to build", "HOST");
94 opts.optmulti("", "target", "target targets to build", "TARGET");
95 opts.optopt("", "on-fail", "command to run on failure", "CMD");
96 opts.optopt("", "stage", "stage to build", "N");
97 opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
98 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
99 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
100 opts.optflag("h", "help", "print this help message");
103 let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
104 println!("{}", opts.usage(subcommand_help));
105 if !extra_help.is_empty() {
106 println!("{}", extra_help);
108 process::exit(exit_code);
111 // We can't use getopt to parse the options until we have completed specifying which
112 // options are valid, but under the current implementation, some options are conditional on
113 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
114 // complete the definition of the options. Then we can use the getopt::Matches object from
116 let subcommand = args.iter().find(|&s|
123 || (s == "install"));
124 let subcommand = match subcommand {
127 // No subcommand -- show the general usage and subcommand help
128 println!("{}\n", subcommand_help);
133 // Some subcommands get extra options
134 match subcommand.as_str() {
136 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
137 opts.optmulti("", "test-args", "extra arguments", "ARGS");
139 "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
143 // Done specifying what options are possible, so do the getopts parsing
144 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
145 // Invalid argument/option format
146 println!("\n{}\n", e);
147 usage(1, &opts, &subcommand_help, &extra_help);
149 // Extra sanity check to make sure we didn't hit this crazy corner case:
151 // ./x.py --frobulate clean build
152 // ^-- option ^ ^- actual subcommand
153 // \_ arg to option could be mistaken as subcommand
154 let mut pass_sanity_check = true;
155 match matches.free.get(0) {
156 Some(check_subcommand) => {
157 if check_subcommand != subcommand {
158 pass_sanity_check = false;
162 pass_sanity_check = false;
165 if !pass_sanity_check {
166 println!("{}\n", subcommand_help);
167 println!("Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
168 You may need to move some options to after the subcommand.\n");
171 // Extra help text for some commands
172 match subcommand.as_str() {
174 subcommand_help.push_str("\n
176 This subcommand accepts a number of paths to directories to the crates
177 and/or artifacts to compile. For example:
179 ./x.py build src/libcore
180 ./x.py build src/libcore src/libproc_macro
181 ./x.py build src/libstd --stage 1
183 If no arguments are passed then the complete artifacts for that stage are
187 ./x.py build --stage 1
189 For a quick build of a usable compiler, you can pass:
191 ./x.py build --stage 1 src/libtest
193 This will first build everything once (like --stage 0 without further
194 arguments would), and then use the compiler built in stage 0 to build
195 src/libtest and its dependencies.
196 Once this is done, build/$ARCH/stage1 contains a usable compiler.");
199 subcommand_help.push_str("\n
201 This subcommand accepts a number of paths to directories to tests that
202 should be compiled and run. For example:
204 ./x.py test src/test/run-pass
205 ./x.py test src/libstd --test-args hash_map
206 ./x.py test src/libstd --stage 0
208 If no arguments are passed then the complete artifacts for that stage are
212 ./x.py test --stage 1");
215 subcommand_help.push_str("\n
217 This subcommand accepts a number of paths to directories of documentation
218 to build. For example:
220 ./x.py doc src/doc/book
221 ./x.py doc src/doc/nomicon
222 ./x.py doc src/doc/book src/libstd
224 If no arguments are passed then everything is documented:
227 ./x.py doc --stage 1");
231 // Get any optional paths which occur after the subcommand
232 let cwd = t!(env::current_dir());
233 let paths = matches.free[1..].iter().map(|p| cwd.join(p)).collect::<Vec<_>>();
235 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
236 if fs::metadata("config.toml").is_ok() {
237 Some(PathBuf::from("config.toml"))
243 // All subcommands can have an optional "Available paths" section
244 if matches.opt_present("verbose") {
245 let flags = Flags::parse(&["build".to_string()]);
246 let mut config = Config::parse(&flags.build, cfg_file.clone());
247 config.build = flags.build.clone();
248 let mut build = Build::new(flags, config);
249 metadata::build(&mut build);
251 // FIXME: How should this happen now? Not super clear...
252 // let maybe_rules_help = step::build_rules(&build).get_help(subcommand);
253 // if maybe_rules_help.is_some() {
254 // extra_help.push_str(maybe_rules_help.unwrap().as_str());
257 extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
258 subcommand).as_str());
261 // User passed in -h/--help?
262 if matches.opt_present("help") {
263 usage(0, &opts, &subcommand_help, &extra_help);
266 let cmd = match subcommand.as_str() {
268 Subcommand::Build { paths: paths }
273 test_args: matches.opt_strs("test-args"),
274 fail_fast: !matches.opt_present("no-fail-fast"),
280 test_args: matches.opt_strs("test-args"),
284 Subcommand::Doc { paths: paths }
288 println!("\nclean takes no arguments\n");
289 usage(1, &opts, &subcommand_help, &extra_help);
299 Subcommand::Install {
304 usage(1, &opts, &subcommand_help, &extra_help);
309 let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
311 if matches.opt_present("incremental") && stage.is_none() {
315 let cwd = t!(env::current_dir());
316 let src = matches.opt_str("src").map(PathBuf::from)
317 .or_else(|| env::var_os("SRC").map(PathBuf::from))
321 verbose: matches.opt_count("verbose"),
323 on_fail: matches.opt_str("on-fail"),
324 keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
325 build: INTERNER.intern_string(matches.opt_str("build").unwrap_or_else(|| {
326 env::var("BUILD").unwrap()
328 host: split(matches.opt_strs("host"))
329 .into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
330 target: split(matches.opt_strs("target"))
331 .into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
334 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
336 incremental: matches.opt_present("incremental"),
342 pub fn test_args(&self) -> Vec<&str> {
344 Subcommand::Test { ref test_args, .. } |
345 Subcommand::Bench { ref test_args, .. } => {
346 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
352 pub fn fail_fast(&self) -> bool {
354 Subcommand::Test { fail_fast, .. } => fail_fast,
360 fn split(s: Vec<String>) -> Vec<String> {
361 s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()