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<String>,
157 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
159 pub rust_profile_use: Option<String>,
160 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
162 pub llvm_profile_use: Option<String>,
163 #[arg(global = true, long)]
169 pub llvm_profile_generate: bool,
170 #[arg(global = true, long)]
172 pub enable_bolt_settings: bool,
173 #[arg(global = true, long)]
175 pub skip_stage0_validation: bool,
176 #[arg(global = true, long)]
178 pub reproducible_artifact: Vec<String>,
179 #[arg(global = true)]
180 pub paths: Vec<PathBuf>,
182 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "section.option=value")]
184 pub set: Vec<String>,
185 #[arg(global = true, last(true), value_name = "ARGS")]
187 pub free_args: Vec<String>,
188 #[arg(global = true, long, value_name = "bool")]
190 pub ci: Option<bool>,
191 #[arg(global = true, long)]
195 pub skip_std_check_if_no_download_rustc: bool,
196}
197
198impl Flags {
199 pub fn try_parse_verbose_help(args: &[String]) -> bool {
202 #[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 Build {
263 #[arg(long)]
264 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 Check {
275 #[arg(long)]
276 all_targets: bool,
278 #[arg(long)]
279 timings: bool,
281 },
282 #[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 #[arg(global = true, short = 'A', action = clap::ArgAction::Append, value_name = "LINT")]
298 allow: Vec<String>,
299 #[arg(global = true, short = 'D', action = clap::ArgAction::Append, value_name = "LINT")]
301 deny: Vec<String>,
302 #[arg(global = true, short = 'W', action = clap::ArgAction::Append, value_name = "LINT")]
304 warn: Vec<String>,
305 #[arg(global = true, short = 'F', action = clap::ArgAction::Append, value_name = "LINT")]
307 forbid: Vec<String>,
308 },
309 #[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 Format {
328 #[arg(long)]
330 check: bool,
331
332 #[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 Doc {
350 #[arg(long)]
351 open: bool,
353 #[arg(long)]
354 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 Test {
377 #[arg(long)]
378 no_fail_fast: bool,
380 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
381 test_args: Vec<String>,
384 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
386 compiletest_rustc_args: Vec<String>,
387 #[arg(long)]
388 all_targets: bool,
390 #[arg(long)]
391 doc: bool,
393 #[arg(long)]
395 tests: bool,
396 #[arg(long)]
397 bless: bool,
399 #[arg(long)]
400 extra_checks: Option<String>,
406 #[arg(long)]
407 force_rerun: bool,
409 #[arg(long)]
410 only_modified: bool,
412 #[arg(long, value_name = "COMPARE MODE")]
413 compare_mode: Option<String>,
415 #[arg(long, value_name = "check | build | run")]
416 pass: Option<String>,
418 #[arg(long, value_name = "auto | always | never")]
419 run: Option<String>,
421 #[arg(long)]
422 rustfix_coverage: bool,
425 #[arg(long)]
426 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 verbose_run_make_subprocess_output: bool,
432 #[arg(long)]
433 test_codegen_backend: Option<CodegenBackendKind>,
435 #[arg(long)]
436 bypass_ignore_backends: bool,
438
439 #[arg(long)]
441 #[doc(hidden)]
442 no_doc: bool,
443
444 #[arg(long)]
448 record: bool,
449 #[arg(long)]
451 rerun: bool,
452 },
453 Miri {
455 #[arg(long)]
456 no_fail_fast: bool,
458 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
459 test_args: Vec<String>,
462 #[arg(long)]
463 all_targets: bool,
465 #[arg(long)]
466 doc: bool,
468 #[arg(long)]
470 tests: bool,
471
472 #[arg(long)]
474 #[doc(hidden)]
475 no_doc: bool,
476 },
477 Bench {
479 #[arg(long, allow_hyphen_values(true))]
480 test_args: Vec<String>,
481 },
482 Clean {
484 #[arg(long)]
485 all: bool,
487 #[arg(long, value_name = "N")]
488 stage: Option<u32>,
490 },
491 Dist,
493 Install,
495 #[command(visible_aliases = ["r"], long_about = "\n
496 Arguments:
497 This subcommand accepts a number of paths to tools to build and run. For
498 example:
499 ./x.py run src/tools/bump-stage0
500 At least a tool needs to be called.")]
501 Run {
503 #[arg(long, allow_hyphen_values(true))]
505 args: Vec<String>,
506 },
507 #[command(long_about = format!(
509 "\n
510x.py setup creates a `bootstrap.toml` which changes the defaults for x.py itself,
511as well as setting up a git pre-push hook, VS Code config and toolchain link.
512Arguments:
513 This subcommand accepts a 'profile' to use for builds. For example:
514 ./x.py setup library
515 The profile is optional and you will be prompted interactively if it is not given.
516 The following profiles are available:
517{}
518 To only set up the git hook, editor config or toolchain link, you may use
519 ./x.py setup hook
520 ./x.py setup editor
521 ./x.py setup link", Profile::all_for_help(" ").trim_end()))]
522 Setup {
523 #[arg(value_name = "<PROFILE>|hook|editor|link")]
526 profile: Option<PathBuf>,
527 },
528 Vendor {
530 #[arg(long)]
532 sync: Vec<PathBuf>,
533 #[arg(long)]
535 versioned_dirs: bool,
536 },
537 Perf(PerfArgs),
539}
540
541impl Default for Subcommand {
542 fn default() -> Self {
543 Subcommand::Build { timings: false }
544 }
545}
546
547impl Subcommand {
548 pub fn kind(&self) -> Kind {
549 match self {
550 Subcommand::Bench { .. } => Kind::Bench,
551 Subcommand::Build { .. } => Kind::Build,
552 Subcommand::Check { .. } => Kind::Check,
553 Subcommand::Clippy { .. } => Kind::Clippy,
554 Subcommand::Doc { .. } => Kind::Doc,
555 Subcommand::Fix => Kind::Fix,
556 Subcommand::Format { .. } => Kind::Format,
557 Subcommand::Test { .. } => Kind::Test,
558 Subcommand::Miri { .. } => Kind::Miri,
559 Subcommand::Clean { .. } => Kind::Clean,
560 Subcommand::Dist => Kind::Dist,
561 Subcommand::Install => Kind::Install,
562 Subcommand::Run { .. } => Kind::Run,
563 Subcommand::Setup { .. } => Kind::Setup,
564 Subcommand::Vendor { .. } => Kind::Vendor,
565 Subcommand::Perf { .. } => Kind::Perf,
566 }
567 }
568
569 pub fn compiletest_rustc_args(&self) -> Vec<&str> {
570 match *self {
571 Subcommand::Test { ref compiletest_rustc_args, .. } => {
572 compiletest_rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
573 }
574 _ => vec![],
575 }
576 }
577
578 pub fn fail_fast(&self) -> bool {
579 match *self {
580 Subcommand::Test { no_fail_fast, .. } | Subcommand::Miri { no_fail_fast, .. } => {
581 !no_fail_fast
582 }
583 _ => false,
584 }
585 }
586
587 pub fn test_target(&self) -> TestTarget {
588 match *self {
589 Subcommand::Test { mut all_targets, doc, tests, no_doc, .. }
590 | Subcommand::Miri { mut all_targets, doc, tests, no_doc, .. } => {
591 all_targets = all_targets || no_doc;
593
594 match (all_targets, doc, tests) {
595 (true, true, _) | (true, _, true) | (_, true, true) => {
596 panic!("You can only set one of `--all-targets`, `--doc` and `--tests`.")
597 }
598 (true, false, false) => TestTarget::AllTargets,
599 (false, true, false) => TestTarget::DocOnly,
600 (false, false, true) => TestTarget::Tests,
601 (false, false, false) => TestTarget::Default,
602 }
603 }
604 _ => TestTarget::Default,
605 }
606 }
607
608 pub fn no_doc(&self) -> bool {
609 match *self {
610 Subcommand::Test { no_doc, .. } | Subcommand::Miri { no_doc, .. } => no_doc,
611 _ => false,
612 }
613 }
614
615 pub fn bless(&self) -> bool {
616 match *self {
617 Subcommand::Test { bless, .. } => bless,
618 _ => false,
619 }
620 }
621
622 pub fn extra_checks(&self) -> Option<&str> {
623 match *self {
624 Subcommand::Test { ref extra_checks, .. } => extra_checks.as_ref().map(String::as_str),
625 _ => None,
626 }
627 }
628
629 pub fn only_modified(&self) -> bool {
630 match *self {
631 Subcommand::Test { only_modified, .. } => only_modified,
632 _ => false,
633 }
634 }
635
636 pub fn force_rerun(&self) -> bool {
637 match *self {
638 Subcommand::Test { force_rerun, .. } => force_rerun,
639 _ => false,
640 }
641 }
642
643 pub fn no_capture(&self) -> bool {
644 match *self {
645 Subcommand::Test { no_capture, .. } => no_capture,
646 _ => false,
647 }
648 }
649
650 pub fn verbose_run_make_subprocess_output(&self) -> bool {
651 match *self {
652 Subcommand::Test { verbose_run_make_subprocess_output, .. } => {
653 verbose_run_make_subprocess_output
654 }
655 _ => true,
656 }
657 }
658
659 pub fn rustfix_coverage(&self) -> bool {
660 match *self {
661 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
662 _ => false,
663 }
664 }
665
666 pub fn compare_mode(&self) -> Option<&str> {
667 match *self {
668 Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]),
669 _ => None,
670 }
671 }
672
673 pub fn pass(&self) -> Option<&str> {
674 match *self {
675 Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]),
676 _ => None,
677 }
678 }
679
680 pub fn run(&self) -> Option<&str> {
681 match *self {
682 Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]),
683 _ => None,
684 }
685 }
686
687 pub fn open(&self) -> bool {
688 match *self {
689 Subcommand::Doc { open, .. } => open,
690 _ => false,
691 }
692 }
693
694 pub fn json(&self) -> bool {
695 match *self {
696 Subcommand::Doc { json, .. } => json,
697 _ => false,
698 }
699 }
700
701 pub fn timings(&self) -> bool {
702 match *self {
703 Subcommand::Build { timings, .. } | Subcommand::Check { timings, .. } => timings,
704 _ => false,
705 }
706 }
707
708 pub fn vendor_versioned_dirs(&self) -> bool {
709 match *self {
710 Subcommand::Vendor { versioned_dirs, .. } => versioned_dirs,
711 _ => false,
712 }
713 }
714
715 pub fn vendor_sync_args(&self) -> Vec<PathBuf> {
716 match self {
717 Subcommand::Vendor { sync, .. } => sync.clone(),
718 _ => vec![],
719 }
720 }
721
722 pub fn test_codegen_backend(&self) -> Option<&CodegenBackendKind> {
723 match self {
724 Subcommand::Test { test_codegen_backend, .. } => test_codegen_backend.as_ref(),
725 _ => None,
726 }
727 }
728
729 pub fn bypass_ignore_backends(&self) -> bool {
730 match self {
731 Subcommand::Test { bypass_ignore_backends, .. } => *bypass_ignore_backends,
732 _ => false,
733 }
734 }
735
736 pub fn record(&self) -> bool {
737 match self {
738 Subcommand::Test { record, .. } => *record,
739 _ => false,
740 }
741 }
742
743 pub fn rerun(&self) -> bool {
744 match self {
745 Subcommand::Test { rerun, .. } => *rerun,
746 _ => false,
747 }
748 }
749}
750
751pub fn get_completion(shell: &dyn Generator, path: &Path) -> Option<String> {
754 let mut cmd = Flags::command();
755 let current = if !path.exists() {
756 String::new()
757 } else {
758 std::fs::read_to_string(path).unwrap_or_else(|_| {
759 eprintln!("couldn't read {}", path.display());
760 crate::exit!(1)
761 })
762 };
763 let mut buf = Vec::new();
764 let (bin_name, _) = path
765 .file_name()
766 .expect("path should be a regular file")
767 .to_str()
768 .expect("file name should be UTF-8")
769 .rsplit_once('.')
770 .expect("file name should have an extension");
771
772 cmd.set_bin_name(bin_name);
775 cmd.build();
776 shell.generate(&cmd, &mut buf);
777 if buf == current.as_bytes() {
778 return None;
779 }
780 Some(String::from_utf8(buf).expect("completion script should be UTF-8"))
781}
782
783pub fn top_level_help() -> String {
785 let mut cmd = Flags::command();
786 cmd.render_help().to_string()
787}