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