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