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