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>,
38 pub src: Option<PathBuf>,
39 pub jobs: Option<u32>,
41 pub incremental: bool,
45 pub fn verbose(&self) -> bool {
49 pub fn very_verbose(&self) -> bool {
63 test_args: Vec<String>,
67 test_args: Vec<String>,
79 pub fn parse(args: &[String]) -> Flags {
80 let mut extra_help = String::new();
81 let mut subcommand_help = format!("\
82 Usage: x.py <subcommand> [options] [<paths>...]
85 build Compile either the compiler or libraries
86 test Build and run some test suites
87 bench Build and run some benchmarks
88 doc Build documentation
89 clean Clean out build directories
90 dist Build distribution artifacts
91 install Install distribution artifacts
93 To learn more about a subcommand, run `./x.py <subcommand> -h`");
95 let mut opts = Options::new();
96 // Options common to all subcommands
97 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
98 opts.optflag("i", "incremental", "use incremental compilation");
99 opts.optopt("", "config", "TOML configuration file for build", "FILE");
100 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
101 opts.optmulti("", "host", "host targets to build", "HOST");
102 opts.optmulti("", "target", "target targets to build", "TARGET");
103 opts.optopt("", "on-fail", "command to run on failure", "CMD");
104 opts.optopt("", "stage", "stage to build", "N");
105 opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
106 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
107 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
108 opts.optflag("h", "help", "print this help message");
111 let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
112 println!("{}", opts.usage(subcommand_help));
113 if !extra_help.is_empty() {
114 println!("{}", extra_help);
116 process::exit(exit_code);
119 // We can't use getopt to parse the options until we have completed specifying which
120 // options are valid, but under the current implementation, some options are conditional on
121 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
122 // complete the definition of the options. Then we can use the getopt::Matches object from
124 let mut possible_subcommands = args.iter().collect::<Vec<_>>();
125 possible_subcommands.retain(|&s|
132 || (s == "install"));
133 let subcommand = match possible_subcommands.first() {
136 // No subcommand -- show the general usage and subcommand help
137 println!("{}\n", subcommand_help);
142 // Some subcommands get extra options
143 match subcommand.as_str() {
144 "test" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
145 "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
149 // Done specifying what options are possible, so do the getopts parsing
150 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
151 // Invalid argument/option format
152 println!("\n{}\n", e);
153 usage(1, &opts, &subcommand_help, &extra_help);
155 // Extra sanity check to make sure we didn't hit this crazy corner case:
157 // ./x.py --frobulate clean build
158 // ^-- option ^ ^- actual subcommand
159 // \_ arg to option could be mistaken as subcommand
160 let mut pass_sanity_check = true;
161 match matches.free.get(0) {
162 Some(check_subcommand) => {
163 if &check_subcommand != subcommand {
164 pass_sanity_check = false;
168 pass_sanity_check = false;
171 if !pass_sanity_check {
172 println!("{}\n", subcommand_help);
173 println!("Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
174 You may need to move some options to after the subcommand.\n");
177 // Extra help text for some commands
178 match subcommand.as_str() {
180 subcommand_help.push_str("\n
182 This subcommand accepts a number of paths to directories to the crates
183 and/or artifacts to compile. For example:
185 ./x.py build src/libcore
186 ./x.py build src/libcore src/libproc_macro
187 ./x.py build src/libstd --stage 1
189 If no arguments are passed then the complete artifacts for that stage are
193 ./x.py build --stage 1
195 For a quick build with a usable compile, you can pass:
197 ./x.py build --stage 1 src/libtest");
200 subcommand_help.push_str("\n
202 This subcommand accepts a number of paths to directories to tests that
203 should be compiled and run. For example:
205 ./x.py test src/test/run-pass
206 ./x.py test src/libstd --test-args hash_map
207 ./x.py test src/libstd --stage 0
209 If no arguments are passed then the complete artifacts for that stage are
213 ./x.py test --stage 1");
216 subcommand_help.push_str("\n
218 This subcommand accepts a number of paths to directories of documentation
219 to build. For example:
221 ./x.py doc src/doc/book
222 ./x.py doc src/doc/nomicon
223 ./x.py doc src/doc/book src/libstd
225 If no arguments are passed then everything is documented:
228 ./x.py doc --stage 1");
232 // Get any optional paths which occur after the subcommand
233 let cwd = t!(env::current_dir());
234 let paths = matches.free[1..].iter().map(|p| cwd.join(p)).collect::<Vec<_>>();
237 // All subcommands can have an optional "Available paths" section
238 if matches.opt_present("verbose") {
239 let flags = Flags::parse(&["build".to_string()]);
240 let mut config = Config::default();
241 config.build = flags.build.clone();
242 let mut build = Build::new(flags, config);
243 metadata::build(&mut build);
244 let maybe_rules_help = step::build_rules(&build).get_help(subcommand);
245 if maybe_rules_help.is_some() {
246 extra_help.push_str(maybe_rules_help.unwrap().as_str());
249 extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
250 subcommand).as_str());
253 // User passed in -h/--help?
254 if matches.opt_present("help") {
255 usage(0, &opts, &subcommand_help, &extra_help);
258 let cmd = match subcommand.as_str() {
260 Subcommand::Build { paths: paths }
265 test_args: matches.opt_strs("test-args"),
271 test_args: matches.opt_strs("test-args"),
275 Subcommand::Doc { paths: paths }
279 println!("\nclean takes no arguments\n");
280 usage(1, &opts, &subcommand_help, &extra_help);
290 Subcommand::Install {
295 usage(1, &opts, &subcommand_help, &extra_help);
300 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
301 if fs::metadata("config.toml").is_ok() {
302 Some(PathBuf::from("config.toml"))
308 let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
310 if matches.opt_present("incremental") {
317 verbose: matches.opt_count("verbose"),
319 on_fail: matches.opt_str("on-fail"),
320 keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
321 build: matches.opt_str("build").unwrap_or_else(|| {
322 env::var("BUILD").unwrap()
324 host: split(matches.opt_strs("host")),
325 target: split(matches.opt_strs("target")),
327 src: matches.opt_str("src").map(PathBuf::from),
328 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
330 incremental: matches.opt_present("incremental"),
336 pub fn test_args(&self) -> Vec<&str> {
338 Subcommand::Test { ref test_args, .. } |
339 Subcommand::Bench { ref test_args, .. } => {
340 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
347 fn split(s: Vec<String>) -> Vec<String> {
348 s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()