]> git.proxmox.com Git - rustc.git/blob - src/bootstrap/flags.rs
New upstream version 1.42.0+dfsg1
[rustc.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 // This overrides the deny-warnings configuation option,
37 // which passes -Dwarnings to the compiler invocations.
38 //
39 // true => deny, false => warn
40 pub deny_warnings: Option<bool>,
41
42 pub llvm_skip_rebuild: Option<bool>,
43 }
44
45 pub enum Subcommand {
46 Build {
47 paths: Vec<PathBuf>,
48 },
49 Check {
50 paths: Vec<PathBuf>,
51 },
52 Clippy {
53 paths: Vec<PathBuf>,
54 },
55 Fix {
56 paths: Vec<PathBuf>,
57 },
58 Format {
59 check: bool,
60 },
61 Doc {
62 paths: Vec<PathBuf>,
63 },
64 Test {
65 paths: Vec<PathBuf>,
66 /// Whether to automatically update stderr/stdout files
67 bless: bool,
68 compare_mode: Option<String>,
69 pass: Option<String>,
70 test_args: Vec<String>,
71 rustc_args: Vec<String>,
72 fail_fast: bool,
73 doc_tests: DocTests,
74 rustfix_coverage: bool,
75 },
76 Bench {
77 paths: Vec<PathBuf>,
78 test_args: Vec<String>,
79 },
80 Clean {
81 all: bool,
82 },
83 Dist {
84 paths: Vec<PathBuf>,
85 },
86 Install {
87 paths: Vec<PathBuf>,
88 },
89 }
90
91 impl Default for Subcommand {
92 fn default() -> Subcommand {
93 Subcommand::Build { paths: vec![PathBuf::from("nowhere")] }
94 }
95 }
96
97 impl Flags {
98 pub fn parse(args: &[String]) -> Flags {
99 let mut extra_help = String::new();
100 let mut subcommand_help = String::from(
101 "\
102 Usage: x.py <subcommand> [options] [<paths>...]
103
104 Subcommands:
105 build Compile either the compiler or libraries
106 check Compile either the compiler or libraries, using cargo check
107 clippy Run clippy (uses rustup/cargo-installed clippy binary)
108 fix Run cargo fix
109 fmt Run rustfmt
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
114 dist Build distribution artifacts
115 install Install distribution artifacts
116
117 To learn more about a subcommand, run `./x.py <subcommand> -h`",
118 );
119
120 let mut opts = Options::new();
121 // Options common to all subcommands
122 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
123 opts.optflag("i", "incremental", "use incremental compilation");
124 opts.optopt("", "config", "TOML configuration file for build", "FILE");
125 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
126 opts.optmulti("", "host", "host targets to build", "HOST");
127 opts.optmulti("", "target", "target targets to build", "TARGET");
128 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
129 opts.optopt("", "on-fail", "command to run on failure", "CMD");
130 opts.optflag("", "dry-run", "dry run; don't build anything");
131 opts.optopt(
132 "",
133 "stage",
134 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
135 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
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 );
145 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
146 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
147 opts.optflag("h", "help", "print this help message");
148 opts.optopt(
149 "",
150 "warnings",
151 "if value is deny, will deny warnings, otherwise use default",
152 "VALUE",
153 );
154 opts.optopt("", "error-format", "rustc error format", "FORMAT");
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 );
163
164 // fn usage()
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 };
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.
179 let subcommand = args.iter().find(|&s| {
180 (s == "build")
181 || (s == "check")
182 || (s == "clippy")
183 || (s == "fix")
184 || (s == "fmt")
185 || (s == "test")
186 || (s == "bench")
187 || (s == "doc")
188 || (s == "clean")
189 || (s == "dist")
190 || (s == "install")
191 });
192 let subcommand = match subcommand {
193 Some(s) => s,
194 None => {
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.
198 println!("{}\n", subcommand_help);
199 let exit_code = if args.is_empty() { 0 } else { 1 };
200 process::exit(exit_code);
201 }
202 };
203
204 // Some subcommands get extra options
205 match subcommand.as_str() {
206 "test" => {
207 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
208 opts.optmulti("", "test-args", "extra arguments", "ARGS");
209 opts.optmulti(
210 "",
211 "rustc-args",
212 "extra options to pass the compiler when running tests",
213 "ARGS",
214 );
215 opts.optflag("", "no-doc", "do not run doc tests");
216 opts.optflag("", "doc", "only run doc tests");
217 opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests");
218 opts.optopt(
219 "",
220 "compare-mode",
221 "mode describing what file the actual ui output will be compared to",
222 "COMPARE MODE",
223 );
224 opts.optopt(
225 "",
226 "pass",
227 "force {check,build,run}-pass tests to this mode.",
228 "check | build | run",
229 );
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 );
236 }
237 "bench" => {
238 opts.optmulti("", "test-args", "extra arguments", "ARGS");
239 }
240 "clean" => {
241 opts.optflag("", "all", "clean all build artifacts");
242 }
243 "fmt" => {
244 opts.optflag("", "check", "check formatting instead of applying.");
245 }
246 _ => {}
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) => {
263 if check_subcommand != subcommand {
264 pass_sanity_check = false;
265 }
266 }
267 None => {
268 pass_sanity_check = false;
269 }
270 }
271 if !pass_sanity_check {
272 println!("{}\n", subcommand_help);
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 );
277 process::exit(1);
278 }
279 // Extra help text for some commands
280 match subcommand.as_str() {
281 "build" => {
282 subcommand_help.push_str(
283 "\n
284 Arguments:
285 This subcommand accepts a number of paths to directories to the crates
286 and/or artifacts to compile. For example:
287
288 ./x.py build src/libcore
289 ./x.py build src/libcore src/libproc_macro
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
298 For a quick build of a usable compiler, you can pass:
299
300 ./x.py build --stage 1 src/libtest
301
302 This will first build everything once (like `--stage 0` without further
303 arguments would), and then use the compiler built in stage 0 to build
304 src/libtest and its dependencies.
305 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
306 );
307 }
308 "check" => {
309 subcommand_help.push_str(
310 "\n
311 Arguments:
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
322 the compiler.",
323 );
324 }
325 "clippy" => {
326 subcommand_help.push_str(
327 "\n
328 Arguments:
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
339 Arguments:
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 }
347 "fmt" => {
348 subcommand_help.push_str(
349 "\n
350 Arguments:
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 }
358 "test" => {
359 subcommand_help.push_str(
360 "\n
361 Arguments:
362 This subcommand accepts a number of paths to directories to tests that
363 should be compiled and run. For example:
364
365 ./x.py test src/test/ui
366 ./x.py test src/libstd --test-args hash_map
367 ./x.py test src/libstd --stage 0 --no-doc
368 ./x.py test src/test/ui --bless
369 ./x.py test src/test/ui --compare-mode nll
370
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
375 If no arguments are passed then the complete artifacts for that stage are
376 compiled and tested.
377
378 ./x.py test
379 ./x.py test --stage 1",
380 );
381 }
382 "doc" => {
383 subcommand_help.push_str(
384 "\n
385 Arguments:
386 This subcommand accepts a number of paths to directories of documentation
387 to build. For example:
388
389 ./x.py doc src/doc/book
390 ./x.py doc src/doc/nomicon
391 ./x.py doc src/doc/book src/libstd
392
393 If no arguments are passed then everything is documented:
394
395 ./x.py doc
396 ./x.py doc --stage 1",
397 );
398 }
399 _ => {}
400 };
401 // Get any optional paths which occur after the subcommand
402 let paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
403
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 });
411
412 // All subcommands except `clean` can have an optional "Available paths" section
413 if matches.opt_present("verbose") {
414 let config = Config::parse(&["build".to_string()]);
415 let mut build = Build::new(config);
416 metadata::build(&mut build);
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());
420 } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") {
421 extra_help.push_str(
422 format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand)
423 .as_str(),
424 );
425 }
426
427 // User passed in -h/--help?
428 if matches.opt_present("help") {
429 usage(0, &opts, &subcommand_help, &extra_help);
430 }
431
432 let cmd = match subcommand.as_str() {
433 "build" => Subcommand::Build { paths },
434 "check" => Subcommand::Check { paths },
435 "clippy" => Subcommand::Clippy { paths },
436 "fix" => Subcommand::Fix { paths },
437 "test" => Subcommand::Test {
438 paths,
439 bless: matches.opt_present("bless"),
440 compare_mode: matches.opt_str("compare-mode"),
441 pass: matches.opt_str("pass"),
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"),
445 rustfix_coverage: matches.opt_present("rustfix-coverage"),
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 },
454 "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") },
455 "doc" => Subcommand::Doc { paths },
456 "clean" => {
457 if !paths.is_empty() {
458 println!("\nclean does not take a path argument\n");
459 usage(1, &opts, &subcommand_help, &extra_help);
460 }
461
462 Subcommand::Clean { all: matches.opt_present("all") }
463 }
464 "fmt" => Subcommand::Format { check: matches.opt_present("check") },
465 "dist" => Subcommand::Dist { paths },
466 "install" => Subcommand::Install { paths },
467 _ => {
468 usage(1, &opts, &subcommand_help, &extra_help);
469 }
470 };
471
472 Flags {
473 verbose: matches.opt_count("verbose"),
474 stage: matches.opt_str("stage").map(|j| j.parse().expect("`stage` should be a number")),
475 dry_run: matches.opt_present("dry-run"),
476 on_fail: matches.opt_str("on-fail"),
477 rustc_error_format: matches.opt_str("error-format"),
478 keep_stage: matches
479 .opt_strs("keep-stage")
480 .into_iter()
481 .map(|j| j.parse().expect("`keep-stage` should be a number"))
482 .collect(),
483 host: split(&matches.opt_strs("host"))
484 .into_iter()
485 .map(|x| INTERNER.intern_string(x))
486 .collect::<Vec<_>>(),
487 target: split(&matches.opt_strs("target"))
488 .into_iter()
489 .map(|x| INTERNER.intern_string(x))
490 .collect::<Vec<_>>(),
491 config: cfg_file,
492 jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")),
493 cmd,
494 incremental: matches.opt_present("incremental"),
495 exclude: split(&matches.opt_strs("exclude"))
496 .into_iter()
497 .map(|p| p.into())
498 .collect::<Vec<_>>(),
499 deny_warnings: parse_deny_warnings(&matches),
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 ),
503 }
504 }
505 }
506
507 impl Subcommand {
508 pub fn test_args(&self) -> Vec<&str> {
509 match *self {
510 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
511 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
512 }
513 _ => Vec::new(),
514 }
515 }
516
517 pub fn rustc_args(&self) -> Vec<&str> {
518 match *self {
519 Subcommand::Test { ref rustc_args, .. } => {
520 rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
521 }
522 _ => Vec::new(),
523 }
524 }
525
526 pub fn fail_fast(&self) -> bool {
527 match *self {
528 Subcommand::Test { fail_fast, .. } => fail_fast,
529 _ => false,
530 }
531 }
532
533 pub fn doc_tests(&self) -> DocTests {
534 match *self {
535 Subcommand::Test { doc_tests, .. } => doc_tests,
536 _ => DocTests::Yes,
537 }
538 }
539
540 pub fn bless(&self) -> bool {
541 match *self {
542 Subcommand::Test { bless, .. } => bless,
543 _ => false,
544 }
545 }
546
547 pub fn rustfix_coverage(&self) -> bool {
548 match *self {
549 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
550 _ => false,
551 }
552 }
553
554 pub fn compare_mode(&self) -> Option<&str> {
555 match *self {
556 Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]),
557 _ => None,
558 }
559 }
560
561 pub fn pass(&self) -> Option<&str> {
562 match *self {
563 Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]),
564 _ => None,
565 }
566 }
567 }
568
569 fn split(s: &[String]) -> Vec<String> {
570 s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
571 }
572
573 fn parse_deny_warnings(matches: &getopts::Matches) -> Option<bool> {
574 match matches.opt_str("warnings").as_ref().map(|v| v.as_str()) {
575 Some("deny") => Some(true),
576 Some("warn") => Some(false),
577 Some(value) => {
578 eprintln!(r#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,);
579 process::exit(1);
580 }
581 None => None,
582 }
583 }