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