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::utils::helpers;
19use crate::{Build, CodegenBackendKind, TestTarget};
20
21#[derive(Copy, Clone, Default, Debug, ValueEnum)]
22pub enum Color {
23 Always,
24 Never,
25 #[default]
26 Auto,
27}
28
29#[derive(Copy, Clone, Default, Debug, ValueEnum)]
31pub enum Warnings {
32 Deny,
33 Warn,
34 #[default]
35 Default,
36}
37
38#[derive(Debug, Parser)]
40#[command(
41 override_usage = "x.py <subcommand> [options] [<paths>...]",
42 disable_help_subcommand(true),
43 about = "",
44 next_line_help(false)
45)]
46pub struct Flags {
47 #[command(subcommand)]
48 pub cmd: Subcommand,
49
50 #[arg(global = true, short, long, action = clap::ArgAction::Count, conflicts_with = "quiet")]
51 pub verbose: u8, #[arg(global = true, short, long, conflicts_with = "verbose")]
54 pub quiet: bool,
56 #[arg(global = true, short, long)]
57 pub incremental: bool,
59 #[arg(global = true, long, value_hint = clap::ValueHint::FilePath, value_name = "FILE")]
60 pub config: Option<PathBuf>,
62 #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")]
63 pub build_dir: Option<PathBuf>,
65
66 #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "BUILD")]
67 pub build: Option<String>,
69
70 #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "HOST", value_parser = target_selection_list)]
71 pub host: Option<TargetSelectionList>,
73
74 #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "TARGET", value_parser = target_selection_list)]
75 pub target: Option<TargetSelectionList>,
77
78 #[arg(global = true, long, value_name = "PATH")]
79 pub exclude: Vec<PathBuf>, #[arg(global = true, long, value_name = "PATH")]
82 pub skip: Vec<PathBuf>,
84 #[arg(global = true, long)]
85 pub include_default_paths: bool,
87
88 #[arg(global = true, value_hint = clap::ValueHint::Other, long)]
90 pub rustc_error_format: Option<String>,
91
92 #[arg(global = true, long, value_hint = clap::ValueHint::CommandString, value_name = "CMD")]
93 pub on_fail: Option<String>,
95 #[arg(global = true, long)]
96 pub dry_run: bool,
98 #[arg(global = true, long)]
100 pub dump_bootstrap_shims: bool,
101 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
102 pub stage: Option<u32>,
105
106 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
107 pub keep_stage: Vec<u32>,
110 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
111 pub keep_stage_std: Vec<u32>,
114 #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")]
115 pub src: Option<PathBuf>,
117
118 #[arg(
119 global = true,
120 short,
121 long,
122 value_hint = clap::ValueHint::Other,
123 value_name = "JOBS"
124 )]
125 pub jobs: Option<u32>,
127 #[arg(global = true, long)]
130 #[arg(value_enum, default_value_t=Warnings::Default, value_name = "deny|warn")]
131 pub warnings: Warnings,
135
136 #[arg(global = true, long)]
137 pub json_output: bool,
139 #[arg(global = true, long)]
140 pub compile_time_deps: bool,
142
143 #[arg(global = true, long, value_name = "STYLE")]
144 #[arg(value_enum, default_value_t = Color::Auto)]
145 pub color: Color,
147
148 #[arg(global = true, long)]
149 pub bypass_bootstrap_lock: bool,
154
155 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
157 pub rust_profile_generate: Option<PathBuf>,
159 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
162 pub rust_profile_use: Option<PathBuf>,
163 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
166 pub llvm_profile_use: Option<PathBuf>,
167 #[arg(global = true, long)]
174 pub llvm_profile_generate: bool,
175 #[arg(global = true, long)]
177 pub enable_bolt_settings: bool,
178 #[arg(global = true, long)]
180 pub skip_stage0_validation: bool,
181 #[arg(global = true, long)]
183 pub reproducible_artifact: Vec<String>,
184 #[arg(global = true)]
185 pub paths: Vec<PathBuf>,
187 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "section.option=value")]
189 pub set: Vec<String>,
190 #[arg(global = true, last(true), value_name = "ARGS")]
192 pub free_args: Vec<String>,
193 #[arg(global = true, long, value_name = "bool")]
195 pub ci: Option<bool>,
196 #[arg(global = true, long)]
200 pub skip_std_check_if_no_download_rustc: bool,
201}
202
203impl Flags {
204 pub fn try_parse_verbose_help(args: &[String]) -> bool {
207 #[derive(Parser)]
209 #[command(disable_help_flag(true))]
210 struct HelpVerboseOnly {
211 #[arg(short, long)]
212 help: bool,
213 #[arg(global = true, short, long, action = clap::ArgAction::Count)]
214 pub verbose: u8,
215 #[arg(value_enum)]
216 cmd: Kind,
217 }
218 if let Ok(HelpVerboseOnly { help: true, verbose: 1.., cmd: subcommand }) =
219 HelpVerboseOnly::try_parse_from(normalize_args(args))
220 {
221 println!("NOTE: updating submodules before printing available paths");
222 let flags = Self::parse(&[String::from("build")]);
223 let config = Config::parse(flags);
224 let build = Build::new(config);
225 let paths = Builder::get_help(&build, subcommand);
226 if let Some(s) = paths {
227 println!("{s}");
228 } else {
229 panic!("No paths available for subcommand `{}`", subcommand.as_str());
230 }
231 true
232 } else {
233 false
234 }
235 }
236
237 #[cfg_attr(
238 feature = "tracing",
239 instrument(level = "trace", name = "Flags::parse", skip_all, fields(args = ?args))
240 )]
241 pub fn parse(args: &[String]) -> Self {
242 Flags::parse_from(normalize_args(args))
243 }
244}
245
246fn normalize_args(args: &[String]) -> Vec<String> {
247 let first = String::from("x.py");
248 let it = std::iter::once(first).chain(args.iter().cloned());
249 it.collect()
250}
251
252#[derive(Debug, Clone, clap::Subcommand)]
253pub enum Subcommand {
254 #[command(visible_aliases = ["b"], long_about = "\n
255 Arguments:
256 This subcommand accepts a number of paths to directories to the crates
257 and/or artifacts to compile. For example, for a quick build of a usable
258 compiler:
259 ./x.py build --stage 1 library/std
260 This will build a compiler and standard library from the local source code.
261 Once this is done, build/$ARCH/stage1 contains a usable compiler.
262 If no arguments are passed then the default artifacts for that stage are
263 compiled. For example:
264 ./x.py build --stage 0
265 ./x.py build ")]
266 Build {
268 #[arg(long)]
269 timings: bool,
271 },
272 #[command(visible_aliases = ["c"], long_about = "\n
273 Arguments:
274 This subcommand accepts a number of paths to directories to the crates
275 and/or artifacts to compile. For example:
276 ./x.py check library/std
277 If no arguments are passed then many artifacts are checked.")]
278 Check {
280 #[arg(long)]
281 all_targets: bool,
283 #[arg(long)]
284 timings: bool,
286 },
287 #[command(long_about = "\n
289 Arguments:
290 This subcommand accepts a number of paths to directories to the crates
291 and/or artifacts to run clippy against. For example:
292 ./x.py clippy library/core
293 ./x.py clippy library/core library/proc_macro")]
294 Clippy {
295 #[arg(long)]
296 fix: bool,
297 #[arg(long, requires = "fix")]
298 allow_dirty: bool,
299 #[arg(long, requires = "fix")]
300 allow_staged: bool,
301 #[arg(global = true, short = 'A', action = clap::ArgAction::Append, value_name = "LINT")]
303 allow: Vec<String>,
304 #[arg(global = true, short = 'D', action = clap::ArgAction::Append, value_name = "LINT")]
306 deny: Vec<String>,
307 #[arg(global = true, short = 'W', action = clap::ArgAction::Append, value_name = "LINT")]
309 warn: Vec<String>,
310 #[arg(global = true, short = 'F', action = clap::ArgAction::Append, value_name = "LINT")]
312 forbid: Vec<String>,
313 },
314 #[command(long_about = "\n
316 Arguments:
317 This subcommand accepts a number of paths to directories to the crates
318 and/or artifacts to run `cargo fix` against. For example:
319 ./x.py fix library/core
320 ./x.py fix library/core library/proc_macro")]
321 Fix,
322 #[command(
323 name = "fmt",
324 long_about = "\n
325 Arguments:
326 This subcommand optionally accepts a `--check` flag which succeeds if
327 formatting is correct and fails if it is not. For example:
328 ./x.py fmt
329 ./x.py fmt --check"
330 )]
331 Format {
333 #[arg(long)]
335 check: bool,
336
337 #[arg(long)]
339 all: bool,
340 },
341 #[command(visible_aliases = ["d"], long_about = "\n
342 Arguments:
343 This subcommand accepts a number of paths to directories of documentation
344 to build. For example:
345 ./x.py doc src/doc/book
346 ./x.py doc src/doc/nomicon
347 ./x.py doc src/doc/book library/std
348 ./x.py doc library/std --json
349 ./x.py doc library/std --open
350 If no arguments are passed then everything is documented:
351 ./x.py doc
352 ./x.py doc --stage 1")]
353 Doc {
355 #[arg(long)]
356 open: bool,
358 #[arg(long)]
359 json: bool,
361 },
362 #[command(visible_aliases = ["t"], long_about = "\n
363 Arguments:
364 This subcommand accepts a number of paths to test directories that
365 should be compiled and run. For example:
366 ./x.py test tests/ui
367 ./x.py test library/std --test-args hash_map
368 ./x.py test library/std --stage 0 --all-targets
369 ./x.py test tests/ui --bless
370 ./x.py test tests/ui --compare-mode next-solver
371 Note that `test tests/* --stage N` does NOT depend on `build compiler/rustc --stage N`;
372 just like `build library/std --stage N` it tests the compiler produced by the previous
373 stage.
374 Execute tool tests with a tool name argument:
375 ./x.py test tidy
376 If no arguments are passed then the complete artifacts for that stage are
377 compiled and tested.
378 ./x.py test
379 ./x.py test --stage 1")]
380 Test {
382 #[arg(long)]
383 no_fail_fast: bool,
385 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
386 test_args: Vec<String>,
389 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
391 compiletest_rustc_args: Vec<String>,
392 #[arg(long)]
393 all_targets: bool,
395 #[arg(long)]
396 doc: bool,
398 #[arg(long)]
400 tests: bool,
401 #[arg(long)]
402 bless: bool,
404 #[arg(long)]
405 extra_checks: Option<String>,
411 #[arg(long)]
412 force_rerun: bool,
414 #[arg(long)]
415 only_modified: bool,
417 #[arg(long, value_name = "COMPARE MODE")]
418 compare_mode: Option<String>,
420 #[arg(long, value_name = "check | build | run")]
421 pass: Option<String>,
423 #[arg(long, value_name = "auto | always | never")]
424 run: Option<String>,
426 #[arg(long)]
427 rustfix_coverage: bool,
430 #[arg(long)]
431 no_capture: bool,
433 #[arg(long, default_value_t = true, action = clap::ArgAction::Set, default_missing_value = "true", num_args = 0..=1, require_equals = true)]
434 verbose_run_make_subprocess_output: bool,
437 #[arg(long)]
438 test_codegen_backend: Option<CodegenBackendKind>,
440 #[arg(long)]
441 bypass_ignore_backends: bool,
443
444 #[arg(long)]
446 #[doc(hidden)]
447 no_doc: bool,
448
449 #[arg(long)]
453 record: bool,
454 #[arg(long)]
456 rerun: bool,
457 },
458 Miri {
460 #[arg(long)]
461 no_fail_fast: bool,
463 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
464 test_args: Vec<String>,
467 #[arg(long)]
468 all_targets: bool,
470 #[arg(long)]
471 doc: bool,
473 #[arg(long)]
475 tests: bool,
476
477 #[arg(long)]
479 #[doc(hidden)]
480 no_doc: bool,
481 },
482 Bench {
484 #[arg(long, allow_hyphen_values(true))]
485 test_args: Vec<String>,
486 },
487 Clean {
489 #[arg(long)]
490 all: bool,
492 #[arg(long, value_name = "N")]
493 stage: Option<u32>,
495 },
496 Dist,
498 Install,
500 #[command(visible_aliases = ["r"], long_about = "\n
501 Arguments:
502 This subcommand accepts a number of paths to tools to build and run. For
503 example:
504 ./x.py run src/tools/bump-stage0
505 At least a tool needs to be called.")]
506 Run {
508 #[arg(long, allow_hyphen_values(true))]
510 args: Vec<String>,
511 },
512 #[command(long_about = format!(
514 "\n
515x.py setup creates a `bootstrap.toml` which changes the defaults for x.py itself,
516as well as setting up a git pre-push hook, VS Code config and toolchain link.
517Arguments:
518 This subcommand accepts a 'profile' to use for builds. For example:
519 ./x.py setup library
520 The profile is optional and you will be prompted interactively if it is not given.
521 The following profiles are available:
522{}
523 To only set up the git hook, editor config or toolchain link, you may use
524 ./x.py setup hook
525 ./x.py setup editor
526 ./x.py setup link", Profile::all_for_help(" ").trim_end()))]
527 Setup {
528 #[arg(value_name = "<PROFILE>|hook|editor|link")]
531 profile: Option<PathBuf>,
532 },
533 Vendor {
535 #[arg(long)]
537 sync: Vec<PathBuf>,
538 #[arg(long)]
540 versioned_dirs: bool,
541 },
542 Perf(PerfArgs),
544}
545
546impl Default for Subcommand {
547 fn default() -> Self {
548 Subcommand::Build { timings: false }
549 }
550}
551
552impl Subcommand {
553 pub fn compiletest_rustc_args(&self) -> Vec<&str> {
554 match *self {
555 Subcommand::Test { ref compiletest_rustc_args, .. } => {
556 compiletest_rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
557 }
558 _ => vec![],
559 }
560 }
561
562 pub fn fail_fast(&self) -> bool {
563 match *self {
564 Subcommand::Test { no_fail_fast, .. } | Subcommand::Miri { no_fail_fast, .. } => {
565 !no_fail_fast
566 }
567 _ => false,
568 }
569 }
570
571 pub fn test_target(&self) -> TestTarget {
572 match *self {
573 Subcommand::Test { mut all_targets, doc, tests, no_doc, .. }
574 | Subcommand::Miri { mut all_targets, doc, tests, no_doc, .. } => {
575 all_targets = all_targets || no_doc;
577
578 match (all_targets, doc, tests) {
579 (true, true, _) | (true, _, true) | (_, true, true) => {
580 panic!("You can only set one of `--all-targets`, `--doc` and `--tests`.")
581 }
582 (true, false, false) => TestTarget::AllTargets,
583 (false, true, false) => TestTarget::DocOnly,
584 (false, false, true) => TestTarget::Tests,
585 (false, false, false) => TestTarget::Default,
586 }
587 }
588 _ => TestTarget::Default,
589 }
590 }
591
592 pub fn no_doc(&self) -> bool {
593 match *self {
594 Subcommand::Test { no_doc, .. } | Subcommand::Miri { no_doc, .. } => no_doc,
595 _ => false,
596 }
597 }
598
599 pub fn bless(&self) -> bool {
600 match *self {
601 Subcommand::Test { bless, .. } => bless,
602 _ => false,
603 }
604 }
605
606 pub fn extra_checks(&self) -> Option<&str> {
607 match *self {
608 Subcommand::Test { ref extra_checks, .. } => extra_checks.as_ref().map(String::as_str),
609 _ => None,
610 }
611 }
612
613 pub fn only_modified(&self) -> bool {
614 match *self {
615 Subcommand::Test { only_modified, .. } => only_modified,
616 _ => false,
617 }
618 }
619
620 pub fn force_rerun(&self) -> bool {
621 match *self {
622 Subcommand::Test { force_rerun, .. } => force_rerun,
623 _ => false,
624 }
625 }
626
627 pub fn no_capture(&self) -> bool {
628 match *self {
629 Subcommand::Test { no_capture, .. } => no_capture,
630 _ => false,
631 }
632 }
633
634 pub fn verbose_run_make_subprocess_output(&self) -> bool {
635 match *self {
636 Subcommand::Test { verbose_run_make_subprocess_output, .. } => {
637 verbose_run_make_subprocess_output
638 }
639 _ => true,
640 }
641 }
642
643 pub fn rustfix_coverage(&self) -> bool {
644 match *self {
645 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
646 _ => false,
647 }
648 }
649
650 pub fn compare_mode(&self) -> Option<&str> {
651 match *self {
652 Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]),
653 _ => None,
654 }
655 }
656
657 pub fn pass(&self) -> Option<&str> {
658 match *self {
659 Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]),
660 _ => None,
661 }
662 }
663
664 pub fn run(&self) -> Option<&str> {
665 match *self {
666 Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]),
667 _ => None,
668 }
669 }
670
671 pub fn open(&self) -> bool {
672 match *self {
673 Subcommand::Doc { open, .. } => open,
674 _ => false,
675 }
676 }
677
678 pub fn json(&self) -> bool {
679 match *self {
680 Subcommand::Doc { json, .. } => json,
681 _ => false,
682 }
683 }
684
685 pub fn timings(&self) -> bool {
686 match *self {
687 Subcommand::Build { timings, .. } | Subcommand::Check { timings, .. } => timings,
688 _ => false,
689 }
690 }
691
692 pub fn vendor_versioned_dirs(&self) -> bool {
693 match *self {
694 Subcommand::Vendor { versioned_dirs, .. } => versioned_dirs,
695 _ => false,
696 }
697 }
698
699 pub fn vendor_sync_args(&self) -> Vec<PathBuf> {
700 match self {
701 Subcommand::Vendor { sync, .. } => sync.clone(),
702 _ => vec![],
703 }
704 }
705
706 pub fn test_codegen_backend(&self) -> Option<&CodegenBackendKind> {
707 match self {
708 Subcommand::Test { test_codegen_backend, .. } => test_codegen_backend.as_ref(),
709 _ => None,
710 }
711 }
712
713 pub fn bypass_ignore_backends(&self) -> bool {
714 match self {
715 Subcommand::Test { bypass_ignore_backends, .. } => *bypass_ignore_backends,
716 _ => false,
717 }
718 }
719
720 pub fn record(&self) -> bool {
721 match self {
722 Subcommand::Test { record, .. } => *record,
723 _ => false,
724 }
725 }
726
727 pub fn rerun(&self) -> bool {
728 match self {
729 Subcommand::Test { rerun, .. } => *rerun,
730 _ => false,
731 }
732 }
733}
734
735pub fn get_completion(shell: &dyn Generator, path: &Path) -> Option<String> {
738 let mut cmd = Flags::command();
739 let current = if !path.exists() {
740 String::new()
741 } else {
742 std::fs::read_to_string(path).unwrap_or_else(|_| {
743 eprintln!("couldn't read {}", path.display());
744 helpers::exit_process(1);
745 })
746 };
747 let mut buf = Vec::new();
748 let (bin_name, _) = path
749 .file_name()
750 .expect("path should be a regular file")
751 .to_str()
752 .expect("file name should be UTF-8")
753 .rsplit_once('.')
754 .expect("file name should have an extension");
755
756 cmd.set_bin_name(bin_name);
759 cmd.build();
760 shell.generate(&cmd, &mut buf);
761 if buf == current.as_bytes() {
762 return None;
763 }
764 Some(String::from_utf8(buf).expect("completion script should be UTF-8"))
765}
766
767pub fn top_level_help() -> String {
769 let mut cmd = Flags::command();
770 cmd.render_help().to_string()
771}