1use 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#[derive(Copy, Clone, Default, Debug, ValueEnum)]
30pub enum Warnings {
31 Deny,
32 Warn,
33 #[default]
34 Default,
35}
36
37#[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 pub verbose: u8, #[arg(global = true, short, long, conflicts_with = "verbose")]
53 pub quiet: bool,
55 #[arg(global = true, short, long)]
56 pub incremental: bool,
58 #[arg(global = true, long, value_hint = clap::ValueHint::FilePath, value_name = "FILE")]
59 pub config: Option<PathBuf>,
61 #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")]
62 pub build_dir: Option<PathBuf>,
64
65 #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "BUILD")]
66 pub build: Option<String>,
68
69 #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "HOST", value_parser = target_selection_list)]
70 pub host: Option<TargetSelectionList>,
72
73 #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "TARGET", value_parser = target_selection_list)]
74 pub target: Option<TargetSelectionList>,
76
77 #[arg(global = true, long, value_name = "PATH")]
78 pub exclude: Vec<PathBuf>, #[arg(global = true, long, value_name = "PATH")]
81 pub skip: Vec<PathBuf>,
83 #[arg(global = true, long)]
84 pub include_default_paths: bool,
86
87 #[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 pub on_fail: Option<String>,
94 #[arg(global = true, long)]
95 pub dry_run: bool,
97 #[arg(global = true, long)]
99 pub dump_bootstrap_shims: bool,
100 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
101 pub stage: Option<u32>,
104
105 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
106 pub keep_stage: Vec<u32>,
109 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
110 pub keep_stage_std: Vec<u32>,
113 #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")]
114 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 pub jobs: Option<u32>,
126 #[arg(global = true, long)]
129 #[arg(value_enum, default_value_t=Warnings::Default, value_name = "deny|warn")]
130 pub warnings: Warnings,
134
135 #[arg(global = true, long)]
136 pub json_output: bool,
138 #[arg(global = true, long)]
139 pub compile_time_deps: bool,
141
142 #[arg(global = true, long, value_name = "STYLE")]
143 #[arg(value_enum, default_value_t = Color::Auto)]
144 pub color: Color,
146
147 #[arg(global = true, long)]
148 pub bypass_bootstrap_lock: bool,
153
154 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
156 pub rust_profile_generate: Option<PathBuf>,
158 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
161 pub rust_profile_use: Option<PathBuf>,
162 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
165 pub llvm_profile_use: Option<PathBuf>,
166 #[arg(global = true, long)]
173 pub llvm_profile_generate: bool,
174 #[arg(global = true, long)]
176 pub enable_bolt_settings: bool,
177 #[arg(global = true, long)]
179 pub skip_stage0_validation: bool,
180 #[arg(global = true, long)]
182 pub reproducible_artifact: Vec<String>,
183 #[arg(global = true)]
184 pub paths: Vec<PathBuf>,
186 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "section.option=value")]
188 pub set: Vec<String>,
189 #[arg(global = true, last(true), value_name = "ARGS")]
191 pub free_args: Vec<String>,
192 #[arg(global = true, long, value_name = "bool")]
194 pub ci: Option<bool>,
195 #[arg(global = true, long)]
199 pub skip_std_check_if_no_download_rustc: bool,
200}
201
202impl Flags {
203 pub fn try_parse_verbose_help(args: &[String]) -> bool {
206 #[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 Build {
267 #[arg(long)]
268 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 Check {
279 #[arg(long)]
280 all_targets: bool,
282 #[arg(long)]
283 timings: bool,
285 },
286 #[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 #[arg(global = true, short = 'A', action = clap::ArgAction::Append, value_name = "LINT")]
302 allow: Vec<String>,
303 #[arg(global = true, short = 'D', action = clap::ArgAction::Append, value_name = "LINT")]
305 deny: Vec<String>,
306 #[arg(global = true, short = 'W', action = clap::ArgAction::Append, value_name = "LINT")]
308 warn: Vec<String>,
309 #[arg(global = true, short = 'F', action = clap::ArgAction::Append, value_name = "LINT")]
311 forbid: Vec<String>,
312 },
313 #[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 Format {
332 #[arg(long)]
334 check: bool,
335
336 #[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 Doc {
354 #[arg(long)]
355 open: bool,
357 #[arg(long)]
358 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 Test {
381 #[arg(long)]
382 no_fail_fast: bool,
384 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
385 test_args: Vec<String>,
388 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
390 compiletest_rustc_args: Vec<String>,
391 #[arg(long)]
392 all_targets: bool,
394 #[arg(long)]
395 doc: bool,
397 #[arg(long)]
399 tests: bool,
400 #[arg(long)]
401 bless: bool,
403 #[arg(long)]
404 extra_checks: Option<String>,
410 #[arg(long)]
411 force_rerun: bool,
413 #[arg(long)]
414 only_modified: bool,
416 #[arg(long, value_name = "COMPARE MODE")]
417 compare_mode: Option<String>,
419 #[arg(long, value_name = "check | build | run")]
420 pass: Option<String>,
422 #[arg(long, value_name = "auto | always | never")]
423 run: Option<String>,
425 #[arg(long)]
426 rustfix_coverage: bool,
429 #[arg(long)]
430 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 verbose_run_make_subprocess_output: bool,
436 #[arg(long)]
437 test_codegen_backend: Option<CodegenBackendKind>,
439 #[arg(long)]
440 bypass_ignore_backends: bool,
442
443 #[arg(long)]
445 #[doc(hidden)]
446 no_doc: bool,
447
448 #[arg(long)]
452 record: bool,
453 #[arg(long)]
455 rerun: bool,
456 },
457 Miri {
459 #[arg(long)]
460 no_fail_fast: bool,
462 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
463 test_args: Vec<String>,
466 #[arg(long)]
467 all_targets: bool,
469 #[arg(long)]
470 doc: bool,
472 #[arg(long)]
474 tests: bool,
475
476 #[arg(long)]
478 #[doc(hidden)]
479 no_doc: bool,
480 },
481 Bench {
483 #[arg(long, allow_hyphen_values(true))]
484 test_args: Vec<String>,
485 },
486 Clean {
488 #[arg(long)]
489 all: bool,
491 #[arg(long, value_name = "N")]
492 stage: Option<u32>,
494 },
495 Dist,
497 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 {
507 #[arg(long, allow_hyphen_values(true))]
509 args: Vec<String>,
510 },
511 #[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 #[arg(value_name = "<PROFILE>|hook|editor|link")]
530 profile: Option<PathBuf>,
531 },
532 Vendor {
534 #[arg(long)]
536 sync: Vec<PathBuf>,
537 #[arg(long)]
539 versioned_dirs: bool,
540 },
541 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 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
755pub 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 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
787pub fn top_level_help() -> String {
789 let mut cmd = Flags::command();
790 cmd.render_help().to_string()
791}