]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/flags.rs
Auto merge of #87598 - ccqpein:master, r=yaahc
[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::env;
7 use std::path::PathBuf;
8 use std::process;
9
10 use build_helper::t;
11 use getopts::Options;
12
13 use crate::builder::Builder;
14 use crate::config::{Config, TargetSelection};
15 use crate::setup::Profile;
16 use crate::{Build, DocTests};
17
18 pub enum Color {
19     Always,
20     Never,
21     Auto,
22 }
23
24 impl Default for Color {
25     fn default() -> Self {
26         Self::Auto
27     }
28 }
29
30 impl std::str::FromStr for Color {
31     type Err = ();
32
33     fn from_str(s: &str) -> Result<Self, Self::Err> {
34         match s.to_lowercase().as_str() {
35             "always" => Ok(Self::Always),
36             "never" => Ok(Self::Never),
37             "auto" => Ok(Self::Auto),
38             _ => Err(()),
39         }
40     }
41 }
42
43 /// Deserialized version of all flags for this compile.
44 pub struct Flags {
45     pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
46     pub on_fail: Option<String>,
47     pub stage: Option<u32>,
48     pub keep_stage: Vec<u32>,
49     pub keep_stage_std: Vec<u32>,
50
51     pub host: Option<Vec<TargetSelection>>,
52     pub target: Option<Vec<TargetSelection>>,
53     pub config: Option<PathBuf>,
54     pub jobs: Option<u32>,
55     pub cmd: Subcommand,
56     pub incremental: bool,
57     pub exclude: Vec<PathBuf>,
58     pub include_default_paths: bool,
59     pub rustc_error_format: Option<String>,
60     pub json_output: bool,
61     pub dry_run: bool,
62     pub color: Color,
63
64     // This overrides the deny-warnings configuration option,
65     // which passes -Dwarnings to the compiler invocations.
66     //
67     // true => deny, false => warn
68     pub deny_warnings: Option<bool>,
69
70     pub llvm_skip_rebuild: Option<bool>,
71
72     pub rust_profile_use: Option<String>,
73     pub rust_profile_generate: Option<String>,
74 }
75
76 pub enum Subcommand {
77     Build {
78         paths: Vec<PathBuf>,
79     },
80     Check {
81         paths: Vec<PathBuf>,
82     },
83     Clippy {
84         fix: bool,
85         paths: Vec<PathBuf>,
86     },
87     Fix {
88         paths: Vec<PathBuf>,
89     },
90     Format {
91         paths: Vec<PathBuf>,
92         check: bool,
93     },
94     Doc {
95         paths: Vec<PathBuf>,
96         open: bool,
97     },
98     Test {
99         paths: Vec<PathBuf>,
100         /// Whether to automatically update stderr/stdout files
101         bless: bool,
102         force_rerun: bool,
103         compare_mode: Option<String>,
104         pass: Option<String>,
105         run: Option<String>,
106         test_args: Vec<String>,
107         rustc_args: Vec<String>,
108         fail_fast: bool,
109         doc_tests: DocTests,
110         rustfix_coverage: bool,
111     },
112     Bench {
113         paths: Vec<PathBuf>,
114         test_args: Vec<String>,
115     },
116     Clean {
117         all: bool,
118     },
119     Dist {
120         paths: Vec<PathBuf>,
121     },
122     Install {
123         paths: Vec<PathBuf>,
124     },
125     Run {
126         paths: Vec<PathBuf>,
127     },
128     Setup {
129         profile: Profile,
130     },
131 }
132
133 impl Default for Subcommand {
134     fn default() -> Subcommand {
135         Subcommand::Build { paths: vec![PathBuf::from("nowhere")] }
136     }
137 }
138
139 impl Flags {
140     pub fn parse(args: &[String]) -> Flags {
141         let mut subcommand_help = String::from(
142             "\
143 Usage: x.py <subcommand> [options] [<paths>...]
144
145 Subcommands:
146     build, b    Compile either the compiler or libraries
147     check, c    Compile either the compiler or libraries, using cargo check
148     clippy      Run clippy (uses rustup/cargo-installed clippy binary)
149     fix         Run cargo fix
150     fmt         Run rustfmt
151     test, t     Build and run some test suites
152     bench       Build and run some benchmarks
153     doc, d      Build documentation
154     clean       Clean out build directories
155     dist        Build distribution artifacts
156     install     Install distribution artifacts
157     run, r      Run tools contained in this repository
158     setup       Create a config.toml (making it easier to use `x.py` itself)
159
160 To learn more about a subcommand, run `./x.py <subcommand> -h`",
161         );
162
163         let mut opts = Options::new();
164         // Options common to all subcommands
165         opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
166         opts.optflag("i", "incremental", "use incremental compilation");
167         opts.optopt("", "config", "TOML configuration file for build", "FILE");
168         opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
169         opts.optmulti("", "host", "host targets to build", "HOST");
170         opts.optmulti("", "target", "target targets to build", "TARGET");
171         opts.optmulti("", "exclude", "build paths to exclude", "PATH");
172         opts.optflag(
173             "",
174             "include-default-paths",
175             "include default paths in addition to the provided ones",
176         );
177         opts.optopt("", "on-fail", "command to run on failure", "CMD");
178         opts.optflag("", "dry-run", "dry run; don't build anything");
179         opts.optopt(
180             "",
181             "stage",
182             "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
183              bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
184             "N",
185         );
186         opts.optmulti(
187             "",
188             "keep-stage",
189             "stage(s) to keep without recompiling \
190             (pass multiple times to keep e.g., both stages 0 and 1)",
191             "N",
192         );
193         opts.optmulti(
194             "",
195             "keep-stage-std",
196             "stage(s) of the standard library to keep without recompiling \
197             (pass multiple times to keep e.g., both stages 0 and 1)",
198             "N",
199         );
200         opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
201         let j_msg = format!(
202             "number of jobs to run in parallel; \
203              defaults to {} (this host's logical CPU count)",
204             num_cpus::get()
205         );
206         opts.optopt("j", "jobs", &j_msg, "JOBS");
207         opts.optflag("h", "help", "print this help message");
208         opts.optopt(
209             "",
210             "warnings",
211             "if value is deny, will deny warnings, otherwise use default",
212             "VALUE",
213         );
214         opts.optopt("", "error-format", "rustc error format", "FORMAT");
215         opts.optflag("", "json-output", "use message-format=json");
216         opts.optopt("", "color", "whether to use color in cargo and rustc output", "STYLE");
217         opts.optopt(
218             "",
219             "llvm-skip-rebuild",
220             "whether rebuilding llvm should be skipped \
221              a VALUE of TRUE indicates that llvm will not be rebuilt \
222              VALUE overrides the skip-rebuild option in config.toml.",
223             "VALUE",
224         );
225         opts.optopt("", "rust-profile-generate", "generate PGO profile with rustc build", "FORMAT");
226         opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "FORMAT");
227
228         // We can't use getopt to parse the options until we have completed specifying which
229         // options are valid, but under the current implementation, some options are conditional on
230         // the subcommand. Therefore we must manually identify the subcommand first, so that we can
231         // complete the definition of the options.  Then we can use the getopt::Matches object from
232         // there on out.
233         let subcommand = args.iter().find(|&s| {
234             (s == "build")
235                 || (s == "b")
236                 || (s == "check")
237                 || (s == "c")
238                 || (s == "clippy")
239                 || (s == "fix")
240                 || (s == "fmt")
241                 || (s == "test")
242                 || (s == "t")
243                 || (s == "bench")
244                 || (s == "doc")
245                 || (s == "d")
246                 || (s == "clean")
247                 || (s == "dist")
248                 || (s == "install")
249                 || (s == "run")
250                 || (s == "r")
251                 || (s == "setup")
252         });
253         let subcommand = match subcommand {
254             Some(s) => s,
255             None => {
256                 // No or an invalid subcommand -- show the general usage and subcommand help
257                 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
258                 // subcommand.
259                 println!("{}\n", subcommand_help);
260                 let exit_code = if args.is_empty() { 0 } else { 1 };
261                 process::exit(exit_code);
262             }
263         };
264
265         // Some subcommands get extra options
266         match subcommand.as_str() {
267             "test" | "t" => {
268                 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
269                 opts.optmulti(
270                     "",
271                     "test-args",
272                     "extra arguments to be passed for the test tool being used \
273                         (e.g. libtest, compiletest or rustdoc)",
274                     "ARGS",
275                 );
276                 opts.optmulti(
277                     "",
278                     "rustc-args",
279                     "extra options to pass the compiler when running tests",
280                     "ARGS",
281                 );
282                 opts.optflag("", "no-doc", "do not run doc tests");
283                 opts.optflag("", "doc", "only run doc tests");
284                 opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests");
285                 opts.optflag("", "force-rerun", "rerun tests even if the inputs are unchanged");
286                 opts.optopt(
287                     "",
288                     "compare-mode",
289                     "mode describing what file the actual ui output will be compared to",
290                     "COMPARE MODE",
291                 );
292                 opts.optopt(
293                     "",
294                     "pass",
295                     "force {check,build,run}-pass tests to this mode.",
296                     "check | build | run",
297                 );
298                 opts.optopt("", "run", "whether to execute run-* tests", "auto | always | never");
299                 opts.optflag(
300                     "",
301                     "rustfix-coverage",
302                     "enable this to generate a Rustfix coverage file, which is saved in \
303                         `/<build_base>/rustfix_missing_coverage.txt`",
304                 );
305             }
306             "check" | "c" => {
307                 opts.optflag("", "all-targets", "Check all targets");
308             }
309             "bench" => {
310                 opts.optmulti("", "test-args", "extra arguments", "ARGS");
311             }
312             "clippy" => {
313                 opts.optflag("", "fix", "automatically apply lint suggestions");
314             }
315             "doc" | "d" => {
316                 opts.optflag("", "open", "open the docs in a browser");
317             }
318             "clean" => {
319                 opts.optflag("", "all", "clean all build artifacts");
320             }
321             "fmt" => {
322                 opts.optflag("", "check", "check formatting instead of applying.");
323             }
324             _ => {}
325         };
326
327         // fn usage()
328         let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
329             let mut extra_help = String::new();
330
331             // All subcommands except `clean` can have an optional "Available paths" section
332             if verbose {
333                 let config = Config::parse(&["build".to_string()]);
334                 let build = Build::new(config);
335
336                 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
337                 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
338             } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") {
339                 extra_help.push_str(
340                     format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand)
341                         .as_str(),
342                 );
343             }
344
345             println!("{}", opts.usage(subcommand_help));
346             if !extra_help.is_empty() {
347                 println!("{}", extra_help);
348             }
349             process::exit(exit_code);
350         };
351
352         // Done specifying what options are possible, so do the getopts parsing
353         let matches = opts.parse(args).unwrap_or_else(|e| {
354             // Invalid argument/option format
355             println!("\n{}\n", e);
356             usage(1, &opts, false, &subcommand_help);
357         });
358
359         // Extra sanity check to make sure we didn't hit this crazy corner case:
360         //
361         //     ./x.py --frobulate clean build
362         //            ^-- option  ^     ^- actual subcommand
363         //                        \_ arg to option could be mistaken as subcommand
364         let mut pass_sanity_check = true;
365         match matches.free.get(0) {
366             Some(check_subcommand) => {
367                 if check_subcommand != subcommand {
368                     pass_sanity_check = false;
369                 }
370             }
371             None => {
372                 pass_sanity_check = false;
373             }
374         }
375         if !pass_sanity_check {
376             println!("{}\n", subcommand_help);
377             println!(
378                 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
379                  You may need to move some options to after the subcommand.\n"
380             );
381             process::exit(1);
382         }
383         // Extra help text for some commands
384         match subcommand.as_str() {
385             "build" | "b" => {
386                 subcommand_help.push_str(
387                     "\n
388 Arguments:
389     This subcommand accepts a number of paths to directories to the crates
390     and/or artifacts to compile. For example:
391
392         ./x.py build library/core
393         ./x.py build library/core library/proc_macro
394         ./x.py build library/std --stage 1
395
396     If no arguments are passed then the complete artifacts for that stage are
397     also compiled.
398
399         ./x.py build
400         ./x.py build --stage 1
401
402     For a quick build of a usable compiler, you can pass:
403
404         ./x.py build --stage 1 library/test
405
406     This will first build everything once (like `--stage 0` without further
407     arguments would), and then use the compiler built in stage 0 to build
408     library/test and its dependencies.
409     Once this is done, build/$ARCH/stage1 contains a usable compiler.",
410                 );
411             }
412             "check" | "c" => {
413                 subcommand_help.push_str(
414                     "\n
415 Arguments:
416     This subcommand accepts a number of paths to directories to the crates
417     and/or artifacts to compile. For example:
418
419         ./x.py check library/core
420         ./x.py check library/core library/proc_macro
421
422     If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
423     also that since we use `cargo check`, by default this will automatically enable incremental
424     compilation, so there's no need to pass it separately, though it won't hurt. We also completely
425     ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
426     the compiler.",
427                 );
428             }
429             "clippy" => {
430                 subcommand_help.push_str(
431                     "\n
432 Arguments:
433     This subcommand accepts a number of paths to directories to the crates
434     and/or artifacts to run clippy against. For example:
435
436         ./x.py clippy library/core
437         ./x.py clippy library/core library/proc_macro",
438                 );
439             }
440             "fix" => {
441                 subcommand_help.push_str(
442                     "\n
443 Arguments:
444     This subcommand accepts a number of paths to directories to the crates
445     and/or artifacts to run `cargo fix` against. For example:
446
447         ./x.py fix library/core
448         ./x.py fix library/core library/proc_macro",
449                 );
450             }
451             "fmt" => {
452                 subcommand_help.push_str(
453                     "\n
454 Arguments:
455     This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and
456     fails if it is not. For example:
457
458         ./x.py fmt
459         ./x.py fmt --check",
460                 );
461             }
462             "test" | "t" => {
463                 subcommand_help.push_str(
464                     "\n
465 Arguments:
466     This subcommand accepts a number of paths to test directories that
467     should be compiled and run. For example:
468
469         ./x.py test src/test/ui
470         ./x.py test library/std --test-args hash_map
471         ./x.py test library/std --stage 0 --no-doc
472         ./x.py test src/test/ui --bless
473         ./x.py test src/test/ui --compare-mode nll
474
475     Note that `test src/test/* --stage N` does NOT depend on `build compiler/rustc --stage N`;
476     just like `build library/std --stage N` it tests the compiler produced by the previous
477     stage.
478
479     Execute tool tests with a tool name argument:
480
481         ./x.py test tidy
482
483     If no arguments are passed then the complete artifacts for that stage are
484     compiled and tested.
485
486         ./x.py test
487         ./x.py test --stage 1",
488                 );
489             }
490             "doc" | "d" => {
491                 subcommand_help.push_str(
492                     "\n
493 Arguments:
494     This subcommand accepts a number of paths to directories of documentation
495     to build. For example:
496
497         ./x.py doc src/doc/book
498         ./x.py doc src/doc/nomicon
499         ./x.py doc src/doc/book library/std
500         ./x.py doc library/std --open
501
502     If no arguments are passed then everything is documented:
503
504         ./x.py doc
505         ./x.py doc --stage 1",
506                 );
507             }
508             "run" | "r" => {
509                 subcommand_help.push_str(
510                     "\n
511 Arguments:
512     This subcommand accepts a number of paths to tools to build and run. For
513     example:
514
515         ./x.py run src/tools/expand-yaml-anchors
516
517     At least a tool needs to be called.",
518                 );
519             }
520             "setup" => {
521                 subcommand_help.push_str(&format!(
522                     "\n
523 x.py setup creates a `config.toml` which changes the defaults for x.py itself.
524
525 Arguments:
526     This subcommand accepts a 'profile' to use for builds. For example:
527
528         ./x.py setup library
529
530     The profile is optional and you will be prompted interactively if it is not given.
531     The following profiles are available:
532
533 {}",
534                     Profile::all_for_help("        ").trim_end()
535                 ));
536             }
537             _ => {}
538         };
539         // Get any optional paths which occur after the subcommand
540         let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
541
542         let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);
543         let verbose = matches.opt_present("verbose");
544
545         // User passed in -h/--help?
546         if matches.opt_present("help") {
547             usage(0, &opts, verbose, &subcommand_help);
548         }
549
550         let cmd = match subcommand.as_str() {
551             "build" | "b" => Subcommand::Build { paths },
552             "check" | "c" => {
553                 if matches.opt_present("all-targets") {
554                     eprintln!(
555                         "Warning: --all-targets is now on by default and does not need to be passed explicitly."
556                     );
557                 }
558                 Subcommand::Check { paths }
559             }
560             "clippy" => Subcommand::Clippy { paths, fix: matches.opt_present("fix") },
561             "fix" => Subcommand::Fix { paths },
562             "test" | "t" => Subcommand::Test {
563                 paths,
564                 bless: matches.opt_present("bless"),
565                 force_rerun: matches.opt_present("force-rerun"),
566                 compare_mode: matches.opt_str("compare-mode"),
567                 pass: matches.opt_str("pass"),
568                 run: matches.opt_str("run"),
569                 test_args: matches.opt_strs("test-args"),
570                 rustc_args: matches.opt_strs("rustc-args"),
571                 fail_fast: !matches.opt_present("no-fail-fast"),
572                 rustfix_coverage: matches.opt_present("rustfix-coverage"),
573                 doc_tests: if matches.opt_present("doc") {
574                     DocTests::Only
575                 } else if matches.opt_present("no-doc") {
576                     DocTests::No
577                 } else {
578                     DocTests::Yes
579                 },
580             },
581             "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") },
582             "doc" | "d" => Subcommand::Doc { paths, open: matches.opt_present("open") },
583             "clean" => {
584                 if !paths.is_empty() {
585                     println!("\nclean does not take a path argument\n");
586                     usage(1, &opts, verbose, &subcommand_help);
587                 }
588
589                 Subcommand::Clean { all: matches.opt_present("all") }
590             }
591             "fmt" => Subcommand::Format { check: matches.opt_present("check"), paths },
592             "dist" => Subcommand::Dist { paths },
593             "install" => Subcommand::Install { paths },
594             "run" | "r" => {
595                 if paths.is_empty() {
596                     println!("\nrun requires at least a path!\n");
597                     usage(1, &opts, verbose, &subcommand_help);
598                 }
599                 Subcommand::Run { paths }
600             }
601             "setup" => {
602                 let profile = if paths.len() > 1 {
603                     println!("\nat most one profile can be passed to setup\n");
604                     usage(1, &opts, verbose, &subcommand_help)
605                 } else if let Some(path) = paths.pop() {
606                     let profile_string = t!(path.into_os_string().into_string().map_err(
607                         |path| format!("{} is not a valid UTF8 string", path.to_string_lossy())
608                     ));
609
610                     profile_string.parse().unwrap_or_else(|err| {
611                         eprintln!("error: {}", err);
612                         eprintln!("help: the available profiles are:");
613                         eprint!("{}", Profile::all_for_help("- "));
614                         std::process::exit(1);
615                     })
616                 } else {
617                     t!(crate::setup::interactive_path())
618                 };
619                 Subcommand::Setup { profile }
620             }
621             _ => {
622                 usage(1, &opts, verbose, &subcommand_help);
623             }
624         };
625
626         if let Subcommand::Check { .. } = &cmd {
627             if matches.opt_str("keep-stage").is_some()
628                 || matches.opt_str("keep-stage-std").is_some()
629             {
630                 println!("--keep-stage not yet supported for x.py check");
631                 process::exit(1);
632             }
633         }
634
635         Flags {
636             verbose: matches.opt_count("verbose"),
637             stage: matches.opt_str("stage").map(|j| j.parse().expect("`stage` should be a number")),
638             dry_run: matches.opt_present("dry-run"),
639             on_fail: matches.opt_str("on-fail"),
640             rustc_error_format: matches.opt_str("error-format"),
641             json_output: matches.opt_present("json-output"),
642             keep_stage: matches
643                 .opt_strs("keep-stage")
644                 .into_iter()
645                 .map(|j| j.parse().expect("`keep-stage` should be a number"))
646                 .collect(),
647             keep_stage_std: matches
648                 .opt_strs("keep-stage-std")
649                 .into_iter()
650                 .map(|j| j.parse().expect("`keep-stage-std` should be a number"))
651                 .collect(),
652             host: if matches.opt_present("host") {
653                 Some(
654                     split(&matches.opt_strs("host"))
655                         .into_iter()
656                         .map(|x| TargetSelection::from_user(&x))
657                         .collect::<Vec<_>>(),
658                 )
659             } else {
660                 None
661             },
662             target: if matches.opt_present("target") {
663                 Some(
664                     split(&matches.opt_strs("target"))
665                         .into_iter()
666                         .map(|x| TargetSelection::from_user(&x))
667                         .collect::<Vec<_>>(),
668                 )
669             } else {
670                 None
671             },
672             config: cfg_file,
673             jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")),
674             cmd,
675             incremental: matches.opt_present("incremental"),
676             exclude: split(&matches.opt_strs("exclude"))
677                 .into_iter()
678                 .map(|p| p.into())
679                 .collect::<Vec<_>>(),
680             include_default_paths: matches.opt_present("include-default-paths"),
681             deny_warnings: parse_deny_warnings(&matches),
682             llvm_skip_rebuild: matches.opt_str("llvm-skip-rebuild").map(|s| s.to_lowercase()).map(
683                 |s| s.parse::<bool>().expect("`llvm-skip-rebuild` should be either true or false"),
684             ),
685             color: matches
686                 .opt_get_default("color", Color::Auto)
687                 .expect("`color` should be `always`, `never`, or `auto`"),
688             rust_profile_use: matches.opt_str("rust-profile-use"),
689             rust_profile_generate: matches.opt_str("rust-profile-generate"),
690         }
691     }
692 }
693
694 impl Subcommand {
695     pub fn test_args(&self) -> Vec<&str> {
696         match *self {
697             Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
698                 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
699             }
700             _ => Vec::new(),
701         }
702     }
703
704     pub fn rustc_args(&self) -> Vec<&str> {
705         match *self {
706             Subcommand::Test { ref rustc_args, .. } => {
707                 rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
708             }
709             _ => Vec::new(),
710         }
711     }
712
713     pub fn fail_fast(&self) -> bool {
714         match *self {
715             Subcommand::Test { fail_fast, .. } => fail_fast,
716             _ => false,
717         }
718     }
719
720     pub fn doc_tests(&self) -> DocTests {
721         match *self {
722             Subcommand::Test { doc_tests, .. } => doc_tests,
723             _ => DocTests::Yes,
724         }
725     }
726
727     pub fn bless(&self) -> bool {
728         match *self {
729             Subcommand::Test { bless, .. } => bless,
730             _ => false,
731         }
732     }
733
734     pub fn force_rerun(&self) -> bool {
735         match *self {
736             Subcommand::Test { force_rerun, .. } => force_rerun,
737             _ => false,
738         }
739     }
740
741     pub fn rustfix_coverage(&self) -> bool {
742         match *self {
743             Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
744             _ => false,
745         }
746     }
747
748     pub fn compare_mode(&self) -> Option<&str> {
749         match *self {
750             Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]),
751             _ => None,
752         }
753     }
754
755     pub fn pass(&self) -> Option<&str> {
756         match *self {
757             Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]),
758             _ => None,
759         }
760     }
761
762     pub fn run(&self) -> Option<&str> {
763         match *self {
764             Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]),
765             _ => None,
766         }
767     }
768
769     pub fn open(&self) -> bool {
770         match *self {
771             Subcommand::Doc { open, .. } => open,
772             _ => false,
773         }
774     }
775 }
776
777 fn split(s: &[String]) -> Vec<String> {
778     s.iter().flat_map(|s| s.split(',')).filter(|s| !s.is_empty()).map(|s| s.to_string()).collect()
779 }
780
781 fn parse_deny_warnings(matches: &getopts::Matches) -> Option<bool> {
782     match matches.opt_str("warnings").as_deref() {
783         Some("deny") => Some(true),
784         Some("warn") => Some(false),
785         Some(value) => {
786             eprintln!(r#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,);
787             process::exit(1);
788         }
789         None => None,
790     }
791 }