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
>,
36 // This overrides the deny-warnings configuation option,
37 // which passes -Dwarnings to the compiler invocations.
39 // true => deny, false => warn
40 pub deny_warnings
: Option
<bool
>,
42 pub llvm_skip_rebuild
: Option
<bool
>,
66 /// Whether to automatically update stderr/stdout files
68 compare_mode
: Option
<String
>,
70 test_args
: Vec
<String
>,
71 rustc_args
: Vec
<String
>,
74 rustfix_coverage
: bool
,
78 test_args
: Vec
<String
>,
91 impl Default
for Subcommand
{
92 fn default() -> Subcommand
{
93 Subcommand
::Build { paths: vec![PathBuf::from("nowhere")] }
98 pub fn parse(args
: &[String
]) -> Flags
{
99 let mut extra_help
= String
::new();
100 let mut subcommand_help
= String
::from(
102 Usage: x.py <subcommand> [options] [<paths>...]
105 build Compile either the compiler or libraries
106 check Compile either the compiler or libraries, using cargo check
107 clippy Run clippy (uses rustup/cargo-installed clippy binary)
110 test Build and run some test suites
111 bench Build and run some benchmarks
112 doc Build documentation
113 clean Clean out build directories
114 dist Build distribution artifacts
115 install Install distribution artifacts
117 To learn more about a subcommand, run `./x.py <subcommand> -h`",
120 let mut opts
= Options
::new();
121 // Options common to all subcommands
122 opts
.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
123 opts
.optflag("i", "incremental", "use incremental compilation");
124 opts
.optopt("", "config", "TOML configuration file for build", "FILE");
125 opts
.optopt("", "build", "build target of the stage0 compiler", "BUILD");
126 opts
.optmulti("", "host", "host targets to build", "HOST");
127 opts
.optmulti("", "target", "target targets to build", "TARGET");
128 opts
.optmulti("", "exclude", "build paths to exclude", "PATH");
129 opts
.optopt("", "on-fail", "command to run on failure", "CMD");
130 opts
.optflag("", "dry-run", "dry run; don't build anything");
134 "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \
135 bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)",
141 "stage(s) to keep without recompiling \
142 (pass multiple times to keep e.g., both stages 0 and 1)",
145 opts
.optopt("", "src", "path to the root of the rust checkout", "DIR");
146 opts
.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
147 opts
.optflag("h", "help", "print this help message");
151 "if value is deny, will deny warnings, otherwise use default",
154 opts
.optopt("", "error-format", "rustc error format", "FORMAT");
158 "whether rebuilding llvm should be skipped \
159 a VALUE of TRUE indicates that llvm will not be rebuilt \
160 VALUE overrides the skip-rebuild option in config.toml.",
166 |exit_code
: i32, opts
: &Options
, subcommand_help
: &str, extra_help
: &str| -> ! {
167 println
!("{}", opts
.usage(subcommand_help
));
168 if !extra_help
.is_empty() {
169 println
!("{}", extra_help
);
171 process
::exit(exit_code
);
174 // We can't use getopt to parse the options until we have completed specifying which
175 // options are valid, but under the current implementation, some options are conditional on
176 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
177 // complete the definition of the options. Then we can use the getopt::Matches object from
179 let subcommand
= args
.iter().find(|&s
| {
192 let subcommand
= match subcommand
{
195 // No or an invalid subcommand -- show the general usage and subcommand help
196 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
198 println
!("{}\n", subcommand_help
);
199 let exit_code
= if args
.is_empty() { 0 }
else { 1 }
;
200 process
::exit(exit_code
);
204 // Some subcommands get extra options
205 match subcommand
.as_str() {
207 opts
.optflag("", "no-fail-fast", "Run all tests regardless of failure");
208 opts
.optmulti("", "test-args", "extra arguments", "ARGS");
212 "extra options to pass the compiler when running tests",
215 opts
.optflag("", "no-doc", "do not run doc tests");
216 opts
.optflag("", "doc", "only run doc tests");
217 opts
.optflag("", "bless", "update all stderr/stdout files of failing ui tests");
221 "mode describing what file the actual ui output will be compared to",
227 "force {check,build,run}-pass tests to this mode.",
228 "check | build | run",
233 "enable this to generate a Rustfix coverage file, which is saved in \
234 `/<build_base>/rustfix_missing_coverage.txt`",
238 opts
.optmulti("", "test-args", "extra arguments", "ARGS");
241 opts
.optflag("", "all", "clean all build artifacts");
244 opts
.optflag("", "check", "check formatting instead of applying.");
249 // Done specifying what options are possible, so do the getopts parsing
250 let matches
= opts
.parse(&args
[..]).unwrap_or_else(|e
| {
251 // Invalid argument/option format
252 println
!("\n{}\n", e
);
253 usage(1, &opts
, &subcommand_help
, &extra_help
);
255 // Extra sanity check to make sure we didn't hit this crazy corner case:
257 // ./x.py --frobulate clean build
258 // ^-- option ^ ^- actual subcommand
259 // \_ arg to option could be mistaken as subcommand
260 let mut pass_sanity_check
= true;
261 match matches
.free
.get(0) {
262 Some(check_subcommand
) => {
263 if check_subcommand
!= subcommand
{
264 pass_sanity_check
= false;
268 pass_sanity_check
= false;
271 if !pass_sanity_check
{
272 println
!("{}\n", subcommand_help
);
274 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
275 You may need to move some options to after the subcommand.\n"
279 // Extra help text for some commands
280 match subcommand
.as_str() {
282 subcommand_help
.push_str(
285 This subcommand accepts a number of paths to directories to the crates
286 and/or artifacts to compile. For example:
288 ./x.py build src/libcore
289 ./x.py build src/libcore src/libproc_macro
290 ./x.py build src/libstd --stage 1
292 If no arguments are passed then the complete artifacts for that stage are
296 ./x.py build --stage 1
298 For a quick build of a usable compiler, you can pass:
300 ./x.py build --stage 1 src/libtest
302 This will first build everything once (like `--stage 0` without further
303 arguments would), and then use the compiler built in stage 0 to build
304 src/libtest and its dependencies.
305 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
309 subcommand_help
.push_str(
312 This subcommand accepts a number of paths to directories to the crates
313 and/or artifacts to compile. For example:
315 ./x.py check src/libcore
316 ./x.py check src/libcore src/libproc_macro
318 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
319 also that since we use `cargo check`, by default this will automatically enable incremental
320 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
321 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
326 subcommand_help
.push_str(
329 This subcommand accepts a number of paths to directories to the crates
330 and/or artifacts to run clippy against. For example:
332 ./x.py clippy src/libcore
333 ./x.py clippy src/libcore src/libproc_macro",
337 subcommand_help
.push_str(
340 This subcommand accepts a number of paths to directories to the crates
341 and/or artifacts to run `cargo fix` against. For example:
343 ./x.py fix src/libcore
344 ./x.py fix src/libcore src/libproc_macro",
348 subcommand_help
.push_str(
351 This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and
352 fails if it is not. For example:
359 subcommand_help
.push_str(
362 This subcommand accepts a number of paths to directories to tests that
363 should be compiled and run. For example:
365 ./x.py test src/test/ui
366 ./x.py test src/libstd --test-args hash_map
367 ./x.py test src/libstd --stage 0 --no-doc
368 ./x.py test src/test/ui --bless
369 ./x.py test src/test/ui --compare-mode nll
371 Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`;
372 just like `build src/libstd --stage N` it tests the compiler produced by the previous
375 If no arguments are passed then the complete artifacts for that stage are
379 ./x.py test --stage 1",
383 subcommand_help
.push_str(
386 This subcommand accepts a number of paths to directories of documentation
387 to build. For example:
389 ./x.py doc src/doc/book
390 ./x.py doc src/doc/nomicon
391 ./x.py doc src/doc/book src/libstd
393 If no arguments are passed then everything is documented:
396 ./x.py doc --stage 1",
401 // Get any optional paths which occur after the subcommand
402 let paths
= matches
.free
[1..].iter().map(|p
| p
.into()).collect
::<Vec
<PathBuf
>>();
404 let cfg_file
= matches
.opt_str("config").map(PathBuf
::from
).or_else(|| {
405 if fs
::metadata("config.toml").is_ok() {
406 Some(PathBuf
::from("config.toml"))
412 // All subcommands except `clean` can have an optional "Available paths" section
413 if matches
.opt_present("verbose") {
414 let config
= Config
::parse(&["build".to_string()]);
415 let mut build
= Build
::new(config
);
416 metadata
::build(&mut build
);
418 let maybe_rules_help
= Builder
::get_help(&build
, subcommand
.as_str());
419 extra_help
.push_str(maybe_rules_help
.unwrap_or_default().as_str());
420 } else if !(subcommand
.as_str() == "clean" || subcommand
.as_str() == "fmt") {
422 format
!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand
)
427 // User passed in -h/--help?
428 if matches
.opt_present("help") {
429 usage(0, &opts
, &subcommand_help
, &extra_help
);
432 let cmd
= match subcommand
.as_str() {
433 "build" => Subcommand
::Build { paths }
,
434 "check" => Subcommand
::Check { paths }
,
435 "clippy" => Subcommand
::Clippy { paths }
,
436 "fix" => Subcommand
::Fix { paths }
,
437 "test" => Subcommand
::Test
{
439 bless
: matches
.opt_present("bless"),
440 compare_mode
: matches
.opt_str("compare-mode"),
441 pass
: matches
.opt_str("pass"),
442 test_args
: matches
.opt_strs("test-args"),
443 rustc_args
: matches
.opt_strs("rustc-args"),
444 fail_fast
: !matches
.opt_present("no-fail-fast"),
445 rustfix_coverage
: matches
.opt_present("rustfix-coverage"),
446 doc_tests
: if matches
.opt_present("doc") {
448 } else if matches
.opt_present("no-doc") {
454 "bench" => Subcommand
::Bench { paths, test_args: matches.opt_strs("test-args") }
,
455 "doc" => Subcommand
::Doc { paths }
,
457 if !paths
.is_empty() {
458 println
!("\nclean does not take a path argument\n");
459 usage(1, &opts
, &subcommand_help
, &extra_help
);
462 Subcommand
::Clean { all: matches.opt_present("all") }
464 "fmt" => Subcommand
::Format { check: matches.opt_present("check") }
,
465 "dist" => Subcommand
::Dist { paths }
,
466 "install" => Subcommand
::Install { paths }
,
468 usage(1, &opts
, &subcommand_help
, &extra_help
);
473 verbose
: matches
.opt_count("verbose"),
474 stage
: matches
.opt_str("stage").map(|j
| j
.parse().expect("`stage` should be a number")),
475 dry_run
: matches
.opt_present("dry-run"),
476 on_fail
: matches
.opt_str("on-fail"),
477 rustc_error_format
: matches
.opt_str("error-format"),
479 .opt_strs("keep-stage")
481 .map(|j
| j
.parse().expect("`keep-stage` should be a number"))
483 host
: split(&matches
.opt_strs("host"))
485 .map(|x
| INTERNER
.intern_string(x
))
486 .collect
::<Vec
<_
>>(),
487 target
: split(&matches
.opt_strs("target"))
489 .map(|x
| INTERNER
.intern_string(x
))
490 .collect
::<Vec
<_
>>(),
492 jobs
: matches
.opt_str("jobs").map(|j
| j
.parse().expect("`jobs` should be a number")),
494 incremental
: matches
.opt_present("incremental"),
495 exclude
: split(&matches
.opt_strs("exclude"))
498 .collect
::<Vec
<_
>>(),
499 deny_warnings
: parse_deny_warnings(&matches
),
500 llvm_skip_rebuild
: matches
.opt_str("llvm-skip-rebuild").map(|s
| s
.to_lowercase()).map(
501 |s
| s
.parse
::<bool
>().expect("`llvm-skip-rebuild` should be either true or false"),
508 pub fn test_args(&self) -> Vec
<&str> {
510 Subcommand
::Test { ref test_args, .. }
| Subcommand
::Bench { ref test_args, .. }
=> {
511 test_args
.iter().flat_map(|s
| s
.split_whitespace()).collect()
517 pub fn rustc_args(&self) -> Vec
<&str> {
519 Subcommand
::Test { ref rustc_args, .. }
=> {
520 rustc_args
.iter().flat_map(|s
| s
.split_whitespace()).collect()
526 pub fn fail_fast(&self) -> bool
{
528 Subcommand
::Test { fail_fast, .. }
=> fail_fast
,
533 pub fn doc_tests(&self) -> DocTests
{
535 Subcommand
::Test { doc_tests, .. }
=> doc_tests
,
540 pub fn bless(&self) -> bool
{
542 Subcommand
::Test { bless, .. }
=> bless
,
547 pub fn rustfix_coverage(&self) -> bool
{
549 Subcommand
::Test { rustfix_coverage, .. }
=> rustfix_coverage
,
554 pub fn compare_mode(&self) -> Option
<&str> {
556 Subcommand
::Test { ref compare_mode, .. }
=> compare_mode
.as_ref().map(|s
| &s
[..]),
561 pub fn pass(&self) -> Option
<&str> {
563 Subcommand
::Test { ref pass, .. }
=> pass
.as_ref().map(|s
| &s
[..]),
569 fn split(s
: &[String
]) -> Vec
<String
> {
570 s
.iter().flat_map(|s
| s
.split('
,'
)).map(|s
| s
.to_string()).collect()
573 fn parse_deny_warnings(matches
: &getopts
::Matches
) -> Option
<bool
> {
574 match matches
.opt_str("warnings").as_ref().map(|v
| v
.as_str()) {
575 Some("deny") => Some(true),
576 Some("warn") => Some(false),
578 eprintln
!(r
#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,);