]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/flags.rs
rustdoc: Hide `self: Box<Self>` in list of deref methods
[rust.git] / src / bootstrap / flags.rs
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.
4 //
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.
10
11 //! Command-line interface of the rustbuild build system.
12 //!
13 //! This module implements the command-line parsing of the build system which
14 //! has various flags to configure how it's run.
15
16 use std::env;
17 use std::fs;
18 use std::path::PathBuf;
19 use std::process;
20
21 use getopts::Options;
22
23 use Build;
24 use config::Config;
25 use metadata;
26 use step;
27
28 /// Deserialized version of all flags for this compile.
29 pub struct Flags {
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>,
34     pub build: String,
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>,
40     pub cmd: Subcommand,
41     pub incremental: bool,
42 }
43
44 impl Flags {
45     pub fn verbose(&self) -> bool {
46         self.verbose > 0
47     }
48
49     pub fn very_verbose(&self) -> bool {
50         self.verbose > 1
51     }
52 }
53
54 pub enum Subcommand {
55     Build {
56         paths: Vec<PathBuf>,
57     },
58     Doc {
59         paths: Vec<PathBuf>,
60     },
61     Test {
62         paths: Vec<PathBuf>,
63         test_args: Vec<String>,
64     },
65     Bench {
66         paths: Vec<PathBuf>,
67         test_args: Vec<String>,
68     },
69     Clean,
70     Dist {
71         paths: Vec<PathBuf>,
72     },
73     Install {
74         paths: Vec<PathBuf>,
75     },
76 }
77
78 impl Flags {
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>...]
83
84 Subcommands:
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
92
93 To learn more about a subcommand, run `./x.py <subcommand> -h`");
94
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");
109
110         // fn usage()
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);
115             }
116             process::exit(exit_code);
117         };
118
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
123         // there on out.
124         let mut possible_subcommands = args.iter().collect::<Vec<_>>();
125         possible_subcommands.retain(|&s|
126                                            (s == "build")
127                                         || (s == "test")
128                                         || (s == "bench")
129                                         || (s == "doc")
130                                         || (s == "clean")
131                                         || (s == "dist")
132                                         || (s == "install"));
133         let subcommand = match possible_subcommands.first() {
134             Some(s) => s,
135             None => {
136                 // No subcommand -- show the general usage and subcommand help
137                 println!("{}\n", subcommand_help);
138                 process::exit(0);
139             }
140         };
141
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"); },
146             _ => { },
147         };
148
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);
154         });
155         // Extra sanity check to make sure we didn't hit this crazy corner case:
156         //
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;
165                 }
166             },
167             None => {
168                 pass_sanity_check = false;
169             }
170         }
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");
175             process::exit(1);
176         }
177         // Extra help text for some commands
178         match subcommand.as_str() {
179             "build" => {
180                 subcommand_help.push_str("\n
181 Arguments:
182     This subcommand accepts a number of paths to directories to the crates
183     and/or artifacts to compile. For example:
184
185         ./x.py build src/libcore
186         ./x.py build src/libcore src/libproc_macro
187         ./x.py build src/libstd --stage 1
188
189     If no arguments are passed then the complete artifacts for that stage are
190     also compiled.
191
192         ./x.py build
193         ./x.py build --stage 1
194
195     For a quick build with a usable compile, you can pass:
196
197         ./x.py build --stage 1 src/libtest");
198             }
199             "test" => {
200                 subcommand_help.push_str("\n
201 Arguments:
202     This subcommand accepts a number of paths to directories to tests that
203     should be compiled and run. For example:
204
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
208
209     If no arguments are passed then the complete artifacts for that stage are
210     compiled and tested.
211
212         ./x.py test
213         ./x.py test --stage 1");
214             }
215             "doc" => {
216                 subcommand_help.push_str("\n
217 Arguments:
218     This subcommand accepts a number of paths to directories of documentation
219     to build. For example:
220
221         ./x.py doc src/doc/book
222         ./x.py doc src/doc/nomicon
223         ./x.py doc src/doc/book src/libstd
224
225     If no arguments are passed then everything is documented:
226
227         ./x.py doc
228         ./x.py doc --stage 1");
229             }
230             _ => { }
231         };
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<_>>();
235
236
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());
247             }
248         } else {
249             extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
250                      subcommand).as_str());
251         }
252
253         // User passed in -h/--help?
254         if matches.opt_present("help") {
255             usage(0, &opts, &subcommand_help, &extra_help);
256         }
257
258         let cmd = match subcommand.as_str() {
259             "build" => {
260                 Subcommand::Build { paths: paths }
261             }
262             "test" => {
263                 Subcommand::Test {
264                     paths: paths,
265                     test_args: matches.opt_strs("test-args"),
266                 }
267             }
268             "bench" => {
269                 Subcommand::Bench {
270                     paths: paths,
271                     test_args: matches.opt_strs("test-args"),
272                 }
273             }
274             "doc" => {
275                 Subcommand::Doc { paths: paths }
276             }
277             "clean" => {
278                 if paths.len() > 0 {
279                     println!("\nclean takes no arguments\n");
280                     usage(1, &opts, &subcommand_help, &extra_help);
281                 }
282                 Subcommand::Clean
283             }
284             "dist" => {
285                 Subcommand::Dist {
286                     paths: paths,
287                 }
288             }
289             "install" => {
290                 Subcommand::Install {
291                     paths: paths,
292                 }
293             }
294             _ => {
295                 usage(1, &opts, &subcommand_help, &extra_help);
296             }
297         };
298
299
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"))
303             } else {
304                 None
305             }
306         });
307
308         let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
309
310         if matches.opt_present("incremental") {
311             if stage.is_none() {
312                 stage = Some(1);
313             }
314         }
315
316         Flags {
317             verbose: matches.opt_count("verbose"),
318             stage: stage,
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()
323             }),
324             host: split(matches.opt_strs("host")),
325             target: split(matches.opt_strs("target")),
326             config: cfg_file,
327             src: matches.opt_str("src").map(PathBuf::from),
328             jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
329             cmd: cmd,
330             incremental: matches.opt_present("incremental"),
331         }
332     }
333 }
334
335 impl Subcommand {
336     pub fn test_args(&self) -> Vec<&str> {
337         match *self {
338             Subcommand::Test { ref test_args, .. } |
339             Subcommand::Bench { ref test_args, .. } => {
340                 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
341             }
342             _ => Vec::new(),
343         }
344     }
345 }
346
347 fn split(s: Vec<String>) -> Vec<String> {
348     s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
349 }