Skip to main content

bootstrap/core/config/
flags.rs

1//! Command-line interface of the bootstrap 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
6use std::path::{Path, PathBuf};
7
8use clap::{CommandFactory, Parser, ValueEnum};
9use clap_complete::Generator;
10#[cfg(feature = "tracing")]
11use tracing::instrument;
12
13use crate::core::build_steps::perf::PerfArgs;
14use crate::core::build_steps::setup::Profile;
15use crate::core::builder::{Builder, Kind};
16use crate::core::config::Config;
17use crate::core::config::target_selection::{TargetSelectionList, target_selection_list};
18use crate::{Build, CodegenBackendKind, TestTarget};
19
20#[derive(Copy, Clone, Default, Debug, ValueEnum)]
21pub enum Color {
22    Always,
23    Never,
24    #[default]
25    Auto,
26}
27
28/// Whether to deny warnings, emit them as warnings, or use the default behavior
29#[derive(Copy, Clone, Default, Debug, ValueEnum)]
30pub enum Warnings {
31    Deny,
32    Warn,
33    #[default]
34    Default,
35}
36
37/// Deserialized version of all flags for this compile.
38#[derive(Debug, Parser)]
39#[command(
40    override_usage = "x.py <subcommand> [options] [<paths>...]",
41    disable_help_subcommand(true),
42    about = "",
43    next_line_help(false)
44)]
45pub struct Flags {
46    #[command(subcommand)]
47    pub cmd: Subcommand,
48
49    #[arg(global = true, short, long, action = clap::ArgAction::Count)]
50    /// use verbose output (-vv for very verbose)
51    pub verbose: u8, // each extra -v after the first is passed to Cargo
52    #[arg(global = true, short, long)]
53    /// use incremental compilation
54    pub incremental: bool,
55    #[arg(global = true, long, value_hint = clap::ValueHint::FilePath, value_name = "FILE")]
56    /// TOML configuration file for build
57    pub config: Option<PathBuf>,
58    #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")]
59    /// Build directory, overrides `build.build-dir` in `bootstrap.toml`
60    pub build_dir: Option<PathBuf>,
61
62    #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "BUILD")]
63    /// host target of the stage0 compiler
64    pub build: Option<String>,
65
66    #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "HOST", value_parser = target_selection_list)]
67    /// host targets to build
68    pub host: Option<TargetSelectionList>,
69
70    #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "TARGET", value_parser = target_selection_list)]
71    /// target targets to build
72    pub target: Option<TargetSelectionList>,
73
74    #[arg(global = true, long, value_name = "PATH")]
75    /// build paths to exclude
76    pub exclude: Vec<PathBuf>, // keeping for client backward compatibility
77    #[arg(global = true, long, value_name = "PATH")]
78    /// build paths to skip
79    pub skip: Vec<PathBuf>,
80    #[arg(global = true, long)]
81    /// include default paths in addition to the provided ones
82    pub include_default_paths: bool,
83
84    /// rustc error format
85    #[arg(global = true, value_hint = clap::ValueHint::Other, long)]
86    pub rustc_error_format: Option<String>,
87
88    #[arg(global = true, long, value_hint = clap::ValueHint::CommandString, value_name = "CMD")]
89    /// command to run on failure
90    pub on_fail: Option<String>,
91    #[arg(global = true, long)]
92    /// dry run; don't build anything
93    pub dry_run: bool,
94    /// Indicates whether to dump the work done from bootstrap shims
95    #[arg(global = true, long)]
96    pub dump_bootstrap_shims: bool,
97    #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
98    /// stage to build (indicates compiler to use/test, e.g., stage 0 uses the
99    /// bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)
100    pub stage: Option<u32>,
101
102    #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
103    /// stage(s) to keep without recompiling
104    /// (pass multiple times to keep e.g., both stages 0 and 1)
105    pub keep_stage: Vec<u32>,
106    #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
107    /// stage(s) of the standard library to keep without recompiling
108    /// (pass multiple times to keep e.g., both stages 0 and 1)
109    pub keep_stage_std: Vec<u32>,
110    #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")]
111    /// path to the root of the rust checkout
112    pub src: Option<PathBuf>,
113
114    #[arg(
115        global = true,
116        short,
117        long,
118        value_hint = clap::ValueHint::Other,
119        value_name = "JOBS"
120    )]
121    /// number of jobs to run in parallel
122    pub jobs: Option<u32>,
123    // This overrides the deny-warnings configuration option,
124    // which passes -Dwarnings to the compiler invocations.
125    #[arg(global = true, long)]
126    #[arg(value_enum, default_value_t=Warnings::Default, value_name = "deny|warn")]
127    /// if value is deny, will deny warnings
128    /// if value is warn, will emit warnings
129    /// otherwise, use the default configured behaviour
130    pub warnings: Warnings,
131
132    #[arg(global = true, long)]
133    /// use message-format=json
134    pub json_output: bool,
135    #[arg(global = true, long)]
136    /// only build proc-macros and build scripts (for rust-analyzer)
137    pub compile_time_deps: bool,
138
139    #[arg(global = true, long, value_name = "STYLE")]
140    #[arg(value_enum, default_value_t = Color::Auto)]
141    /// whether to use color in cargo and rustc output
142    pub color: Color,
143
144    #[arg(global = true, long)]
145    /// Bootstrap uses this value to decide whether it should bypass locking the build process.
146    /// This is rarely needed (e.g., compiling the std library for different targets in parallel).
147    ///
148    /// Unless you know exactly what you are doing, you probably don't need this.
149    pub bypass_bootstrap_lock: bool,
150
151    /// generate PGO profile with rustc build
152    #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
153    pub rust_profile_generate: Option<String>,
154    /// use PGO profile for rustc build
155    #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
156    pub rust_profile_use: Option<String>,
157    /// use PGO profile for LLVM build
158    #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
159    pub llvm_profile_use: Option<String>,
160    // LLVM doesn't support a custom location for generating profile
161    // information.
162    //
163    // llvm_out/build/profiles/ is the location this writes to.
164    /// generate PGO profile with llvm built for rustc
165    #[arg(global = true, long)]
166    pub llvm_profile_generate: bool,
167    /// Enable BOLT link flags
168    #[arg(global = true, long)]
169    pub enable_bolt_settings: bool,
170    /// Skip stage0 compiler validation
171    #[arg(global = true, long)]
172    pub skip_stage0_validation: bool,
173    /// Additional reproducible artifacts that should be added to the reproducible artifacts archive.
174    #[arg(global = true, long)]
175    pub reproducible_artifact: Vec<String>,
176    #[arg(global = true)]
177    /// paths for the subcommand
178    pub paths: Vec<PathBuf>,
179    /// override options in bootstrap.toml
180    #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "section.option=value")]
181    pub set: Vec<String>,
182    /// arguments passed to subcommands
183    #[arg(global = true, last(true), value_name = "ARGS")]
184    pub free_args: Vec<String>,
185    /// Make bootstrap to behave as it's running on the CI environment or not.
186    #[arg(global = true, long, value_name = "bool")]
187    pub ci: Option<bool>,
188    /// Skip checking the standard library if `rust.download-rustc` isn't available.
189    /// This is mostly for RA as building the stage1 compiler to check the library tree
190    /// on each code change might be too much for some computers.
191    #[arg(global = true, long)]
192    pub skip_std_check_if_no_download_rustc: bool,
193}
194
195impl Flags {
196    /// Check if `<cmd> -h -v` was passed.
197    /// If yes, print the available paths and return `true`.
198    pub fn try_parse_verbose_help(args: &[String]) -> bool {
199        // We need to check for `<cmd> -h -v`, in which case we list the paths
200        #[derive(Parser)]
201        #[command(disable_help_flag(true))]
202        struct HelpVerboseOnly {
203            #[arg(short, long)]
204            help: bool,
205            #[arg(global = true, short, long, action = clap::ArgAction::Count)]
206            pub verbose: u8,
207            #[arg(value_enum)]
208            cmd: Kind,
209        }
210        if let Ok(HelpVerboseOnly { help: true, verbose: 1.., cmd: subcommand }) =
211            HelpVerboseOnly::try_parse_from(normalize_args(args))
212        {
213            println!("NOTE: updating submodules before printing available paths");
214            let flags = Self::parse(&[String::from("build")]);
215            let config = Config::parse(flags);
216            let build = Build::new(config);
217            let paths = Builder::get_help(&build, subcommand);
218            if let Some(s) = paths {
219                println!("{s}");
220            } else {
221                panic!("No paths available for subcommand `{}`", subcommand.as_str());
222            }
223            true
224        } else {
225            false
226        }
227    }
228
229    #[cfg_attr(
230        feature = "tracing",
231        instrument(level = "trace", name = "Flags::parse", skip_all, fields(args = ?args))
232    )]
233    pub fn parse(args: &[String]) -> Self {
234        Flags::parse_from(normalize_args(args))
235    }
236}
237
238fn normalize_args(args: &[String]) -> Vec<String> {
239    let first = String::from("x.py");
240    let it = std::iter::once(first).chain(args.iter().cloned());
241    it.collect()
242}
243
244#[derive(Debug, Clone, clap::Subcommand)]
245pub enum Subcommand {
246    #[command(aliases = ["b"], long_about = "\n
247    Arguments:
248        This subcommand accepts a number of paths to directories to the crates
249        and/or artifacts to compile. For example, for a quick build of a usable
250        compiler:
251            ./x.py build --stage 1 library/std
252        This will build a compiler and standard library from the local source code.
253        Once this is done, build/$ARCH/stage1 contains a usable compiler.
254        If no arguments are passed then the default artifacts for that stage are
255        compiled. For example:
256            ./x.py build --stage 0
257            ./x.py build ")]
258    /// Compile either the compiler or libraries
259    Build {
260        #[arg(long)]
261        /// Pass `--timings` to Cargo to get crate build timings
262        timings: bool,
263    },
264    #[command(aliases = ["c"], long_about = "\n
265    Arguments:
266        This subcommand accepts a number of paths to directories to the crates
267        and/or artifacts to compile. For example:
268            ./x.py check library/std
269        If no arguments are passed then many artifacts are checked.")]
270    /// Compile either the compiler or libraries, using cargo check
271    Check {
272        #[arg(long)]
273        /// Check all targets
274        all_targets: bool,
275        #[arg(long)]
276        /// Pass `--timings` to Cargo to get crate build timings
277        timings: bool,
278    },
279    /// Run Clippy (uses rustup/cargo-installed clippy binary)
280    #[command(long_about = "\n
281    Arguments:
282        This subcommand accepts a number of paths to directories to the crates
283        and/or artifacts to run clippy against. For example:
284            ./x.py clippy library/core
285            ./x.py clippy library/core library/proc_macro")]
286    Clippy {
287        #[arg(long)]
288        fix: bool,
289        #[arg(long, requires = "fix")]
290        allow_dirty: bool,
291        #[arg(long, requires = "fix")]
292        allow_staged: bool,
293        /// clippy lints to allow
294        #[arg(global = true, short = 'A', action = clap::ArgAction::Append, value_name = "LINT")]
295        allow: Vec<String>,
296        /// clippy lints to deny
297        #[arg(global = true, short = 'D', action = clap::ArgAction::Append, value_name = "LINT")]
298        deny: Vec<String>,
299        /// clippy lints to warn on
300        #[arg(global = true, short = 'W', action = clap::ArgAction::Append, value_name = "LINT")]
301        warn: Vec<String>,
302        /// clippy lints to forbid
303        #[arg(global = true, short = 'F', action = clap::ArgAction::Append, value_name = "LINT")]
304        forbid: Vec<String>,
305    },
306    /// Run cargo fix
307    #[command(long_about = "\n
308    Arguments:
309        This subcommand accepts a number of paths to directories to the crates
310        and/or artifacts to run `cargo fix` against. For example:
311            ./x.py fix library/core
312            ./x.py fix library/core library/proc_macro")]
313    Fix,
314    #[command(
315        name = "fmt",
316        long_about = "\n
317    Arguments:
318        This subcommand optionally accepts a `--check` flag which succeeds if
319        formatting is correct and fails if it is not. For example:
320            ./x.py fmt
321            ./x.py fmt --check"
322    )]
323    /// Run rustfmt
324    Format {
325        /// check formatting instead of applying
326        #[arg(long)]
327        check: bool,
328
329        /// apply to all appropriate files, not just those that have been modified
330        #[arg(long)]
331        all: bool,
332    },
333    #[command(aliases = ["d"], long_about = "\n
334    Arguments:
335        This subcommand accepts a number of paths to directories of documentation
336        to build. For example:
337            ./x.py doc src/doc/book
338            ./x.py doc src/doc/nomicon
339            ./x.py doc src/doc/book library/std
340            ./x.py doc library/std --json
341            ./x.py doc library/std --open
342        If no arguments are passed then everything is documented:
343            ./x.py doc
344            ./x.py doc --stage 1")]
345    /// Build documentation
346    Doc {
347        #[arg(long)]
348        /// open the docs in a browser
349        open: bool,
350        #[arg(long)]
351        /// render the documentation in JSON format in addition to the usual HTML format
352        json: bool,
353    },
354    #[command(aliases = ["t"], long_about = "\n
355    Arguments:
356        This subcommand accepts a number of paths to test directories that
357        should be compiled and run. For example:
358            ./x.py test tests/ui
359            ./x.py test library/std --test-args hash_map
360            ./x.py test library/std --stage 0 --all-targets
361            ./x.py test tests/ui --bless
362            ./x.py test tests/ui --compare-mode next-solver
363        Note that `test tests/* --stage N` does NOT depend on `build compiler/rustc --stage N`;
364        just like `build library/std --stage N` it tests the compiler produced by the previous
365        stage.
366        Execute tool tests with a tool name argument:
367            ./x.py test tidy
368        If no arguments are passed then the complete artifacts for that stage are
369        compiled and tested.
370            ./x.py test
371            ./x.py test --stage 1")]
372    /// Build and run some test suites
373    Test {
374        #[arg(long)]
375        /// run all tests regardless of failure
376        no_fail_fast: bool,
377        #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
378        /// extra arguments to be passed for the test tool being used
379        /// (e.g. libtest, compiletest or rustdoc)
380        test_args: Vec<String>,
381        /// extra options to pass the compiler when running compiletest tests
382        #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
383        compiletest_rustc_args: Vec<String>,
384        #[arg(long)]
385        /// Run all test targets (no doc tests)
386        all_targets: bool,
387        #[arg(long)]
388        /// Only run doc tests
389        doc: bool,
390        /// Only run unit and integration tests
391        #[arg(long)]
392        tests: bool,
393        #[arg(long)]
394        /// whether to automatically update stderr/stdout files
395        bless: bool,
396        #[arg(long)]
397        /// comma-separated list of other files types to check (accepts py, py:lint,
398        /// py:fmt, shell, cpp, cpp:fmt, js, js:lint, js:typecheck, spellcheck)
399        ///
400        /// Any argument can be prefixed with "auto:" to only run if
401        /// relevant files are modified (eg. "auto:py").
402        extra_checks: Option<String>,
403        #[arg(long)]
404        /// rerun tests even if the inputs are unchanged
405        force_rerun: bool,
406        #[arg(long)]
407        /// only run tests that result has been changed
408        only_modified: bool,
409        #[arg(long, value_name = "COMPARE MODE")]
410        /// mode describing what file the actual ui output will be compared to
411        compare_mode: Option<String>,
412        #[arg(long, value_name = "check | build | run")]
413        /// force {check,build,run}-pass tests to this mode.
414        pass: Option<String>,
415        #[arg(long, value_name = "auto | always | never")]
416        /// whether to execute run-* tests
417        run: Option<String>,
418        #[arg(long)]
419        /// enable this to generate a Rustfix coverage file, which is saved in
420        /// `/<build_base>/rustfix_missing_coverage.txt`
421        rustfix_coverage: bool,
422        #[arg(long)]
423        /// don't capture stdout/stderr of tests
424        no_capture: bool,
425        #[arg(long)]
426        /// Use a different codegen backend when running tests.
427        test_codegen_backend: Option<CodegenBackendKind>,
428        #[arg(long)]
429        /// Ignore `//@ ignore-backends` directives.
430        bypass_ignore_backends: bool,
431
432        /// Deprecated. Use `--all-targets` or `--tests` instead.
433        #[arg(long)]
434        #[doc(hidden)]
435        no_doc: bool,
436    },
437    /// Build and run some test suites *in Miri*
438    Miri {
439        #[arg(long)]
440        /// run all tests regardless of failure
441        no_fail_fast: bool,
442        #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
443        /// extra arguments to be passed for the test tool being used
444        /// (e.g. libtest, compiletest or rustdoc)
445        test_args: Vec<String>,
446        #[arg(long)]
447        /// Run all test targets (no doc tests)
448        all_targets: bool,
449        #[arg(long)]
450        /// Only run doc tests
451        doc: bool,
452        /// Only run unit and integration tests
453        #[arg(long)]
454        tests: bool,
455
456        /// Deprecated. Use `--all-targets` or `--tests` instead.
457        #[arg(long)]
458        #[doc(hidden)]
459        no_doc: bool,
460    },
461    /// Build and run some benchmarks
462    Bench {
463        #[arg(long, allow_hyphen_values(true))]
464        test_args: Vec<String>,
465    },
466    /// Clean out build directories
467    Clean {
468        #[arg(long)]
469        /// Clean the entire build directory (not used by default)
470        all: bool,
471        #[arg(long, value_name = "N")]
472        /// Clean a specific stage without touching other artifacts. By default, every stage is cleaned if this option is not used.
473        stage: Option<u32>,
474    },
475    /// Build distribution artifacts
476    Dist,
477    /// Install distribution artifacts
478    Install,
479    #[command(aliases = ["r"], long_about = "\n
480    Arguments:
481        This subcommand accepts a number of paths to tools to build and run. For
482        example:
483            ./x.py run src/tools/bump-stage0
484        At least a tool needs to be called.")]
485    /// Run tools contained in this repository
486    Run {
487        /// arguments for the tool
488        #[arg(long, allow_hyphen_values(true))]
489        args: Vec<String>,
490    },
491    /// Set up the environment for development
492    #[command(long_about = format!(
493        "\n
494x.py setup creates a `bootstrap.toml` which changes the defaults for x.py itself,
495as well as setting up a git pre-push hook, VS Code config and toolchain link.
496Arguments:
497    This subcommand accepts a 'profile' to use for builds. For example:
498        ./x.py setup library
499    The profile is optional and you will be prompted interactively if it is not given.
500    The following profiles are available:
501{}
502    To only set up the git hook, editor config or toolchain link, you may use
503        ./x.py setup hook
504        ./x.py setup editor
505        ./x.py setup link", Profile::all_for_help("        ").trim_end()))]
506    Setup {
507        /// Either the profile for `bootstrap.toml` or another setup action.
508        /// May be omitted to set up interactively
509        #[arg(value_name = "<PROFILE>|hook|editor|link")]
510        profile: Option<PathBuf>,
511    },
512    /// Vendor dependencies
513    Vendor {
514        /// Additional `Cargo.toml` to sync and vendor
515        #[arg(long)]
516        sync: Vec<PathBuf>,
517        /// Always include version in subdir name
518        #[arg(long)]
519        versioned_dirs: bool,
520    },
521    /// Perform profiling and benchmarking of the compiler using `rustc-perf`.
522    Perf(PerfArgs),
523}
524
525impl Default for Subcommand {
526    fn default() -> Self {
527        Subcommand::Build { timings: false }
528    }
529}
530
531impl Subcommand {
532    pub fn kind(&self) -> Kind {
533        match self {
534            Subcommand::Bench { .. } => Kind::Bench,
535            Subcommand::Build { .. } => Kind::Build,
536            Subcommand::Check { .. } => Kind::Check,
537            Subcommand::Clippy { .. } => Kind::Clippy,
538            Subcommand::Doc { .. } => Kind::Doc,
539            Subcommand::Fix => Kind::Fix,
540            Subcommand::Format { .. } => Kind::Format,
541            Subcommand::Test { .. } => Kind::Test,
542            Subcommand::Miri { .. } => Kind::Miri,
543            Subcommand::Clean { .. } => Kind::Clean,
544            Subcommand::Dist => Kind::Dist,
545            Subcommand::Install => Kind::Install,
546            Subcommand::Run { .. } => Kind::Run,
547            Subcommand::Setup { .. } => Kind::Setup,
548            Subcommand::Vendor { .. } => Kind::Vendor,
549            Subcommand::Perf { .. } => Kind::Perf,
550        }
551    }
552
553    pub fn compiletest_rustc_args(&self) -> Vec<&str> {
554        match *self {
555            Subcommand::Test { ref compiletest_rustc_args, .. } => {
556                compiletest_rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
557            }
558            _ => vec![],
559        }
560    }
561
562    pub fn fail_fast(&self) -> bool {
563        match *self {
564            Subcommand::Test { no_fail_fast, .. } | Subcommand::Miri { no_fail_fast, .. } => {
565                !no_fail_fast
566            }
567            _ => false,
568        }
569    }
570
571    pub fn test_target(&self) -> TestTarget {
572        match *self {
573            Subcommand::Test { mut all_targets, doc, tests, no_doc, .. }
574            | Subcommand::Miri { mut all_targets, doc, tests, no_doc, .. } => {
575                // for backwards compatibility --no-doc keeps working
576                all_targets = all_targets || no_doc;
577
578                match (all_targets, doc, tests) {
579                    (true, true, _) | (true, _, true) | (_, true, true) => {
580                        panic!("You can only set one of `--all-targets`, `--doc` and `--tests`.")
581                    }
582                    (true, false, false) => TestTarget::AllTargets,
583                    (false, true, false) => TestTarget::DocOnly,
584                    (false, false, true) => TestTarget::Tests,
585                    (false, false, false) => TestTarget::Default,
586                }
587            }
588            _ => TestTarget::Default,
589        }
590    }
591
592    pub fn no_doc(&self) -> bool {
593        match *self {
594            Subcommand::Test { no_doc, .. } | Subcommand::Miri { no_doc, .. } => no_doc,
595            _ => false,
596        }
597    }
598
599    pub fn bless(&self) -> bool {
600        match *self {
601            Subcommand::Test { bless, .. } => bless,
602            _ => false,
603        }
604    }
605
606    pub fn extra_checks(&self) -> Option<&str> {
607        match *self {
608            Subcommand::Test { ref extra_checks, .. } => extra_checks.as_ref().map(String::as_str),
609            _ => None,
610        }
611    }
612
613    pub fn only_modified(&self) -> bool {
614        match *self {
615            Subcommand::Test { only_modified, .. } => only_modified,
616            _ => false,
617        }
618    }
619
620    pub fn force_rerun(&self) -> bool {
621        match *self {
622            Subcommand::Test { force_rerun, .. } => force_rerun,
623            _ => false,
624        }
625    }
626
627    pub fn no_capture(&self) -> bool {
628        match *self {
629            Subcommand::Test { no_capture, .. } => no_capture,
630            _ => false,
631        }
632    }
633
634    pub fn rustfix_coverage(&self) -> bool {
635        match *self {
636            Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
637            _ => false,
638        }
639    }
640
641    pub fn compare_mode(&self) -> Option<&str> {
642        match *self {
643            Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]),
644            _ => None,
645        }
646    }
647
648    pub fn pass(&self) -> Option<&str> {
649        match *self {
650            Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]),
651            _ => None,
652        }
653    }
654
655    pub fn run(&self) -> Option<&str> {
656        match *self {
657            Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]),
658            _ => None,
659        }
660    }
661
662    pub fn open(&self) -> bool {
663        match *self {
664            Subcommand::Doc { open, .. } => open,
665            _ => false,
666        }
667    }
668
669    pub fn json(&self) -> bool {
670        match *self {
671            Subcommand::Doc { json, .. } => json,
672            _ => false,
673        }
674    }
675
676    pub fn timings(&self) -> bool {
677        match *self {
678            Subcommand::Build { timings, .. } | Subcommand::Check { timings, .. } => timings,
679            _ => false,
680        }
681    }
682
683    pub fn vendor_versioned_dirs(&self) -> bool {
684        match *self {
685            Subcommand::Vendor { versioned_dirs, .. } => versioned_dirs,
686            _ => false,
687        }
688    }
689
690    pub fn vendor_sync_args(&self) -> Vec<PathBuf> {
691        match self {
692            Subcommand::Vendor { sync, .. } => sync.clone(),
693            _ => vec![],
694        }
695    }
696
697    pub fn test_codegen_backend(&self) -> Option<&CodegenBackendKind> {
698        match self {
699            Subcommand::Test { test_codegen_backend, .. } => test_codegen_backend.as_ref(),
700            _ => None,
701        }
702    }
703
704    pub fn bypass_ignore_backends(&self) -> bool {
705        match self {
706            Subcommand::Test { bypass_ignore_backends, .. } => *bypass_ignore_backends,
707            _ => false,
708        }
709    }
710}
711
712/// Returns the shell completion for a given shell, if the result differs from the current
713/// content of `path`. If `path` does not exist, always returns `Some`.
714pub fn get_completion(shell: &dyn Generator, path: &Path) -> Option<String> {
715    let mut cmd = Flags::command();
716    let current = if !path.exists() {
717        String::new()
718    } else {
719        std::fs::read_to_string(path).unwrap_or_else(|_| {
720            eprintln!("couldn't read {}", path.display());
721            crate::exit!(1)
722        })
723    };
724    let mut buf = Vec::new();
725    let (bin_name, _) = path
726        .file_name()
727        .expect("path should be a regular file")
728        .to_str()
729        .expect("file name should be UTF-8")
730        .rsplit_once('.')
731        .expect("file name should have an extension");
732
733    // We sort of replicate `clap_complete::generate` here, because we want to call it with
734    // `&dyn Generator`, but that function requires `G: Generator` instead.
735    cmd.set_bin_name(bin_name);
736    cmd.build();
737    shell.generate(&cmd, &mut buf);
738    if buf == current.as_bytes() {
739        return None;
740    }
741    Some(String::from_utf8(buf).expect("completion script should be UTF-8"))
742}
743
744/// Return the top level help of the bootstrap.
745pub fn top_level_help() -> String {
746    let mut cmd = Flags::command();
747    cmd.render_help().to_string()
748}