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)]
50 pub verbose: u8, #[arg(global = true, short, long)]
53 pub incremental: bool,
55 #[arg(global = true, long, value_hint = clap::ValueHint::FilePath, value_name = "FILE")]
56 pub config: Option<PathBuf>,
58 #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")]
59 pub build_dir: Option<PathBuf>,
61
62 #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "BUILD")]
63 pub build: Option<String>,
65
66 #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "HOST", value_parser = target_selection_list)]
67 pub host: Option<TargetSelectionList>,
69
70 #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "TARGET", value_parser = target_selection_list)]
71 pub target: Option<TargetSelectionList>,
73
74 #[arg(global = true, long, value_name = "PATH")]
75 pub exclude: Vec<PathBuf>, #[arg(global = true, long, value_name = "PATH")]
78 pub skip: Vec<PathBuf>,
80 #[arg(global = true, long)]
81 pub include_default_paths: bool,
83
84 #[arg(global = true, value_hint = clap::ValueHint::Other, long)]
86 pub rustc_error_format: Option<String>,
87
88 #[arg(global = true, long, value_hint = clap::ValueHint::CommandString, value_name = "CMD")]
89 pub on_fail: Option<String>,
91 #[arg(global = true, long)]
92 pub dry_run: bool,
94 #[arg(global = true, long)]
96 pub dump_bootstrap_shims: bool,
97 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
98 pub stage: Option<u32>,
101
102 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
103 pub keep_stage: Vec<u32>,
106 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")]
107 pub keep_stage_std: Vec<u32>,
110 #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")]
111 pub src: Option<PathBuf>,
113
114 #[arg(
115 global = true,
116 short,
117 long,
118 value_hint = clap::ValueHint::Other,
119 value_name = "JOBS"
120 )]
121 pub jobs: Option<u32>,
123 #[arg(global = true, long)]
126 #[arg(value_enum, default_value_t=Warnings::Default, value_name = "deny|warn")]
127 pub warnings: Warnings,
131
132 #[arg(global = true, long)]
133 pub json_output: bool,
135 #[arg(global = true, long)]
136 pub compile_time_deps: bool,
138
139 #[arg(global = true, long, value_name = "STYLE")]
140 #[arg(value_enum, default_value_t = Color::Auto)]
141 pub color: Color,
143
144 #[arg(global = true, long)]
145 pub bypass_bootstrap_lock: bool,
150
151 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
153 pub rust_profile_generate: Option<String>,
154 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
156 pub rust_profile_use: Option<String>,
157 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
159 pub llvm_profile_use: Option<String>,
160 #[arg(global = true, long)]
166 pub llvm_profile_generate: bool,
167 #[arg(global = true, long)]
169 pub enable_bolt_settings: bool,
170 #[arg(global = true, long)]
172 pub skip_stage0_validation: bool,
173 #[arg(global = true, long)]
175 pub reproducible_artifact: Vec<String>,
176 #[arg(global = true)]
177 pub paths: Vec<PathBuf>,
179 #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "section.option=value")]
181 pub set: Vec<String>,
182 #[arg(global = true, last(true), value_name = "ARGS")]
184 pub free_args: Vec<String>,
185 #[arg(global = true, long, value_name = "bool")]
187 pub ci: Option<bool>,
188 #[arg(global = true, long)]
192 pub skip_std_check_if_no_download_rustc: bool,
193}
194
195impl Flags {
196 pub fn try_parse_verbose_help(args: &[String]) -> bool {
199 #[derive(Parser)]
201 #[command(disable_help_flag(true))]
202 struct HelpVerboseOnly {
203 #[arg(short, long)]
204 help: bool,
205 #[arg(global = true, short, long, action = clap::ArgAction::Count)]
206 pub verbose: u8,
207 #[arg(value_enum)]
208 cmd: Kind,
209 }
210 if let Ok(HelpVerboseOnly { help: true, verbose: 1.., cmd: subcommand }) =
211 HelpVerboseOnly::try_parse_from(normalize_args(args))
212 {
213 println!("NOTE: updating submodules before printing available paths");
214 let flags = Self::parse(&[String::from("build")]);
215 let config = Config::parse(flags);
216 let build = Build::new(config);
217 let paths = Builder::get_help(&build, subcommand);
218 if let Some(s) = paths {
219 println!("{s}");
220 } else {
221 panic!("No paths available for subcommand `{}`", subcommand.as_str());
222 }
223 true
224 } else {
225 false
226 }
227 }
228
229 #[cfg_attr(
230 feature = "tracing",
231 instrument(level = "trace", name = "Flags::parse", skip_all, fields(args = ?args))
232 )]
233 pub fn parse(args: &[String]) -> Self {
234 Flags::parse_from(normalize_args(args))
235 }
236}
237
238fn normalize_args(args: &[String]) -> Vec<String> {
239 let first = String::from("x.py");
240 let it = std::iter::once(first).chain(args.iter().cloned());
241 it.collect()
242}
243
244#[derive(Debug, Clone, clap::Subcommand)]
245pub enum Subcommand {
246 #[command(aliases = ["b"], long_about = "\n
247 Arguments:
248 This subcommand accepts a number of paths to directories to the crates
249 and/or artifacts to compile. For example, for a quick build of a usable
250 compiler:
251 ./x.py build --stage 1 library/std
252 This will build a compiler and standard library from the local source code.
253 Once this is done, build/$ARCH/stage1 contains a usable compiler.
254 If no arguments are passed then the default artifacts for that stage are
255 compiled. For example:
256 ./x.py build --stage 0
257 ./x.py build ")]
258 Build {
260 #[arg(long)]
261 timings: bool,
263 },
264 #[command(aliases = ["c"], long_about = "\n
265 Arguments:
266 This subcommand accepts a number of paths to directories to the crates
267 and/or artifacts to compile. For example:
268 ./x.py check library/std
269 If no arguments are passed then many artifacts are checked.")]
270 Check {
272 #[arg(long)]
273 all_targets: bool,
275 #[arg(long)]
276 timings: bool,
278 },
279 #[command(long_about = "\n
281 Arguments:
282 This subcommand accepts a number of paths to directories to the crates
283 and/or artifacts to run clippy against. For example:
284 ./x.py clippy library/core
285 ./x.py clippy library/core library/proc_macro")]
286 Clippy {
287 #[arg(long)]
288 fix: bool,
289 #[arg(long, requires = "fix")]
290 allow_dirty: bool,
291 #[arg(long, requires = "fix")]
292 allow_staged: bool,
293 #[arg(global = true, short = 'A', action = clap::ArgAction::Append, value_name = "LINT")]
295 allow: Vec<String>,
296 #[arg(global = true, short = 'D', action = clap::ArgAction::Append, value_name = "LINT")]
298 deny: Vec<String>,
299 #[arg(global = true, short = 'W', action = clap::ArgAction::Append, value_name = "LINT")]
301 warn: Vec<String>,
302 #[arg(global = true, short = 'F', action = clap::ArgAction::Append, value_name = "LINT")]
304 forbid: Vec<String>,
305 },
306 #[command(long_about = "\n
308 Arguments:
309 This subcommand accepts a number of paths to directories to the crates
310 and/or artifacts to run `cargo fix` against. For example:
311 ./x.py fix library/core
312 ./x.py fix library/core library/proc_macro")]
313 Fix,
314 #[command(
315 name = "fmt",
316 long_about = "\n
317 Arguments:
318 This subcommand optionally accepts a `--check` flag which succeeds if
319 formatting is correct and fails if it is not. For example:
320 ./x.py fmt
321 ./x.py fmt --check"
322 )]
323 Format {
325 #[arg(long)]
327 check: bool,
328
329 #[arg(long)]
331 all: bool,
332 },
333 #[command(aliases = ["d"], long_about = "\n
334 Arguments:
335 This subcommand accepts a number of paths to directories of documentation
336 to build. For example:
337 ./x.py doc src/doc/book
338 ./x.py doc src/doc/nomicon
339 ./x.py doc src/doc/book library/std
340 ./x.py doc library/std --json
341 ./x.py doc library/std --open
342 If no arguments are passed then everything is documented:
343 ./x.py doc
344 ./x.py doc --stage 1")]
345 Doc {
347 #[arg(long)]
348 open: bool,
350 #[arg(long)]
351 json: bool,
353 },
354 #[command(aliases = ["t"], long_about = "\n
355 Arguments:
356 This subcommand accepts a number of paths to test directories that
357 should be compiled and run. For example:
358 ./x.py test tests/ui
359 ./x.py test library/std --test-args hash_map
360 ./x.py test library/std --stage 0 --all-targets
361 ./x.py test tests/ui --bless
362 ./x.py test tests/ui --compare-mode next-solver
363 Note that `test tests/* --stage N` does NOT depend on `build compiler/rustc --stage N`;
364 just like `build library/std --stage N` it tests the compiler produced by the previous
365 stage.
366 Execute tool tests with a tool name argument:
367 ./x.py test tidy
368 If no arguments are passed then the complete artifacts for that stage are
369 compiled and tested.
370 ./x.py test
371 ./x.py test --stage 1")]
372 Test {
374 #[arg(long)]
375 no_fail_fast: bool,
377 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
378 test_args: Vec<String>,
381 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
383 compiletest_rustc_args: Vec<String>,
384 #[arg(long)]
385 all_targets: bool,
387 #[arg(long)]
388 doc: bool,
390 #[arg(long)]
392 tests: bool,
393 #[arg(long)]
394 bless: bool,
396 #[arg(long)]
397 extra_checks: Option<String>,
403 #[arg(long)]
404 force_rerun: bool,
406 #[arg(long)]
407 only_modified: bool,
409 #[arg(long, value_name = "COMPARE MODE")]
410 compare_mode: Option<String>,
412 #[arg(long, value_name = "check | build | run")]
413 pass: Option<String>,
415 #[arg(long, value_name = "auto | always | never")]
416 run: Option<String>,
418 #[arg(long)]
419 rustfix_coverage: bool,
422 #[arg(long)]
423 no_capture: bool,
425 #[arg(long)]
426 test_codegen_backend: Option<CodegenBackendKind>,
428 #[arg(long)]
429 bypass_ignore_backends: bool,
431
432 #[arg(long)]
434 #[doc(hidden)]
435 no_doc: bool,
436 },
437 Miri {
439 #[arg(long)]
440 no_fail_fast: bool,
442 #[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
443 test_args: Vec<String>,
446 #[arg(long)]
447 all_targets: bool,
449 #[arg(long)]
450 doc: bool,
452 #[arg(long)]
454 tests: bool,
455
456 #[arg(long)]
458 #[doc(hidden)]
459 no_doc: bool,
460 },
461 Bench {
463 #[arg(long, allow_hyphen_values(true))]
464 test_args: Vec<String>,
465 },
466 Clean {
468 #[arg(long)]
469 all: bool,
471 #[arg(long, value_name = "N")]
472 stage: Option<u32>,
474 },
475 Dist,
477 Install,
479 #[command(aliases = ["r"], long_about = "\n
480 Arguments:
481 This subcommand accepts a number of paths to tools to build and run. For
482 example:
483 ./x.py run src/tools/bump-stage0
484 At least a tool needs to be called.")]
485 Run {
487 #[arg(long, allow_hyphen_values(true))]
489 args: Vec<String>,
490 },
491 #[command(long_about = format!(
493 "\n
494x.py setup creates a `bootstrap.toml` which changes the defaults for x.py itself,
495as well as setting up a git pre-push hook, VS Code config and toolchain link.
496Arguments:
497 This subcommand accepts a 'profile' to use for builds. For example:
498 ./x.py setup library
499 The profile is optional and you will be prompted interactively if it is not given.
500 The following profiles are available:
501{}
502 To only set up the git hook, editor config or toolchain link, you may use
503 ./x.py setup hook
504 ./x.py setup editor
505 ./x.py setup link", Profile::all_for_help(" ").trim_end()))]
506 Setup {
507 #[arg(value_name = "<PROFILE>|hook|editor|link")]
510 profile: Option<PathBuf>,
511 },
512 Vendor {
514 #[arg(long)]
516 sync: Vec<PathBuf>,
517 #[arg(long)]
519 versioned_dirs: bool,
520 },
521 Perf(PerfArgs),
523}
524
525impl Default for Subcommand {
526 fn default() -> Self {
527 Subcommand::Build { timings: false }
528 }
529}
530
531impl Subcommand {
532 pub fn kind(&self) -> Kind {
533 match self {
534 Subcommand::Bench { .. } => Kind::Bench,
535 Subcommand::Build { .. } => Kind::Build,
536 Subcommand::Check { .. } => Kind::Check,
537 Subcommand::Clippy { .. } => Kind::Clippy,
538 Subcommand::Doc { .. } => Kind::Doc,
539 Subcommand::Fix => Kind::Fix,
540 Subcommand::Format { .. } => Kind::Format,
541 Subcommand::Test { .. } => Kind::Test,
542 Subcommand::Miri { .. } => Kind::Miri,
543 Subcommand::Clean { .. } => Kind::Clean,
544 Subcommand::Dist => Kind::Dist,
545 Subcommand::Install => Kind::Install,
546 Subcommand::Run { .. } => Kind::Run,
547 Subcommand::Setup { .. } => Kind::Setup,
548 Subcommand::Vendor { .. } => Kind::Vendor,
549 Subcommand::Perf { .. } => Kind::Perf,
550 }
551 }
552
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 rustfix_coverage(&self) -> bool {
635 match *self {
636 Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage,
637 _ => false,
638 }
639 }
640
641 pub fn compare_mode(&self) -> Option<&str> {
642 match *self {
643 Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]),
644 _ => None,
645 }
646 }
647
648 pub fn pass(&self) -> Option<&str> {
649 match *self {
650 Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]),
651 _ => None,
652 }
653 }
654
655 pub fn run(&self) -> Option<&str> {
656 match *self {
657 Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]),
658 _ => None,
659 }
660 }
661
662 pub fn open(&self) -> bool {
663 match *self {
664 Subcommand::Doc { open, .. } => open,
665 _ => false,
666 }
667 }
668
669 pub fn json(&self) -> bool {
670 match *self {
671 Subcommand::Doc { json, .. } => json,
672 _ => false,
673 }
674 }
675
676 pub fn timings(&self) -> bool {
677 match *self {
678 Subcommand::Build { timings, .. } | Subcommand::Check { timings, .. } => timings,
679 _ => false,
680 }
681 }
682
683 pub fn vendor_versioned_dirs(&self) -> bool {
684 match *self {
685 Subcommand::Vendor { versioned_dirs, .. } => versioned_dirs,
686 _ => false,
687 }
688 }
689
690 pub fn vendor_sync_args(&self) -> Vec<PathBuf> {
691 match self {
692 Subcommand::Vendor { sync, .. } => sync.clone(),
693 _ => vec![],
694 }
695 }
696
697 pub fn test_codegen_backend(&self) -> Option<&CodegenBackendKind> {
698 match self {
699 Subcommand::Test { test_codegen_backend, .. } => test_codegen_backend.as_ref(),
700 _ => None,
701 }
702 }
703
704 pub fn bypass_ignore_backends(&self) -> bool {
705 match self {
706 Subcommand::Test { bypass_ignore_backends, .. } => *bypass_ignore_backends,
707 _ => false,
708 }
709 }
710}
711
712pub fn get_completion(shell: &dyn Generator, path: &Path) -> Option<String> {
715 let mut cmd = Flags::command();
716 let current = if !path.exists() {
717 String::new()
718 } else {
719 std::fs::read_to_string(path).unwrap_or_else(|_| {
720 eprintln!("couldn't read {}", path.display());
721 crate::exit!(1)
722 })
723 };
724 let mut buf = Vec::new();
725 let (bin_name, _) = path
726 .file_name()
727 .expect("path should be a regular file")
728 .to_str()
729 .expect("file name should be UTF-8")
730 .rsplit_once('.')
731 .expect("file name should have an extension");
732
733 cmd.set_bin_name(bin_name);
736 cmd.build();
737 shell.generate(&cmd, &mut buf);
738 if buf == current.as_bytes() {
739 return None;
740 }
741 Some(String::from_utf8(buf).expect("completion script should be UTF-8"))
742}
743
744pub fn top_level_help() -> String {
746 let mut cmd = Flags::command();
747 cmd.render_help().to_string()
748}