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