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