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