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