]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/flags.rs
Auto merge of #53832 - pietroalbini:rollup, r=pietroalbini
[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::fs;
17 use std::path::PathBuf;
18 use std::process;
19
20 use getopts::Options;
21
22 use builder::Builder;
23 use config::Config;
24 use metadata;
25 use {Build, DocTests};
26
27 use cache::{Interned, INTERNER};
28
29 /// Deserialized version of all flags for this compile.
30 pub struct Flags {
31     pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
32     pub on_fail: Option<String>,
33     pub stage: Option<u32>,
34     pub keep_stage: Vec<u32>,
35
36     pub host: Vec<Interned<String>>,
37     pub target: Vec<Interned<String>>,
38     pub config: Option<PathBuf>,
39     pub jobs: Option<u32>,
40     pub cmd: Subcommand,
41     pub incremental: bool,
42     pub exclude: Vec<PathBuf>,
43     pub rustc_error_format: Option<String>,
44     pub dry_run: bool,
45
46     // true => deny
47     pub warnings: Option<bool>,
48 }
49
50 pub enum Subcommand {
51     Build {
52         paths: Vec<PathBuf>,
53     },
54     Check {
55         paths: Vec<PathBuf>,
56     },
57     Doc {
58         paths: Vec<PathBuf>,
59     },
60     Test {
61         paths: Vec<PathBuf>,
62         /// Whether to automatically update stderr/stdout files
63         bless: bool,
64         compare_mode: Option<String>,
65         test_args: Vec<String>,
66         rustc_args: Vec<String>,
67         fail_fast: bool,
68         doc_tests: DocTests,
69     },
70     Bench {
71         paths: Vec<PathBuf>,
72         test_args: Vec<String>,
73     },
74     Clean {
75         all: bool,
76     },
77     Dist {
78         paths: Vec<PathBuf>,
79     },
80     Install {
81         paths: Vec<PathBuf>,
82     },
83 }
84
85 impl Default for Subcommand {
86     fn default() -> Subcommand {
87         Subcommand::Build {
88             paths: vec![PathBuf::from("nowhere")],
89         }
90     }
91 }
92
93 impl Flags {
94     pub fn parse(args: &[String]) -> Flags {
95         let mut extra_help = String::new();
96         let mut subcommand_help = format!(
97             "\
98 Usage: x.py <subcommand> [options] [<paths>...]
99
100 Subcommands:
101     build       Compile either the compiler or libraries
102     check       Compile either the compiler or libraries, using cargo check
103     test        Build and run some test suites
104     bench       Build and run some benchmarks
105     doc         Build documentation
106     clean       Clean out build directories
107     dist        Build distribution artifacts
108     install     Install distribution artifacts
109
110 To learn more about a subcommand, run `./x.py <subcommand> -h`"
111         );
112
113         let mut opts = Options::new();
114         // Options common to all subcommands
115         opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
116         opts.optflag("i", "incremental", "use incremental compilation");
117         opts.optopt("", "config", "TOML configuration file for build", "FILE");
118         opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
119         opts.optmulti("", "host", "host targets to build", "HOST");
120         opts.optmulti("", "target", "target targets to build", "TARGET");
121         opts.optmulti("", "exclude", "build paths to exclude", "PATH");
122         opts.optopt("", "on-fail", "command to run on failure", "CMD");
123         opts.optflag("", "dry-run", "dry run; don't build anything");
124         opts.optopt("", "stage",
125             "stage to build (indicates compiler to use/test, e.g. stage 0 uses the \
126              bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
127             "N");
128         opts.optmulti("", "keep-stage", "stage(s) to keep without recompiling \
129             (pass multiple times to keep e.g. both stages 0 and 1)", "N");
130         opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
131         opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
132         opts.optflag("h", "help", "print this help message");
133         opts.optopt(
134             "",
135             "warnings",
136             "if value is deny, will deny warnings, otherwise use default",
137             "VALUE",
138         );
139         opts.optopt("", "error-format", "rustc error format", "FORMAT");
140
141         // fn usage()
142         let usage =
143             |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
144                 println!("{}", opts.usage(subcommand_help));
145                 if !extra_help.is_empty() {
146                     println!("{}", extra_help);
147                 }
148                 process::exit(exit_code);
149             };
150
151         // We can't use getopt to parse the options until we have completed specifying which
152         // options are valid, but under the current implementation, some options are conditional on
153         // the subcommand. Therefore we must manually identify the subcommand first, so that we can
154         // complete the definition of the options.  Then we can use the getopt::Matches object from
155         // there on out.
156         let subcommand = args.iter().find(|&s| {
157             (s == "build")
158                 || (s == "check")
159                 || (s == "test")
160                 || (s == "bench")
161                 || (s == "doc")
162                 || (s == "clean")
163                 || (s == "dist")
164                 || (s == "install")
165         });
166         let subcommand = match subcommand {
167             Some(s) => s,
168             None => {
169                 // No or an invalid subcommand -- show the general usage and subcommand help
170                 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
171                 // subcommand.
172                 println!("{}\n", subcommand_help);
173                 let exit_code = if args.is_empty() { 0 } else { 1 };
174                 process::exit(exit_code);
175             }
176         };
177
178         // Some subcommands get extra options
179         match subcommand.as_str() {
180             "test" => {
181                 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
182                 opts.optmulti("", "test-args", "extra arguments", "ARGS");
183                 opts.optmulti(
184                     "",
185                     "rustc-args",
186                     "extra options to pass the compiler when running tests",
187                     "ARGS",
188                 );
189                 opts.optflag("", "no-doc", "do not run doc tests");
190                 opts.optflag("", "doc", "only run doc tests");
191                 opts.optflag(
192                     "",
193                     "bless",
194                     "update all stderr/stdout files of failing ui tests",
195                 );
196                 opts.optopt(
197                     "",
198                     "compare-mode",
199                     "mode describing what file the actual ui output will be compared to",
200                     "COMPARE MODE",
201                 );
202             }
203             "bench" => {
204                 opts.optmulti("", "test-args", "extra arguments", "ARGS");
205             }
206             "clean" => {
207                 opts.optflag("", "all", "clean all build artifacts");
208             }
209             _ => {}
210         };
211
212         // Done specifying what options are possible, so do the getopts parsing
213         let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
214             // Invalid argument/option format
215             println!("\n{}\n", e);
216             usage(1, &opts, &subcommand_help, &extra_help);
217         });
218         // Extra sanity check to make sure we didn't hit this crazy corner case:
219         //
220         //     ./x.py --frobulate clean build
221         //            ^-- option  ^     ^- actual subcommand
222         //                        \_ arg to option could be mistaken as subcommand
223         let mut pass_sanity_check = true;
224         match matches.free.get(0) {
225             Some(check_subcommand) => {
226                 if check_subcommand != subcommand {
227                     pass_sanity_check = false;
228                 }
229             }
230             None => {
231                 pass_sanity_check = false;
232             }
233         }
234         if !pass_sanity_check {
235             println!("{}\n", subcommand_help);
236             println!(
237                 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
238                  You may need to move some options to after the subcommand.\n"
239             );
240             process::exit(1);
241         }
242         // Extra help text for some commands
243         match subcommand.as_str() {
244             "build" => {
245                 subcommand_help.push_str(
246                     "\n
247 Arguments:
248     This subcommand accepts a number of paths to directories to the crates
249     and/or artifacts to compile. For example:
250
251         ./x.py build src/libcore
252         ./x.py build src/libcore src/libproc_macro
253         ./x.py build src/libstd --stage 1
254
255     If no arguments are passed then the complete artifacts for that stage are
256     also compiled.
257
258         ./x.py build
259         ./x.py build --stage 1
260
261     For a quick build of a usable compiler, you can pass:
262
263         ./x.py build --stage 1 src/libtest
264
265     This will first build everything once (like `--stage 0` without further
266     arguments would), and then use the compiler built in stage 0 to build
267     src/libtest and its dependencies.
268     Once this is done, build/$ARCH/stage1 contains a usable compiler.",
269                 );
270             }
271             "check" => {
272                 subcommand_help.push_str(
273                     "\n
274 Arguments:
275     This subcommand accepts a number of paths to directories to the crates
276     and/or artifacts to compile. For example:
277
278         ./x.py check src/libcore
279         ./x.py check src/libcore src/libproc_macro
280
281     If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
282     also that since we use `cargo check`, by default this will automatically enable incremental
283     compilation, so there's no need to pass it separately, though it won't hurt. We also completely
284     ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
285     the compiler.",
286                 );
287             }
288             "test" => {
289                 subcommand_help.push_str(
290                     "\n
291 Arguments:
292     This subcommand accepts a number of paths to directories to tests that
293     should be compiled and run. For example:
294
295         ./x.py test src/test/run-pass
296         ./x.py test src/libstd --test-args hash_map
297         ./x.py test src/libstd --stage 0 --no-doc
298         ./x.py test src/test/ui --bless
299         ./x.py test src/test/ui --compare-mode nll
300
301     Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
302     just like `build src/libstd --stage N` it tests the compiler produced by the previous
303     stage.
304
305     If no arguments are passed then the complete artifacts for that stage are
306     compiled and tested.
307
308         ./x.py test
309         ./x.py test --stage 1",
310                 );
311             }
312             "doc" => {
313                 subcommand_help.push_str(
314                     "\n
315 Arguments:
316     This subcommand accepts a number of paths to directories of documentation
317     to build. For example:
318
319         ./x.py doc src/doc/book
320         ./x.py doc src/doc/nomicon
321         ./x.py doc src/doc/book src/libstd
322
323     If no arguments are passed then everything is documented:
324
325         ./x.py doc
326         ./x.py doc --stage 1",
327                 );
328             }
329             _ => {}
330         };
331         // Get any optional paths which occur after the subcommand
332         let paths = matches.free[1..]
333             .iter()
334             .map(|p| p.into())
335             .collect::<Vec<PathBuf>>();
336
337         let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
338             if fs::metadata("config.toml").is_ok() {
339                 Some(PathBuf::from("config.toml"))
340             } else {
341                 None
342             }
343         });
344
345         // All subcommands except `clean` can have an optional "Available paths" section
346         if matches.opt_present("verbose") {
347             let config = Config::parse(&["build".to_string()]);
348             let mut build = Build::new(config);
349             metadata::build(&mut build);
350
351             let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
352             extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
353         } else if subcommand.as_str() != "clean" {
354             extra_help.push_str(
355                 format!(
356                     "Run `./x.py {} -h -v` to see a list of available paths.",
357                     subcommand
358                 ).as_str(),
359             );
360         }
361
362         // User passed in -h/--help?
363         if matches.opt_present("help") {
364             usage(0, &opts, &subcommand_help, &extra_help);
365         }
366
367         let cmd = match subcommand.as_str() {
368             "build" => Subcommand::Build { paths: paths },
369             "check" => Subcommand::Check { paths: paths },
370             "test" => Subcommand::Test {
371                 paths,
372                 bless: matches.opt_present("bless"),
373                 compare_mode: matches.opt_str("compare-mode"),
374                 test_args: matches.opt_strs("test-args"),
375                 rustc_args: matches.opt_strs("rustc-args"),
376                 fail_fast: !matches.opt_present("no-fail-fast"),
377                 doc_tests: if matches.opt_present("doc") {
378                     DocTests::Only
379                 } else if matches.opt_present("no-doc") {
380                     DocTests::No
381                 } else {
382                     DocTests::Yes
383                 },
384             },
385             "bench" => Subcommand::Bench {
386                 paths,
387                 test_args: matches.opt_strs("test-args"),
388             },
389             "doc" => Subcommand::Doc { paths: paths },
390             "clean" => {
391                 if paths.len() > 0 {
392                     println!("\nclean does not take a path argument\n");
393                     usage(1, &opts, &subcommand_help, &extra_help);
394                 }
395
396                 Subcommand::Clean {
397                     all: matches.opt_present("all"),
398                 }
399             }
400             "dist" => Subcommand::Dist { paths },
401             "install" => Subcommand::Install { paths },
402             _ => {
403                 usage(1, &opts, &subcommand_help, &extra_help);
404             }
405         };
406
407         Flags {
408             verbose: matches.opt_count("verbose"),
409             stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
410             dry_run: matches.opt_present("dry-run"),
411             on_fail: matches.opt_str("on-fail"),
412             rustc_error_format: matches.opt_str("error-format"),
413             keep_stage: matches.opt_strs("keep-stage")
414                 .into_iter().map(|j| j.parse().unwrap())
415                 .collect(),
416             host: split(matches.opt_strs("host"))
417                 .into_iter()
418                 .map(|x| INTERNER.intern_string(x))
419                 .collect::<Vec<_>>(),
420             target: split(matches.opt_strs("target"))
421                 .into_iter()
422                 .map(|x| INTERNER.intern_string(x))
423                 .collect::<Vec<_>>(),
424             config: cfg_file,
425             jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
426             cmd,
427             incremental: matches.opt_present("incremental"),
428             exclude: split(matches.opt_strs("exclude"))
429                 .into_iter()
430                 .map(|p| p.into())
431                 .collect::<Vec<_>>(),
432             warnings: matches.opt_str("warnings").map(|v| v == "deny"),
433         }
434     }
435 }
436
437 impl Subcommand {
438     pub fn test_args(&self) -> Vec<&str> {
439         match *self {
440             Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
441                 test_args
442                     .iter()
443                     .flat_map(|s| s.split_whitespace())
444                     .collect()
445             }
446             _ => Vec::new(),
447         }
448     }
449
450     pub fn rustc_args(&self) -> Vec<&str> {
451         match *self {
452             Subcommand::Test { ref rustc_args, .. } => rustc_args
453                 .iter()
454                 .flat_map(|s| s.split_whitespace())
455                 .collect(),
456             _ => Vec::new(),
457         }
458     }
459
460     pub fn fail_fast(&self) -> bool {
461         match *self {
462             Subcommand::Test { fail_fast, .. } => fail_fast,
463             _ => false,
464         }
465     }
466
467     pub fn doc_tests(&self) -> DocTests {
468         match *self {
469             Subcommand::Test { doc_tests, .. } => doc_tests,
470             _ => DocTests::Yes,
471         }
472     }
473
474     pub fn bless(&self) -> bool {
475         match *self {
476             Subcommand::Test { bless, .. } => bless,
477             _ => false,
478         }
479     }
480
481     pub fn compare_mode(&self) -> Option<&str> {
482         match *self {
483             Subcommand::Test {
484                 ref compare_mode, ..
485             } => compare_mode.as_ref().map(|s| &s[..]),
486             _ => None,
487         }
488     }
489 }
490
491 fn split(s: Vec<String>) -> Vec<String> {
492     s.iter()
493         .flat_map(|s| s.split(','))
494         .map(|s| s.to_string())
495         .collect()
496 }