1 //! Command-line interface of the rustbuild build system.
3 //! This module implements the command-line parsing of the build system which
4 //! has various flags to configure how it's run.
7 use std
::path
::PathBuf
;
13 use crate::builder
::Builder
;
14 use crate::config
::{Config, TargetSelection}
;
15 use crate::setup
::Profile
;
16 use crate::{Build, DocTests}
;
24 impl Default
for Color
{
25 fn default() -> Self {
30 impl std
::str::FromStr
for Color
{
33 fn from_str(s
: &str) -> Result
<Self, Self::Err
> {
34 match s
.to_lowercase().as_str() {
35 "always" => Ok(Self::Always
),
36 "never" => Ok(Self::Never
),
37 "auto" => Ok(Self::Auto
),
43 /// Deserialized version of all flags for this compile.
45 pub verbose
: usize, // number of -v args; each extra -v after the first is passed to Cargo
46 pub on_fail
: Option
<String
>,
47 pub stage
: Option
<u32>,
48 pub keep_stage
: Vec
<u32>,
49 pub keep_stage_std
: Vec
<u32>,
51 pub host
: Option
<Vec
<TargetSelection
>>,
52 pub target
: Option
<Vec
<TargetSelection
>>,
53 pub config
: Option
<PathBuf
>,
54 pub jobs
: Option
<u32>,
56 pub incremental
: bool
,
57 pub exclude
: Vec
<PathBuf
>,
58 pub include_default_paths
: bool
,
59 pub rustc_error_format
: Option
<String
>,
60 pub json_output
: bool
,
64 // This overrides the deny-warnings configuration option,
65 // which passes -Dwarnings to the compiler invocations.
67 // true => deny, false => warn
68 pub deny_warnings
: Option
<bool
>,
70 pub llvm_skip_rebuild
: Option
<bool
>,
72 pub rust_profile_use
: Option
<String
>,
73 pub rust_profile_generate
: Option
<String
>,
75 pub llvm_profile_use
: Option
<String
>,
76 // LLVM doesn't support a custom location for generating profile
79 // llvm_out/build/profiles/ is the location this writes to.
80 pub llvm_profile_generate
: bool
,
107 /// Whether to automatically update stderr/stdout files
110 compare_mode
: Option
<String
>,
111 pass
: Option
<String
>,
113 test_args
: Vec
<String
>,
114 rustc_args
: Vec
<String
>,
117 rustfix_coverage
: bool
,
121 test_args
: Vec
<String
>,
140 impl Default
for Subcommand
{
141 fn default() -> Subcommand
{
142 Subcommand
::Build { paths: vec![PathBuf::from("nowhere")] }
147 pub fn parse(args
: &[String
]) -> Flags
{
148 let mut subcommand_help
= String
::from(
150 Usage: x.py <subcommand> [options] [<paths>...]
153 build, b Compile either the compiler or libraries
154 check, c Compile either the compiler or libraries, using cargo check
155 clippy Run clippy (uses rustup/cargo-installed clippy binary)
158 test, t Build and run some test suites
159 bench Build and run some benchmarks
160 doc, d Build documentation
161 clean Clean out build directories
162 dist Build distribution artifacts
163 install Install distribution artifacts
164 run, r Run tools contained in this repository
165 setup Create a config.toml (making it easier to use `x.py` itself)
167 To learn more about a subcommand, run `./x.py <subcommand> -h`",
170 let mut opts
= Options
::new();
171 // Options common to all subcommands
172 opts
.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
173 opts
.optflag("i", "incremental", "use incremental compilation");
174 opts
.optopt("", "config", "TOML configuration file for build", "FILE");
175 opts
.optopt("", "build", "build target of the stage0 compiler", "BUILD");
176 opts
.optmulti("", "host", "host targets to build", "HOST");
177 opts
.optmulti("", "target", "target targets to build", "TARGET");
178 opts
.optmulti("", "exclude", "build paths to exclude", "PATH");
181 "include-default-paths",
182 "include default paths in addition to the provided ones",
184 opts
.optopt("", "on-fail", "command to run on failure", "CMD");
185 opts
.optflag("", "dry-run", "dry run; don't build anything");
189 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
190 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
196 "stage(s) to keep without recompiling \
197 (pass multiple times to keep e.g., both stages 0 and 1)",
203 "stage(s) of the standard library to keep without recompiling \
204 (pass multiple times to keep e.g., both stages 0 and 1)",
207 opts
.optopt("", "src", "path to the root of the rust checkout", "DIR");
209 "number of jobs to run in parallel; \
210 defaults to {} (this host's logical CPU count)",
213 opts
.optopt("j", "jobs", &j_msg
, "JOBS");
214 opts
.optflag("h", "help", "print this help message");
218 "if value is deny, will deny warnings, otherwise use default",
221 opts
.optopt("", "error-format", "rustc error format", "FORMAT");
222 opts
.optflag("", "json-output", "use message-format=json");
223 opts
.optopt("", "color", "whether to use color in cargo and rustc output", "STYLE");
227 "whether rebuilding llvm should be skipped \
228 a VALUE of TRUE indicates that llvm will not be rebuilt \
229 VALUE overrides the skip-rebuild option in config.toml.",
234 "rust-profile-generate",
235 "generate PGO profile with rustc build",
238 opts
.optopt("", "rust-profile-use", "use PGO profile for rustc build", "PROFILE");
239 opts
.optflag("", "llvm-profile-generate", "generate PGO profile with llvm built for rustc");
240 opts
.optopt("", "llvm-profile-use", "use PGO profile for llvm build", "PROFILE");
242 // We can't use getopt to parse the options until we have completed specifying which
243 // options are valid, but under the current implementation, some options are conditional on
244 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
245 // complete the definition of the options. Then we can use the getopt::Matches object from
247 let subcommand
= args
.iter().find(|&s
| {
267 let subcommand
= match subcommand
{
270 // No or an invalid subcommand -- show the general usage and subcommand help
271 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
273 println
!("{}\n", subcommand_help
);
274 let exit_code
= if args
.is_empty() { 0 }
else { 1 }
;
275 process
::exit(exit_code
);
279 // Some subcommands get extra options
280 match subcommand
.as_str() {
282 opts
.optflag("", "no-fail-fast", "Run all tests regardless of failure");
286 "extra arguments to be passed for the test tool being used \
287 (e.g. libtest, compiletest or rustdoc)",
293 "extra options to pass the compiler when running tests",
296 opts
.optflag("", "no-doc", "do not run doc tests");
297 opts
.optflag("", "doc", "only run doc tests");
298 opts
.optflag("", "bless", "update all stderr/stdout files of failing ui tests");
299 opts
.optflag("", "force-rerun", "rerun tests even if the inputs are unchanged");
303 "mode describing what file the actual ui output will be compared to",
309 "force {check,build,run}-pass tests to this mode.",
310 "check | build | run",
312 opts
.optopt("", "run", "whether to execute run-* tests", "auto | always | never");
316 "enable this to generate a Rustfix coverage file, which is saved in \
317 `/<build_base>/rustfix_missing_coverage.txt`",
321 opts
.optflag("", "all-targets", "Check all targets");
324 opts
.optmulti("", "test-args", "extra arguments", "ARGS");
327 opts
.optflag("", "fix", "automatically apply lint suggestions");
330 opts
.optflag("", "open", "open the docs in a browser");
333 opts
.optflag("", "all", "clean all build artifacts");
336 opts
.optflag("", "check", "check formatting instead of applying.");
342 let usage
= |exit_code
: i32, opts
: &Options
, verbose
: bool
, subcommand_help
: &str| -> ! {
343 let mut extra_help
= String
::new();
345 // All subcommands except `clean` can have an optional "Available paths" section
347 let config
= Config
::parse(&["build".to_string()]);
348 let build
= Build
::new(config
);
350 let maybe_rules_help
= Builder
::get_help(&build
, subcommand
.as_str());
351 extra_help
.push_str(maybe_rules_help
.unwrap_or_default().as_str());
352 } else if !(subcommand
.as_str() == "clean" || subcommand
.as_str() == "fmt") {
354 format
!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand
)
359 println
!("{}", opts
.usage(subcommand_help
));
360 if !extra_help
.is_empty() {
361 println
!("{}", extra_help
);
363 process
::exit(exit_code
);
366 // Done specifying what options are possible, so do the getopts parsing
367 let matches
= opts
.parse(args
).unwrap_or_else(|e
| {
368 // Invalid argument/option format
369 println
!("\n{}\n", e
);
370 usage(1, &opts
, false, &subcommand_help
);
373 // Extra sanity check to make sure we didn't hit this crazy corner case:
375 // ./x.py --frobulate clean build
376 // ^-- option ^ ^- actual subcommand
377 // \_ arg to option could be mistaken as subcommand
378 let mut pass_sanity_check
= true;
379 match matches
.free
.get(0) {
380 Some(check_subcommand
) => {
381 if check_subcommand
!= subcommand
{
382 pass_sanity_check
= false;
386 pass_sanity_check
= false;
389 if !pass_sanity_check
{
390 println
!("{}\n", subcommand_help
);
392 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
393 You may need to move some options to after the subcommand.\n"
397 // Extra help text for some commands
398 match subcommand
.as_str() {
400 subcommand_help
.push_str(
403 This subcommand accepts a number of paths to directories to the crates
404 and/or artifacts to compile. For example:
406 ./x.py build library/core
407 ./x.py build library/core library/proc_macro
408 ./x.py build library/std --stage 1
410 If no arguments are passed then the complete artifacts for that stage are
414 ./x.py build --stage 1
416 For a quick build of a usable compiler, you can pass:
418 ./x.py build --stage 1 library/test
420 This will first build everything once (like `--stage 0` without further
421 arguments would), and then use the compiler built in stage 0 to build
422 library/test and its dependencies.
423 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
427 subcommand_help
.push_str(
430 This subcommand accepts a number of paths to directories to the crates
431 and/or artifacts to compile. For example:
433 ./x.py check library/core
434 ./x.py check library/core library/proc_macro
436 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
437 also that since we use `cargo check`, by default this will automatically enable incremental
438 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
439 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
444 subcommand_help
.push_str(
447 This subcommand accepts a number of paths to directories to the crates
448 and/or artifacts to run clippy against. For example:
450 ./x.py clippy library/core
451 ./x.py clippy library/core library/proc_macro",
455 subcommand_help
.push_str(
458 This subcommand accepts a number of paths to directories to the crates
459 and/or artifacts to run `cargo fix` against. For example:
461 ./x.py fix library/core
462 ./x.py fix library/core library/proc_macro",
466 subcommand_help
.push_str(
469 This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and
470 fails if it is not. For example:
477 subcommand_help
.push_str(
480 This subcommand accepts a number of paths to test directories that
481 should be compiled and run. For example:
483 ./x.py test src/test/ui
484 ./x.py test library/std --test-args hash_map
485 ./x.py test library/std --stage 0 --no-doc
486 ./x.py test src/test/ui --bless
487 ./x.py test src/test/ui --compare-mode nll
489 Note that `test src/test/* --stage N` does NOT depend on `build compiler/rustc --stage N`;
490 just like `build library/std --stage N` it tests the compiler produced by the previous
493 Execute tool tests with a tool name argument:
497 If no arguments are passed then the complete artifacts for that stage are
501 ./x.py test --stage 1",
505 subcommand_help
.push_str(
508 This subcommand accepts a number of paths to directories of documentation
509 to build. For example:
511 ./x.py doc src/doc/book
512 ./x.py doc src/doc/nomicon
513 ./x.py doc src/doc/book library/std
514 ./x.py doc library/std --open
516 If no arguments are passed then everything is documented:
519 ./x.py doc --stage 1",
523 subcommand_help
.push_str(
526 This subcommand accepts a number of paths to tools to build and run. For
529 ./x.py run src/tools/expand-yaml-anchors
531 At least a tool needs to be called.",
535 subcommand_help
.push_str(&format
!(
537 x.py setup creates a `config.toml` which changes the defaults for x.py itself.
540 This subcommand accepts a 'profile' to use for builds. For example:
544 The profile is optional and you will be prompted interactively if it is not given.
545 The following profiles are available:
548 Profile
::all_for_help(" ").trim_end()
553 // Get any optional paths which occur after the subcommand
554 let mut paths
= matches
.free
[1..].iter().map(|p
| p
.into()).collect
::<Vec
<PathBuf
>>();
556 let cfg_file
= env
::var_os("BOOTSTRAP_CONFIG").map(PathBuf
::from
);
557 let verbose
= matches
.opt_present("verbose");
559 // User passed in -h/--help?
560 if matches
.opt_present("help") {
561 usage(0, &opts
, verbose
, &subcommand_help
);
564 let cmd
= match subcommand
.as_str() {
565 "build" | "b" => Subcommand
::Build { paths }
,
567 if matches
.opt_present("all-targets") {
569 "Warning: --all-targets is now on by default and does not need to be passed explicitly."
572 Subcommand
::Check { paths }
574 "clippy" => Subcommand
::Clippy { paths, fix: matches.opt_present("fix") }
,
575 "fix" => Subcommand
::Fix { paths }
,
576 "test" | "t" => Subcommand
::Test
{
578 bless
: matches
.opt_present("bless"),
579 force_rerun
: matches
.opt_present("force-rerun"),
580 compare_mode
: matches
.opt_str("compare-mode"),
581 pass
: matches
.opt_str("pass"),
582 run
: matches
.opt_str("run"),
583 test_args
: matches
.opt_strs("test-args"),
584 rustc_args
: matches
.opt_strs("rustc-args"),
585 fail_fast
: !matches
.opt_present("no-fail-fast"),
586 rustfix_coverage
: matches
.opt_present("rustfix-coverage"),
587 doc_tests
: if matches
.opt_present("doc") {
589 } else if matches
.opt_present("no-doc") {
595 "bench" => Subcommand
::Bench { paths, test_args: matches.opt_strs("test-args") }
,
596 "doc" | "d" => Subcommand
::Doc { paths, open: matches.opt_present("open") }
,
598 if !paths
.is_empty() {
599 println
!("\nclean does not take a path argument\n");
600 usage(1, &opts
, verbose
, &subcommand_help
);
603 Subcommand
::Clean { all: matches.opt_present("all") }
605 "fmt" => Subcommand
::Format { check: matches.opt_present("check"), paths }
,
606 "dist" => Subcommand
::Dist { paths }
,
607 "install" => Subcommand
::Install { paths }
,
609 if paths
.is_empty() {
610 println
!("\nrun requires at least a path!\n");
611 usage(1, &opts
, verbose
, &subcommand_help
);
613 Subcommand
::Run { paths }
616 let profile
= if paths
.len() > 1 {
617 println
!("\nat most one profile can be passed to setup\n");
618 usage(1, &opts
, verbose
, &subcommand_help
)
619 } else if let Some(path
) = paths
.pop() {
620 let profile_string
= t
!(path
.into_os_string().into_string().map_err(
621 |path
| format
!("{} is not a valid UTF8 string", path
.to_string_lossy())
624 profile_string
.parse().unwrap_or_else(|err
| {
625 eprintln
!("error: {}", err
);
626 eprintln
!("help: the available profiles are:");
627 eprint
!("{}", Profile
::all_for_help("- "));
628 std
::process
::exit(1);
631 t
!(crate::setup
::interactive_path())
633 Subcommand
::Setup { profile }
636 usage(1, &opts
, verbose
, &subcommand_help
);
640 if let Subcommand
::Check { .. }
= &cmd
{
641 if matches
.opt_str("keep-stage").is_some()
642 || matches
.opt_str("keep-stage-std").is_some()
644 println
!("--keep-stage not yet supported for x.py check");
650 verbose
: matches
.opt_count("verbose"),
651 stage
: matches
.opt_str("stage").map(|j
| j
.parse().expect("`stage` should be a number")),
652 dry_run
: matches
.opt_present("dry-run"),
653 on_fail
: matches
.opt_str("on-fail"),
654 rustc_error_format
: matches
.opt_str("error-format"),
655 json_output
: matches
.opt_present("json-output"),
657 .opt_strs("keep-stage")
659 .map(|j
| j
.parse().expect("`keep-stage` should be a number"))
661 keep_stage_std
: matches
662 .opt_strs("keep-stage-std")
664 .map(|j
| j
.parse().expect("`keep-stage-std` should be a number"))
666 host
: if matches
.opt_present("host") {
668 split(&matches
.opt_strs("host"))
670 .map(|x
| TargetSelection
::from_user(&x
))
671 .collect
::<Vec
<_
>>(),
676 target
: if matches
.opt_present("target") {
678 split(&matches
.opt_strs("target"))
680 .map(|x
| TargetSelection
::from_user(&x
))
681 .collect
::<Vec
<_
>>(),
687 jobs
: matches
.opt_str("jobs").map(|j
| j
.parse().expect("`jobs` should be a number")),
689 incremental
: matches
.opt_present("incremental"),
690 exclude
: split(&matches
.opt_strs("exclude"))
693 .collect
::<Vec
<_
>>(),
694 include_default_paths
: matches
.opt_present("include-default-paths"),
695 deny_warnings
: parse_deny_warnings(&matches
),
696 llvm_skip_rebuild
: matches
.opt_str("llvm-skip-rebuild").map(|s
| s
.to_lowercase()).map(
697 |s
| s
.parse
::<bool
>().expect("`llvm-skip-rebuild` should be either true or false"),
700 .opt_get_default("color", Color
::Auto
)
701 .expect("`color` should be `always`, `never`, or `auto`"),
702 rust_profile_use
: matches
.opt_str("rust-profile-use"),
703 rust_profile_generate
: matches
.opt_str("rust-profile-generate"),
704 llvm_profile_use
: matches
.opt_str("llvm-profile-use"),
705 llvm_profile_generate
: matches
.opt_present("llvm-profile-generate"),
711 pub fn test_args(&self) -> Vec
<&str> {
713 Subcommand
::Test { ref test_args, .. }
| Subcommand
::Bench { ref test_args, .. }
=> {
714 test_args
.iter().flat_map(|s
| s
.split_whitespace()).collect()
720 pub fn rustc_args(&self) -> Vec
<&str> {
722 Subcommand
::Test { ref rustc_args, .. }
=> {
723 rustc_args
.iter().flat_map(|s
| s
.split_whitespace()).collect()
729 pub fn fail_fast(&self) -> bool
{
731 Subcommand
::Test { fail_fast, .. }
=> fail_fast
,
736 pub fn doc_tests(&self) -> DocTests
{
738 Subcommand
::Test { doc_tests, .. }
=> doc_tests
,
743 pub fn bless(&self) -> bool
{
745 Subcommand
::Test { bless, .. }
=> bless
,
750 pub fn force_rerun(&self) -> bool
{
752 Subcommand
::Test { force_rerun, .. }
=> force_rerun
,
757 pub fn rustfix_coverage(&self) -> bool
{
759 Subcommand
::Test { rustfix_coverage, .. }
=> rustfix_coverage
,
764 pub fn compare_mode(&self) -> Option
<&str> {
766 Subcommand
::Test { ref compare_mode, .. }
=> compare_mode
.as_ref().map(|s
| &s
[..]),
771 pub fn pass(&self) -> Option
<&str> {
773 Subcommand
::Test { ref pass, .. }
=> pass
.as_ref().map(|s
| &s
[..]),
778 pub fn run(&self) -> Option
<&str> {
780 Subcommand
::Test { ref run, .. }
=> run
.as_ref().map(|s
| &s
[..]),
785 pub fn open(&self) -> bool
{
787 Subcommand
::Doc { open, .. }
=> open
,
793 fn split(s
: &[String
]) -> Vec
<String
> {
794 s
.iter().flat_map(|s
| s
.split('
,'
)).filter(|s
| !s
.is_empty()).map(|s
| s
.to_string()).collect()
797 fn parse_deny_warnings(matches
: &getopts
::Matches
) -> Option
<bool
> {
798 match matches
.opt_str("warnings").as_deref() {
799 Some("deny") => Some(true),
800 Some("warn") => Some(false),
802 eprintln
!(r
#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,);