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
;
12 use crate::builder
::Builder
;
13 use crate::config
::Config
;
15 use crate::{Build, DocTests}
;
17 use crate::cache
::{Interned, INTERNER}
;
19 /// Deserialized version of all flags for this compile.
21 pub verbose
: usize, // number of -v args; each extra -v after the first is passed to Cargo
22 pub on_fail
: Option
<String
>,
23 pub stage
: Option
<u32>,
24 pub keep_stage
: Vec
<u32>,
26 pub host
: Vec
<Interned
<String
>>,
27 pub target
: Vec
<Interned
<String
>>,
28 pub config
: Option
<PathBuf
>,
29 pub jobs
: Option
<u32>,
31 pub incremental
: bool
,
32 pub exclude
: Vec
<PathBuf
>,
33 pub rustc_error_format
: Option
<String
>,
34 pub json_output
: bool
,
37 // This overrides the deny-warnings configuration option,
38 // which passes -Dwarnings to the compiler invocations.
40 // true => deny, false => warn
41 pub deny_warnings
: Option
<bool
>,
43 pub llvm_skip_rebuild
: Option
<bool
>,
67 /// Whether to automatically update stderr/stdout files
69 compare_mode
: Option
<String
>,
71 test_args
: Vec
<String
>,
72 rustc_args
: Vec
<String
>,
75 rustfix_coverage
: bool
,
79 test_args
: Vec
<String
>,
95 impl Default
for Subcommand
{
96 fn default() -> Subcommand
{
97 Subcommand
::Build { paths: vec![PathBuf::from("nowhere")] }
102 pub fn parse(args
: &[String
]) -> Flags
{
103 let mut extra_help
= String
::new();
104 let mut subcommand_help
= String
::from(
106 Usage: x.py <subcommand> [options] [<paths>...]
109 build Compile either the compiler or libraries
110 check Compile either the compiler or libraries, using cargo check
111 clippy Run clippy (uses rustup/cargo-installed clippy binary)
114 test Build and run some test suites
115 bench Build and run some benchmarks
116 doc Build documentation
117 clean Clean out build directories
118 dist Build distribution artifacts
119 install Install distribution artifacts
120 run Run tools contained in this repository
122 To learn more about a subcommand, run `./x.py <subcommand> -h`",
125 let mut opts
= Options
::new();
126 // Options common to all subcommands
127 opts
.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
128 opts
.optflag("i", "incremental", "use incremental compilation");
129 opts
.optopt("", "config", "TOML configuration file for build", "FILE");
130 opts
.optopt("", "build", "build target of the stage0 compiler", "BUILD");
131 opts
.optmulti("", "host", "host targets to build", "HOST");
132 opts
.optmulti("", "target", "target targets to build", "TARGET");
133 opts
.optmulti("", "exclude", "build paths to exclude", "PATH");
134 opts
.optopt("", "on-fail", "command to run on failure", "CMD");
135 opts
.optflag("", "dry-run", "dry run; don't build anything");
139 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
140 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
146 "stage(s) to keep without recompiling \
147 (pass multiple times to keep e.g., both stages 0 and 1)",
150 opts
.optopt("", "src", "path to the root of the rust checkout", "DIR");
151 opts
.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
152 opts
.optflag("h", "help", "print this help message");
156 "if value is deny, will deny warnings, otherwise use default",
159 opts
.optopt("", "error-format", "rustc error format", "FORMAT");
160 opts
.optflag("", "json-output", "use message-format=json");
164 "whether rebuilding llvm should be skipped \
165 a VALUE of TRUE indicates that llvm will not be rebuilt \
166 VALUE overrides the skip-rebuild option in config.toml.",
172 |exit_code
: i32, opts
: &Options
, subcommand_help
: &str, extra_help
: &str| -> ! {
173 println
!("{}", opts
.usage(subcommand_help
));
174 if !extra_help
.is_empty() {
175 println
!("{}", extra_help
);
177 process
::exit(exit_code
);
180 // We can't use getopt to parse the options until we have completed specifying which
181 // options are valid, but under the current implementation, some options are conditional on
182 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
183 // complete the definition of the options. Then we can use the getopt::Matches object from
185 let subcommand
= args
.iter().find(|&s
| {
199 let subcommand
= match subcommand
{
202 // No or an invalid subcommand -- show the general usage and subcommand help
203 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
205 println
!("{}\n", subcommand_help
);
206 let exit_code
= if args
.is_empty() { 0 }
else { 1 }
;
207 process
::exit(exit_code
);
211 // Some subcommands get extra options
212 match subcommand
.as_str() {
214 opts
.optflag("", "no-fail-fast", "Run all tests regardless of failure");
215 opts
.optmulti("", "test-args", "extra arguments", "ARGS");
219 "extra options to pass the compiler when running tests",
222 opts
.optflag("", "no-doc", "do not run doc tests");
223 opts
.optflag("", "doc", "only run doc tests");
224 opts
.optflag("", "bless", "update all stderr/stdout files of failing ui tests");
228 "mode describing what file the actual ui output will be compared to",
234 "force {check,build,run}-pass tests to this mode.",
235 "check | build | run",
240 "enable this to generate a Rustfix coverage file, which is saved in \
241 `/<build_base>/rustfix_missing_coverage.txt`",
245 opts
.optmulti("", "test-args", "extra arguments", "ARGS");
248 opts
.optflag("", "all", "clean all build artifacts");
251 opts
.optflag("", "check", "check formatting instead of applying.");
256 // Done specifying what options are possible, so do the getopts parsing
257 let matches
= opts
.parse(&args
[..]).unwrap_or_else(|e
| {
258 // Invalid argument/option format
259 println
!("\n{}\n", e
);
260 usage(1, &opts
, &subcommand_help
, &extra_help
);
262 // Extra sanity check to make sure we didn't hit this crazy corner case:
264 // ./x.py --frobulate clean build
265 // ^-- option ^ ^- actual subcommand
266 // \_ arg to option could be mistaken as subcommand
267 let mut pass_sanity_check
= true;
268 match matches
.free
.get(0) {
269 Some(check_subcommand
) => {
270 if check_subcommand
!= subcommand
{
271 pass_sanity_check
= false;
275 pass_sanity_check
= false;
278 if !pass_sanity_check
{
279 println
!("{}\n", subcommand_help
);
281 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
282 You may need to move some options to after the subcommand.\n"
286 // Extra help text for some commands
287 match subcommand
.as_str() {
289 subcommand_help
.push_str(
292 This subcommand accepts a number of paths to directories to the crates
293 and/or artifacts to compile. For example:
295 ./x.py build src/libcore
296 ./x.py build src/libcore src/libproc_macro
297 ./x.py build src/libstd --stage 1
299 If no arguments are passed then the complete artifacts for that stage are
303 ./x.py build --stage 1
305 For a quick build of a usable compiler, you can pass:
307 ./x.py build --stage 1 src/libtest
309 This will first build everything once (like `--stage 0` without further
310 arguments would), and then use the compiler built in stage 0 to build
311 src/libtest and its dependencies.
312 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
316 subcommand_help
.push_str(
319 This subcommand accepts a number of paths to directories to the crates
320 and/or artifacts to compile. For example:
322 ./x.py check src/libcore
323 ./x.py check src/libcore src/libproc_macro
325 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
326 also that since we use `cargo check`, by default this will automatically enable incremental
327 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
328 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
333 subcommand_help
.push_str(
336 This subcommand accepts a number of paths to directories to the crates
337 and/or artifacts to run clippy against. For example:
339 ./x.py clippy src/libcore
340 ./x.py clippy src/libcore src/libproc_macro",
344 subcommand_help
.push_str(
347 This subcommand accepts a number of paths to directories to the crates
348 and/or artifacts to run `cargo fix` against. For example:
350 ./x.py fix src/libcore
351 ./x.py fix src/libcore src/libproc_macro",
355 subcommand_help
.push_str(
358 This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and
359 fails if it is not. For example:
366 subcommand_help
.push_str(
369 This subcommand accepts a number of paths to test directories that
370 should be compiled and run. For example:
372 ./x.py test src/test/ui
373 ./x.py test src/libstd --test-args hash_map
374 ./x.py test src/libstd --stage 0 --no-doc
375 ./x.py test src/test/ui --bless
376 ./x.py test src/test/ui --compare-mode nll
378 Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
379 just like `build src/libstd --stage N` it tests the compiler produced by the previous
382 Execute tool tests with a tool name argument:
386 If no arguments are passed then the complete artifacts for that stage are
390 ./x.py test --stage 1",
394 subcommand_help
.push_str(
397 This subcommand accepts a number of paths to directories of documentation
398 to build. For example:
400 ./x.py doc src/doc/book
401 ./x.py doc src/doc/nomicon
402 ./x.py doc src/doc/book src/libstd
404 If no arguments are passed then everything is documented:
407 ./x.py doc --stage 1",
411 subcommand_help
.push_str(
414 This subcommand accepts a number of paths to tools to build and run. For
417 ./x.py run src/tool/expand-yaml-anchors
419 At least a tool needs to be called.",
424 // Get any optional paths which occur after the subcommand
425 let paths
= matches
.free
[1..].iter().map(|p
| p
.into()).collect
::<Vec
<PathBuf
>>();
427 let cfg_file
= matches
.opt_str("config").map(PathBuf
::from
).or_else(|| {
428 if fs
::metadata("config.toml").is_ok() {
429 Some(PathBuf
::from("config.toml"))
435 // All subcommands except `clean` can have an optional "Available paths" section
436 if matches
.opt_present("verbose") {
437 let config
= Config
::parse(&["build".to_string()]);
438 let mut build
= Build
::new(config
);
439 metadata
::build(&mut build
);
441 let maybe_rules_help
= Builder
::get_help(&build
, subcommand
.as_str());
442 extra_help
.push_str(maybe_rules_help
.unwrap_or_default().as_str());
443 } else if !(subcommand
.as_str() == "clean" || subcommand
.as_str() == "fmt") {
445 format
!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand
)
450 // User passed in -h/--help?
451 if matches
.opt_present("help") {
452 usage(0, &opts
, &subcommand_help
, &extra_help
);
455 let cmd
= match subcommand
.as_str() {
456 "build" => Subcommand
::Build { paths }
,
457 "check" => Subcommand
::Check { paths }
,
458 "clippy" => Subcommand
::Clippy { paths }
,
459 "fix" => Subcommand
::Fix { paths }
,
460 "test" => Subcommand
::Test
{
462 bless
: matches
.opt_present("bless"),
463 compare_mode
: matches
.opt_str("compare-mode"),
464 pass
: matches
.opt_str("pass"),
465 test_args
: matches
.opt_strs("test-args"),
466 rustc_args
: matches
.opt_strs("rustc-args"),
467 fail_fast
: !matches
.opt_present("no-fail-fast"),
468 rustfix_coverage
: matches
.opt_present("rustfix-coverage"),
469 doc_tests
: if matches
.opt_present("doc") {
471 } else if matches
.opt_present("no-doc") {
477 "bench" => Subcommand
::Bench { paths, test_args: matches.opt_strs("test-args") }
,
478 "doc" => Subcommand
::Doc { paths }
,
480 if !paths
.is_empty() {
481 println
!("\nclean does not take a path argument\n");
482 usage(1, &opts
, &subcommand_help
, &extra_help
);
485 Subcommand
::Clean { all: matches.opt_present("all") }
487 "fmt" => Subcommand
::Format { check: matches.opt_present("check") }
,
488 "dist" => Subcommand
::Dist { paths }
,
489 "install" => Subcommand
::Install { paths }
,
491 if paths
.is_empty() {
492 println
!("\nrun requires at least a path!\n");
493 usage(1, &opts
, &subcommand_help
, &extra_help
);
495 Subcommand
::Run { paths }
498 usage(1, &opts
, &subcommand_help
, &extra_help
);
503 verbose
: matches
.opt_count("verbose"),
504 stage
: matches
.opt_str("stage").map(|j
| j
.parse().expect("`stage` should be a number")),
505 dry_run
: matches
.opt_present("dry-run"),
506 on_fail
: matches
.opt_str("on-fail"),
507 rustc_error_format
: matches
.opt_str("error-format"),
508 json_output
: matches
.opt_present("json-output"),
510 .opt_strs("keep-stage")
512 .map(|j
| j
.parse().expect("`keep-stage` should be a number"))
514 host
: split(&matches
.opt_strs("host"))
516 .map(|x
| INTERNER
.intern_string(x
))
517 .collect
::<Vec
<_
>>(),
518 target
: split(&matches
.opt_strs("target"))
520 .map(|x
| INTERNER
.intern_string(x
))
521 .collect
::<Vec
<_
>>(),
523 jobs
: matches
.opt_str("jobs").map(|j
| j
.parse().expect("`jobs` should be a number")),
525 incremental
: matches
.opt_present("incremental"),
526 exclude
: split(&matches
.opt_strs("exclude"))
529 .collect
::<Vec
<_
>>(),
530 deny_warnings
: parse_deny_warnings(&matches
),
531 llvm_skip_rebuild
: matches
.opt_str("llvm-skip-rebuild").map(|s
| s
.to_lowercase()).map(
532 |s
| s
.parse
::<bool
>().expect("`llvm-skip-rebuild` should be either true or false"),
539 pub fn test_args(&self) -> Vec
<&str> {
541 Subcommand
::Test { ref test_args, .. }
| Subcommand
::Bench { ref test_args, .. }
=> {
542 test_args
.iter().flat_map(|s
| s
.split_whitespace()).collect()
548 pub fn rustc_args(&self) -> Vec
<&str> {
550 Subcommand
::Test { ref rustc_args, .. }
=> {
551 rustc_args
.iter().flat_map(|s
| s
.split_whitespace()).collect()
557 pub fn fail_fast(&self) -> bool
{
559 Subcommand
::Test { fail_fast, .. }
=> fail_fast
,
564 pub fn doc_tests(&self) -> DocTests
{
566 Subcommand
::Test { doc_tests, .. }
=> doc_tests
,
571 pub fn bless(&self) -> bool
{
573 Subcommand
::Test { bless, .. }
=> bless
,
578 pub fn rustfix_coverage(&self) -> bool
{
580 Subcommand
::Test { rustfix_coverage, .. }
=> rustfix_coverage
,
585 pub fn compare_mode(&self) -> Option
<&str> {
587 Subcommand
::Test { ref compare_mode, .. }
=> compare_mode
.as_ref().map(|s
| &s
[..]),
592 pub fn pass(&self) -> Option
<&str> {
594 Subcommand
::Test { ref pass, .. }
=> pass
.as_ref().map(|s
| &s
[..]),
600 fn split(s
: &[String
]) -> Vec
<String
> {
601 s
.iter().flat_map(|s
| s
.split('
,'
)).map(|s
| s
.to_string()).collect()
604 fn parse_deny_warnings(matches
: &getopts
::Matches
) -> Option
<bool
> {
605 match matches
.opt_str("warnings").as_deref() {
606 Some("deny") => Some(true),
607 Some("warn") => Some(false),
609 eprintln
!(r
#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,);