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