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