]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/flags.rs
Fixes warnings and errors introduced while moving code around
[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
27 /// Deserialized version of all flags for this compile.
28 pub struct Flags {
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>,
33     pub build: String,
34     pub host: Vec<String>,
35     pub target: Vec<String>,
36     pub config: Option<PathBuf>,
37     pub src: PathBuf,
38     pub jobs: Option<u32>,
39     pub cmd: Subcommand,
40     pub incremental: bool,
41 }
42
43 pub enum Subcommand {
44     Build {
45         paths: Vec<PathBuf>,
46     },
47     Doc {
48         paths: Vec<PathBuf>,
49     },
50     Test {
51         paths: Vec<PathBuf>,
52         test_args: Vec<String>,
53         fail_fast: bool,
54     },
55     Bench {
56         paths: Vec<PathBuf>,
57         test_args: Vec<String>,
58     },
59     Clean,
60     Dist {
61         paths: Vec<PathBuf>,
62     },
63     Install {
64         paths: Vec<PathBuf>,
65     },
66 }
67
68 impl Flags {
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>...]
73
74 Subcommands:
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
82
83 To learn more about a subcommand, run `./x.py <subcommand> -h`");
84
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");
99
100         // fn usage()
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);
105             }
106             process::exit(exit_code);
107         };
108
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
113         // there on out.
114         let subcommand = args.iter().find(|&s|
115             (s == "build")
116             || (s == "test")
117             || (s == "bench")
118             || (s == "doc")
119             || (s == "clean")
120             || (s == "dist")
121             || (s == "install"));
122         let subcommand = match subcommand {
123             Some(s) => s,
124             None => {
125                 // No subcommand -- show the general usage and subcommand help
126                 println!("{}\n", subcommand_help);
127                 process::exit(0);
128             }
129         };
130
131         // Some subcommands get extra options
132         match subcommand.as_str() {
133             "test"  => {
134                 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
135                 opts.optmulti("", "test-args", "extra arguments", "ARGS");
136             },
137             "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
138             _ => { },
139         };
140
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);
146         });
147         // Extra sanity check to make sure we didn't hit this crazy corner case:
148         //
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;
157                 }
158             },
159             None => {
160                 pass_sanity_check = false;
161             }
162         }
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");
167             process::exit(1);
168         }
169         // Extra help text for some commands
170         match subcommand.as_str() {
171             "build" => {
172                 subcommand_help.push_str("\n
173 Arguments:
174     This subcommand accepts a number of paths to directories to the crates
175     and/or artifacts to compile. For example:
176
177         ./x.py build src/libcore
178         ./x.py build src/libcore src/libproc_macro
179         ./x.py build src/libstd --stage 1
180
181     If no arguments are passed then the complete artifacts for that stage are
182     also compiled.
183
184         ./x.py build
185         ./x.py build --stage 1
186
187     For a quick build of a usable compiler, you can pass:
188
189         ./x.py build --stage 1 src/libtest
190
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.");
195             }
196             "test" => {
197                 subcommand_help.push_str("\n
198 Arguments:
199     This subcommand accepts a number of paths to directories to tests that
200     should be compiled and run. For example:
201
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
205
206     If no arguments are passed then the complete artifacts for that stage are
207     compiled and tested.
208
209         ./x.py test
210         ./x.py test --stage 1");
211             }
212             "doc" => {
213                 subcommand_help.push_str("\n
214 Arguments:
215     This subcommand accepts a number of paths to directories of documentation
216     to build. For example:
217
218         ./x.py doc src/doc/book
219         ./x.py doc src/doc/nomicon
220         ./x.py doc src/doc/book src/libstd
221
222     If no arguments are passed then everything is documented:
223
224         ./x.py doc
225         ./x.py doc --stage 1");
226             }
227             _ => { }
228         };
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<_>>();
232
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"))
236             } else {
237                 None
238             }
239         });
240
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);
248
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());
253             // }
254         } else {
255             extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
256                      subcommand).as_str());
257         }
258
259         // User passed in -h/--help?
260         if matches.opt_present("help") {
261             usage(0, &opts, &subcommand_help, &extra_help);
262         }
263
264         let cmd = match subcommand.as_str() {
265             "build" => {
266                 Subcommand::Build { paths: paths }
267             }
268             "test" => {
269                 Subcommand::Test {
270                     paths: paths,
271                     test_args: matches.opt_strs("test-args"),
272                     fail_fast: !matches.opt_present("no-fail-fast"),
273                 }
274             }
275             "bench" => {
276                 Subcommand::Bench {
277                     paths: paths,
278                     test_args: matches.opt_strs("test-args"),
279                 }
280             }
281             "doc" => {
282                 Subcommand::Doc { paths: paths }
283             }
284             "clean" => {
285                 if paths.len() > 0 {
286                     println!("\nclean takes no arguments\n");
287                     usage(1, &opts, &subcommand_help, &extra_help);
288                 }
289                 Subcommand::Clean
290             }
291             "dist" => {
292                 Subcommand::Dist {
293                     paths: paths,
294                 }
295             }
296             "install" => {
297                 Subcommand::Install {
298                     paths: paths,
299                 }
300             }
301             _ => {
302                 usage(1, &opts, &subcommand_help, &extra_help);
303             }
304         };
305
306
307         let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
308
309         if matches.opt_present("incremental") && stage.is_none() {
310             stage = Some(1);
311         }
312
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))
316             .unwrap_or(cwd);
317
318         Flags {
319             verbose: matches.opt_count("verbose"),
320             stage: stage,
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()
325             }),
326             host: split(matches.opt_strs("host")),
327             target: split(matches.opt_strs("target")),
328             config: cfg_file,
329             src: src,
330             jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
331             cmd: cmd,
332             incremental: matches.opt_present("incremental"),
333         }
334     }
335 }
336
337 impl Subcommand {
338     pub fn test_args(&self) -> Vec<&str> {
339         match *self {
340             Subcommand::Test { ref test_args, .. } |
341             Subcommand::Bench { ref test_args, .. } => {
342                 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
343             }
344             _ => Vec::new(),
345         }
346     }
347
348     pub fn fail_fast(&self) -> bool {
349         match *self {
350             Subcommand::Test { fail_fast, .. } => fail_fast,
351             _ => false,
352         }
353     }
354 }
355
356 fn split(s: Vec<String>) -> Vec<String> {
357     s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
358 }