]>
Commit | Line | Data |
---|---|---|
a7813a04 XL |
1 | //! Command-line interface of the rustbuild build system. |
2 | //! | |
3 | //! This module implements the command-line parsing of the build system which | |
4 | //! has various flags to configure how it's run. | |
5 | ||
f035d41b | 6 | use std::env; |
7453a54e SL |
7 | use std::path::PathBuf; |
8 | use std::process; | |
7453a54e | 9 | |
1b1a35ee | 10 | use build_helper::t; |
cc61c64b | 11 | use getopts::Options; |
c30ab7b3 | 12 | |
0731742a | 13 | use crate::builder::Builder; |
3dfed10e | 14 | use crate::config::{Config, TargetSelection}; |
29967ef6 | 15 | use crate::setup::Profile; |
0731742a | 16 | use crate::{Build, DocTests}; |
3b2f2976 | 17 | |
fc512014 XL |
18 | pub enum Color { |
19 | Always, | |
20 | Never, | |
21 | Auto, | |
22 | } | |
23 | ||
24 | impl Default for Color { | |
25 | fn default() -> Self { | |
26 | Self::Auto | |
27 | } | |
28 | } | |
29 | ||
30 | impl std::str::FromStr for Color { | |
31 | type Err = (); | |
32 | ||
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), | |
38 | _ => Err(()), | |
39 | } | |
40 | } | |
41 | } | |
42 | ||
a7813a04 | 43 | /// Deserialized version of all flags for this compile. |
7453a54e | 44 | pub struct Flags { |
0531ce1d | 45 | pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo |
8bb4bdeb | 46 | pub on_fail: Option<String>, |
7453a54e | 47 | pub stage: Option<u32>, |
8faf50e0 | 48 | pub keep_stage: Vec<u32>, |
1b1a35ee | 49 | pub keep_stage_std: Vec<u32>, |
3b2f2976 | 50 | |
1b1a35ee XL |
51 | pub host: Option<Vec<TargetSelection>>, |
52 | pub target: Option<Vec<TargetSelection>>, | |
7453a54e | 53 | pub config: Option<PathBuf>, |
7453a54e | 54 | pub jobs: Option<u32>, |
c30ab7b3 | 55 | pub cmd: Subcommand, |
32a655c1 | 56 | pub incremental: bool, |
2c00a5a8 | 57 | pub exclude: Vec<PathBuf>, |
1b1a35ee | 58 | pub include_default_paths: bool, |
0531ce1d | 59 | pub rustc_error_format: Option<String>, |
ba9703b0 | 60 | pub json_output: bool, |
83c7162d | 61 | pub dry_run: bool, |
fc512014 | 62 | pub color: Color, |
83c7162d | 63 | |
74b04a01 | 64 | // This overrides the deny-warnings configuration option, |
416331ca XL |
65 | // which passes -Dwarnings to the compiler invocations. |
66 | // | |
e1599b0c | 67 | // true => deny, false => warn |
416331ca | 68 | pub deny_warnings: Option<bool>, |
dfeec247 XL |
69 | |
70 | pub llvm_skip_rebuild: Option<bool>, | |
fc512014 XL |
71 | |
72 | pub rust_profile_use: Option<String>, | |
73 | pub rust_profile_generate: Option<String>, | |
94222f64 XL |
74 | |
75 | pub llvm_profile_use: Option<String>, | |
76 | // LLVM doesn't support a custom location for generating profile | |
77 | // information. | |
78 | // | |
79 | // llvm_out/build/profiles/ is the location this writes to. | |
80 | pub llvm_profile_generate: bool, | |
32a655c1 SL |
81 | } |
82 | ||
c30ab7b3 SL |
83 | pub enum Subcommand { |
84 | Build { | |
85 | paths: Vec<PathBuf>, | |
86 | }, | |
2c00a5a8 XL |
87 | Check { |
88 | paths: Vec<PathBuf>, | |
89 | }, | |
dc9dc135 | 90 | Clippy { |
29967ef6 | 91 | fix: bool, |
dc9dc135 XL |
92 | paths: Vec<PathBuf>, |
93 | }, | |
94 | Fix { | |
95 | paths: Vec<PathBuf>, | |
96 | }, | |
dfeec247 | 97 | Format { |
17df50a5 | 98 | paths: Vec<PathBuf>, |
dfeec247 XL |
99 | check: bool, |
100 | }, | |
c30ab7b3 SL |
101 | Doc { |
102 | paths: Vec<PathBuf>, | |
f9f354fc | 103 | open: bool, |
c30ab7b3 SL |
104 | }, |
105 | Test { | |
106 | paths: Vec<PathBuf>, | |
94b46f34 XL |
107 | /// Whether to automatically update stderr/stdout files |
108 | bless: bool, | |
94222f64 | 109 | force_rerun: bool, |
94b46f34 | 110 | compare_mode: Option<String>, |
dc9dc135 | 111 | pass: Option<String>, |
17df50a5 | 112 | run: Option<String>, |
c30ab7b3 | 113 | test_args: Vec<String>, |
2c00a5a8 | 114 | rustc_args: Vec<String>, |
041b39d2 | 115 | fail_fast: bool, |
83c7162d | 116 | doc_tests: DocTests, |
532ac7d7 | 117 | rustfix_coverage: bool, |
c30ab7b3 | 118 | }, |
476ff2be SL |
119 | Bench { |
120 | paths: Vec<PathBuf>, | |
121 | test_args: Vec<String>, | |
122 | }, | |
ea8adc8c XL |
123 | Clean { |
124 | all: bool, | |
125 | }, | |
c30ab7b3 | 126 | Dist { |
32a655c1 | 127 | paths: Vec<PathBuf>, |
7cac9316 XL |
128 | }, |
129 | Install { | |
130 | paths: Vec<PathBuf>, | |
c30ab7b3 | 131 | }, |
ba9703b0 XL |
132 | Run { |
133 | paths: Vec<PathBuf>, | |
134 | }, | |
1b1a35ee | 135 | Setup { |
29967ef6 | 136 | profile: Profile, |
1b1a35ee | 137 | }, |
7453a54e SL |
138 | } |
139 | ||
3b2f2976 XL |
140 | impl Default for Subcommand { |
141 | fn default() -> Subcommand { | |
dfeec247 | 142 | Subcommand::Build { paths: vec![PathBuf::from("nowhere")] } |
3b2f2976 XL |
143 | } |
144 | } | |
145 | ||
7453a54e SL |
146 | impl Flags { |
147 | pub fn parse(args: &[String]) -> Flags { | |
dfeec247 XL |
148 | let mut subcommand_help = String::from( |
149 | "\ | |
cc61c64b XL |
150 | Usage: x.py <subcommand> [options] [<paths>...] |
151 | ||
152 | Subcommands: | |
f9f354fc XL |
153 | build, b Compile either the compiler or libraries |
154 | check, c Compile either the compiler or libraries, using cargo check | |
dfeec247 | 155 | clippy Run clippy (uses rustup/cargo-installed clippy binary) |
dc9dc135 | 156 | fix Run cargo fix |
dfeec247 | 157 | fmt Run rustfmt |
f9f354fc | 158 | test, t Build and run some test suites |
cc61c64b | 159 | bench Build and run some benchmarks |
136023e0 | 160 | doc, d Build documentation |
cc61c64b | 161 | clean Clean out build directories |
7cac9316 XL |
162 | dist Build distribution artifacts |
163 | install Install distribution artifacts | |
f9f354fc | 164 | run, r Run tools contained in this repository |
29967ef6 | 165 | setup Create a config.toml (making it easier to use `x.py` itself) |
cc61c64b | 166 | |
dfeec247 | 167 | To learn more about a subcommand, run `./x.py <subcommand> -h`", |
94b46f34 | 168 | ); |
cc61c64b | 169 | |
7453a54e | 170 | let mut opts = Options::new(); |
cc61c64b | 171 | // Options common to all subcommands |
32a655c1 SL |
172 | opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)"); |
173 | opts.optflag("i", "incremental", "use incremental compilation"); | |
7453a54e | 174 | opts.optopt("", "config", "TOML configuration file for build", "FILE"); |
c30ab7b3 | 175 | opts.optopt("", "build", "build target of the stage0 compiler", "BUILD"); |
7453a54e | 176 | opts.optmulti("", "host", "host targets to build", "HOST"); |
c30ab7b3 | 177 | opts.optmulti("", "target", "target targets to build", "TARGET"); |
2c00a5a8 | 178 | opts.optmulti("", "exclude", "build paths to exclude", "PATH"); |
1b1a35ee XL |
179 | opts.optflag( |
180 | "", | |
181 | "include-default-paths", | |
182 | "include default paths in addition to the provided ones", | |
183 | ); | |
8bb4bdeb | 184 | opts.optopt("", "on-fail", "command to run on failure", "CMD"); |
83c7162d | 185 | opts.optflag("", "dry-run", "dry run; don't build anything"); |
dfeec247 XL |
186 | opts.optopt( |
187 | "", | |
188 | "stage", | |
0731742a | 189 | "stage to build (indicates compiler to use/test, e.g., stage 0 uses the \ |
b7449926 | 190 | bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)", |
dfeec247 XL |
191 | "N", |
192 | ); | |
193 | opts.optmulti( | |
194 | "", | |
195 | "keep-stage", | |
196 | "stage(s) to keep without recompiling \ | |
197 | (pass multiple times to keep e.g., both stages 0 and 1)", | |
198 | "N", | |
199 | ); | |
1b1a35ee XL |
200 | opts.optmulti( |
201 | "", | |
202 | "keep-stage-std", | |
203 | "stage(s) of the standard library to keep without recompiling \ | |
204 | (pass multiple times to keep e.g., both stages 0 and 1)", | |
205 | "N", | |
206 | ); | |
c30ab7b3 | 207 | opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); |
f035d41b XL |
208 | let j_msg = format!( |
209 | "number of jobs to run in parallel; \ | |
210 | defaults to {} (this host's logical CPU count)", | |
211 | num_cpus::get() | |
212 | ); | |
213 | opts.optopt("j", "jobs", &j_msg, "JOBS"); | |
7453a54e | 214 | opts.optflag("h", "help", "print this help message"); |
94b46f34 XL |
215 | opts.optopt( |
216 | "", | |
217 | "warnings", | |
218 | "if value is deny, will deny warnings, otherwise use default", | |
219 | "VALUE", | |
220 | ); | |
0531ce1d | 221 | opts.optopt("", "error-format", "rustc error format", "FORMAT"); |
ba9703b0 | 222 | opts.optflag("", "json-output", "use message-format=json"); |
fc512014 | 223 | opts.optopt("", "color", "whether to use color in cargo and rustc output", "STYLE"); |
dfeec247 XL |
224 | opts.optopt( |
225 | "", | |
226 | "llvm-skip-rebuild", | |
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.", | |
230 | "VALUE", | |
231 | ); | |
94222f64 XL |
232 | opts.optopt( |
233 | "", | |
234 | "rust-profile-generate", | |
235 | "generate PGO profile with rustc build", | |
236 | "PROFILE", | |
237 | ); | |
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"); | |
7453a54e | 241 | |
cc61c64b XL |
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 | |
246 | // there on out. | |
94b46f34 | 247 | let subcommand = args.iter().find(|&s| { |
041b39d2 | 248 | (s == "build") |
f9f354fc | 249 | || (s == "b") |
94b46f34 | 250 | || (s == "check") |
f9f354fc | 251 | || (s == "c") |
dc9dc135 XL |
252 | || (s == "clippy") |
253 | || (s == "fix") | |
dfeec247 | 254 | || (s == "fmt") |
94b46f34 | 255 | || (s == "test") |
f9f354fc | 256 | || (s == "t") |
94b46f34 XL |
257 | || (s == "bench") |
258 | || (s == "doc") | |
136023e0 | 259 | || (s == "d") |
94b46f34 XL |
260 | || (s == "clean") |
261 | || (s == "dist") | |
262 | || (s == "install") | |
ba9703b0 | 263 | || (s == "run") |
f9f354fc | 264 | || (s == "r") |
1b1a35ee | 265 | || (s == "setup") |
94b46f34 | 266 | }); |
041b39d2 | 267 | let subcommand = match subcommand { |
cc61c64b XL |
268 | Some(s) => s, |
269 | None => { | |
abe05a73 XL |
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 | |
272 | // subcommand. | |
cc61c64b | 273 | println!("{}\n", subcommand_help); |
abe05a73 XL |
274 | let exit_code = if args.is_empty() { 0 } else { 1 }; |
275 | process::exit(exit_code); | |
cc61c64b XL |
276 | } |
277 | }; | |
c30ab7b3 | 278 | |
cc61c64b XL |
279 | // Some subcommands get extra options |
280 | match subcommand.as_str() { | |
f9f354fc | 281 | "test" | "t" => { |
7cac9316 | 282 | opts.optflag("", "no-fail-fast", "Run all tests regardless of failure"); |
29967ef6 XL |
283 | opts.optmulti( |
284 | "", | |
285 | "test-args", | |
286 | "extra arguments to be passed for the test tool being used \ | |
287 | (e.g. libtest, compiletest or rustdoc)", | |
288 | "ARGS", | |
289 | ); | |
2c00a5a8 XL |
290 | opts.optmulti( |
291 | "", | |
292 | "rustc-args", | |
293 | "extra options to pass the compiler when running tests", | |
294 | "ARGS", | |
295 | ); | |
83c7162d XL |
296 | opts.optflag("", "no-doc", "do not run doc tests"); |
297 | opts.optflag("", "doc", "only run doc tests"); | |
dfeec247 | 298 | opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests"); |
94222f64 | 299 | opts.optflag("", "force-rerun", "rerun tests even if the inputs are unchanged"); |
94b46f34 XL |
300 | opts.optopt( |
301 | "", | |
302 | "compare-mode", | |
303 | "mode describing what file the actual ui output will be compared to", | |
304 | "COMPARE MODE", | |
305 | ); | |
dc9dc135 XL |
306 | opts.optopt( |
307 | "", | |
308 | "pass", | |
309 | "force {check,build,run}-pass tests to this mode.", | |
dfeec247 | 310 | "check | build | run", |
dc9dc135 | 311 | ); |
17df50a5 | 312 | opts.optopt("", "run", "whether to execute run-* tests", "auto | always | never"); |
532ac7d7 XL |
313 | opts.optflag( |
314 | "", | |
315 | "rustfix-coverage", | |
316 | "enable this to generate a Rustfix coverage file, which is saved in \ | |
317 | `/<build_base>/rustfix_missing_coverage.txt`", | |
318 | ); | |
94b46f34 | 319 | } |
29967ef6 XL |
320 | "check" | "c" => { |
321 | opts.optflag("", "all-targets", "Check all targets"); | |
322 | } | |
94b46f34 XL |
323 | "bench" => { |
324 | opts.optmulti("", "test-args", "extra arguments", "ARGS"); | |
325 | } | |
29967ef6 XL |
326 | "clippy" => { |
327 | opts.optflag("", "fix", "automatically apply lint suggestions"); | |
328 | } | |
136023e0 | 329 | "doc" | "d" => { |
f9f354fc XL |
330 | opts.optflag("", "open", "open the docs in a browser"); |
331 | } | |
94b46f34 XL |
332 | "clean" => { |
333 | opts.optflag("", "all", "clean all build artifacts"); | |
334 | } | |
dfeec247 XL |
335 | "fmt" => { |
336 | opts.optflag("", "check", "check formatting instead of applying."); | |
337 | } | |
94b46f34 | 338 | _ => {} |
cc61c64b XL |
339 | }; |
340 | ||
1b1a35ee XL |
341 | // fn usage() |
342 | let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! { | |
343 | let mut extra_help = String::new(); | |
344 | ||
345 | // All subcommands except `clean` can have an optional "Available paths" section | |
346 | if verbose { | |
347 | let config = Config::parse(&["build".to_string()]); | |
348 | let build = Build::new(config); | |
349 | ||
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") { | |
353 | extra_help.push_str( | |
354 | format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand) | |
355 | .as_str(), | |
356 | ); | |
357 | } | |
358 | ||
359 | println!("{}", opts.usage(subcommand_help)); | |
360 | if !extra_help.is_empty() { | |
361 | println!("{}", extra_help); | |
362 | } | |
363 | process::exit(exit_code); | |
364 | }; | |
365 | ||
cc61c64b | 366 | // Done specifying what options are possible, so do the getopts parsing |
6a06907d | 367 | let matches = opts.parse(args).unwrap_or_else(|e| { |
cc61c64b XL |
368 | // Invalid argument/option format |
369 | println!("\n{}\n", e); | |
1b1a35ee | 370 | usage(1, &opts, false, &subcommand_help); |
cc61c64b | 371 | }); |
1b1a35ee | 372 | |
cc61c64b XL |
373 | // Extra sanity check to make sure we didn't hit this crazy corner case: |
374 | // | |
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) => { | |
041b39d2 | 381 | if check_subcommand != subcommand { |
cc61c64b XL |
382 | pass_sanity_check = false; |
383 | } | |
94b46f34 | 384 | } |
cc61c64b XL |
385 | None => { |
386 | pass_sanity_check = false; | |
387 | } | |
388 | } | |
389 | if !pass_sanity_check { | |
390 | println!("{}\n", subcommand_help); | |
94b46f34 XL |
391 | println!( |
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" | |
394 | ); | |
cc61c64b XL |
395 | process::exit(1); |
396 | } | |
397 | // Extra help text for some commands | |
398 | match subcommand.as_str() { | |
f9f354fc | 399 | "build" | "b" => { |
94b46f34 XL |
400 | subcommand_help.push_str( |
401 | "\n | |
c30ab7b3 | 402 | Arguments: |
cc61c64b XL |
403 | This subcommand accepts a number of paths to directories to the crates |
404 | and/or artifacts to compile. For example: | |
c30ab7b3 | 405 | |
3dfed10e XL |
406 | ./x.py build library/core |
407 | ./x.py build library/core library/proc_macro | |
408 | ./x.py build library/std --stage 1 | |
c30ab7b3 SL |
409 | |
410 | If no arguments are passed then the complete artifacts for that stage are | |
411 | also compiled. | |
412 | ||
413 | ./x.py build | |
414 | ./x.py build --stage 1 | |
415 | ||
041b39d2 XL |
416 | For a quick build of a usable compiler, you can pass: |
417 | ||
3dfed10e | 418 | ./x.py build --stage 1 library/test |
c30ab7b3 | 419 | |
b7449926 | 420 | This will first build everything once (like `--stage 0` without further |
041b39d2 | 421 | arguments would), and then use the compiler built in stage 0 to build |
3dfed10e | 422 | library/test and its dependencies. |
94b46f34 XL |
423 | Once this is done, build/$ARCH/stage1 contains a usable compiler.", |
424 | ); | |
2c00a5a8 | 425 | } |
f9f354fc | 426 | "check" | "c" => { |
94b46f34 XL |
427 | subcommand_help.push_str( |
428 | "\n | |
2c00a5a8 XL |
429 | Arguments: |
430 | This subcommand accepts a number of paths to directories to the crates | |
431 | and/or artifacts to compile. For example: | |
432 | ||
3dfed10e XL |
433 | ./x.py check library/core |
434 | ./x.py check library/core library/proc_macro | |
2c00a5a8 XL |
435 | |
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 | |
94b46f34 XL |
440 | the compiler.", |
441 | ); | |
cc61c64b | 442 | } |
dc9dc135 XL |
443 | "clippy" => { |
444 | subcommand_help.push_str( | |
445 | "\n | |
446 | Arguments: | |
447 | This subcommand accepts a number of paths to directories to the crates | |
448 | and/or artifacts to run clippy against. For example: | |
449 | ||
3dfed10e XL |
450 | ./x.py clippy library/core |
451 | ./x.py clippy library/core library/proc_macro", | |
dc9dc135 XL |
452 | ); |
453 | } | |
454 | "fix" => { | |
455 | subcommand_help.push_str( | |
456 | "\n | |
457 | Arguments: | |
458 | This subcommand accepts a number of paths to directories to the crates | |
459 | and/or artifacts to run `cargo fix` against. For example: | |
460 | ||
3dfed10e XL |
461 | ./x.py fix library/core |
462 | ./x.py fix library/core library/proc_macro", | |
dc9dc135 XL |
463 | ); |
464 | } | |
dfeec247 XL |
465 | "fmt" => { |
466 | subcommand_help.push_str( | |
467 | "\n | |
468 | Arguments: | |
469 | This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and | |
470 | fails if it is not. For example: | |
471 | ||
472 | ./x.py fmt | |
473 | ./x.py fmt --check", | |
474 | ); | |
475 | } | |
f9f354fc | 476 | "test" | "t" => { |
94b46f34 XL |
477 | subcommand_help.push_str( |
478 | "\n | |
c30ab7b3 | 479 | Arguments: |
ba9703b0 | 480 | This subcommand accepts a number of paths to test directories that |
cc61c64b | 481 | should be compiled and run. For example: |
c30ab7b3 | 482 | |
416331ca | 483 | ./x.py test src/test/ui |
3dfed10e XL |
484 | ./x.py test library/std --test-args hash_map |
485 | ./x.py test library/std --stage 0 --no-doc | |
94b46f34 XL |
486 | ./x.py test src/test/ui --bless |
487 | ./x.py test src/test/ui --compare-mode nll | |
c30ab7b3 | 488 | |
1b1a35ee | 489 | Note that `test src/test/* --stage N` does NOT depend on `build compiler/rustc --stage N`; |
3dfed10e | 490 | just like `build library/std --stage N` it tests the compiler produced by the previous |
b7449926 XL |
491 | stage. |
492 | ||
ba9703b0 XL |
493 | Execute tool tests with a tool name argument: |
494 | ||
495 | ./x.py test tidy | |
496 | ||
c30ab7b3 SL |
497 | If no arguments are passed then the complete artifacts for that stage are |
498 | compiled and tested. | |
499 | ||
500 | ./x.py test | |
94b46f34 XL |
501 | ./x.py test --stage 1", |
502 | ); | |
cc61c64b | 503 | } |
136023e0 | 504 | "doc" | "d" => { |
94b46f34 XL |
505 | subcommand_help.push_str( |
506 | "\n | |
c30ab7b3 | 507 | Arguments: |
cc61c64b XL |
508 | This subcommand accepts a number of paths to directories of documentation |
509 | to build. For example: | |
c30ab7b3 SL |
510 | |
511 | ./x.py doc src/doc/book | |
512 | ./x.py doc src/doc/nomicon | |
3dfed10e XL |
513 | ./x.py doc src/doc/book library/std |
514 | ./x.py doc library/std --open | |
c30ab7b3 SL |
515 | |
516 | If no arguments are passed then everything is documented: | |
517 | ||
518 | ./x.py doc | |
94b46f34 XL |
519 | ./x.py doc --stage 1", |
520 | ); | |
c30ab7b3 | 521 | } |
f9f354fc | 522 | "run" | "r" => { |
ba9703b0 XL |
523 | subcommand_help.push_str( |
524 | "\n | |
525 | Arguments: | |
526 | This subcommand accepts a number of paths to tools to build and run. For | |
527 | example: | |
528 | ||
3dfed10e | 529 | ./x.py run src/tools/expand-yaml-anchors |
ba9703b0 XL |
530 | |
531 | At least a tool needs to be called.", | |
532 | ); | |
533 | } | |
1b1a35ee | 534 | "setup" => { |
29967ef6 | 535 | subcommand_help.push_str(&format!( |
1b1a35ee | 536 | "\n |
29967ef6 XL |
537 | x.py setup creates a `config.toml` which changes the defaults for x.py itself. |
538 | ||
1b1a35ee XL |
539 | Arguments: |
540 | This subcommand accepts a 'profile' to use for builds. For example: | |
541 | ||
542 | ./x.py setup library | |
543 | ||
29967ef6 XL |
544 | The profile is optional and you will be prompted interactively if it is not given. |
545 | The following profiles are available: | |
546 | ||
547 | {}", | |
548 | Profile::all_for_help(" ").trim_end() | |
549 | )); | |
1b1a35ee | 550 | } |
94b46f34 | 551 | _ => {} |
7453a54e | 552 | }; |
cc61c64b | 553 | // Get any optional paths which occur after the subcommand |
1b1a35ee | 554 | let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>(); |
cc61c64b | 555 | |
f035d41b | 556 | let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from); |
1b1a35ee | 557 | let verbose = matches.opt_present("verbose"); |
c30ab7b3 | 558 | |
cc61c64b XL |
559 | // User passed in -h/--help? |
560 | if matches.opt_present("help") { | |
1b1a35ee | 561 | usage(0, &opts, verbose, &subcommand_help); |
cc61c64b | 562 | } |
c30ab7b3 | 563 | |
cc61c64b | 564 | let cmd = match subcommand.as_str() { |
f9f354fc | 565 | "build" | "b" => Subcommand::Build { paths }, |
29967ef6 | 566 | "check" | "c" => { |
94222f64 XL |
567 | if matches.opt_present("all-targets") { |
568 | eprintln!( | |
569 | "Warning: --all-targets is now on by default and does not need to be passed explicitly." | |
570 | ); | |
571 | } | |
572 | Subcommand::Check { paths } | |
29967ef6 XL |
573 | } |
574 | "clippy" => Subcommand::Clippy { paths, fix: matches.opt_present("fix") }, | |
dc9dc135 | 575 | "fix" => Subcommand::Fix { paths }, |
f9f354fc | 576 | "test" | "t" => Subcommand::Test { |
94b46f34 XL |
577 | paths, |
578 | bless: matches.opt_present("bless"), | |
94222f64 | 579 | force_rerun: matches.opt_present("force-rerun"), |
94b46f34 | 580 | compare_mode: matches.opt_str("compare-mode"), |
dc9dc135 | 581 | pass: matches.opt_str("pass"), |
17df50a5 | 582 | run: matches.opt_str("run"), |
94b46f34 XL |
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"), | |
532ac7d7 | 586 | rustfix_coverage: matches.opt_present("rustfix-coverage"), |
94b46f34 XL |
587 | doc_tests: if matches.opt_present("doc") { |
588 | DocTests::Only | |
589 | } else if matches.opt_present("no-doc") { | |
590 | DocTests::No | |
591 | } else { | |
592 | DocTests::Yes | |
593 | }, | |
594 | }, | |
dfeec247 | 595 | "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") }, |
136023e0 | 596 | "doc" | "d" => Subcommand::Doc { paths, open: matches.opt_present("open") }, |
c30ab7b3 | 597 | "clean" => { |
a1dfa0c6 | 598 | if !paths.is_empty() { |
ea8adc8c | 599 | println!("\nclean does not take a path argument\n"); |
1b1a35ee | 600 | usage(1, &opts, verbose, &subcommand_help); |
c30ab7b3 | 601 | } |
ea8adc8c | 602 | |
dfeec247 | 603 | Subcommand::Clean { all: matches.opt_present("all") } |
c30ab7b3 | 604 | } |
17df50a5 | 605 | "fmt" => Subcommand::Format { check: matches.opt_present("check"), paths }, |
94b46f34 XL |
606 | "dist" => Subcommand::Dist { paths }, |
607 | "install" => Subcommand::Install { paths }, | |
f9f354fc | 608 | "run" | "r" => { |
ba9703b0 XL |
609 | if paths.is_empty() { |
610 | println!("\nrun requires at least a path!\n"); | |
1b1a35ee | 611 | usage(1, &opts, verbose, &subcommand_help); |
ba9703b0 XL |
612 | } |
613 | Subcommand::Run { paths } | |
614 | } | |
1b1a35ee | 615 | "setup" => { |
29967ef6 | 616 | let profile = if paths.len() > 1 { |
1b1a35ee XL |
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() { | |
29967ef6 XL |
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()) | |
622 | )); | |
623 | ||
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); | |
629 | }) | |
1b1a35ee XL |
630 | } else { |
631 | t!(crate::setup::interactive_path()) | |
632 | }; | |
29967ef6 | 633 | Subcommand::Setup { profile } |
1b1a35ee | 634 | } |
cc61c64b | 635 | _ => { |
1b1a35ee | 636 | usage(1, &opts, verbose, &subcommand_help); |
c30ab7b3 SL |
637 | } |
638 | }; | |
639 | ||
f9f354fc | 640 | if let Subcommand::Check { .. } = &cmd { |
1b1a35ee XL |
641 | if matches.opt_str("keep-stage").is_some() |
642 | || matches.opt_str("keep-stage-std").is_some() | |
643 | { | |
5869c6ff | 644 | println!("--keep-stage not yet supported for x.py check"); |
f9f354fc XL |
645 | process::exit(1); |
646 | } | |
647 | } | |
648 | ||
7453a54e | 649 | Flags { |
cc61c64b | 650 | verbose: matches.opt_count("verbose"), |
60c5eb7d | 651 | stage: matches.opt_str("stage").map(|j| j.parse().expect("`stage` should be a number")), |
83c7162d | 652 | dry_run: matches.opt_present("dry-run"), |
cc61c64b | 653 | on_fail: matches.opt_str("on-fail"), |
0531ce1d | 654 | rustc_error_format: matches.opt_str("error-format"), |
ba9703b0 | 655 | json_output: matches.opt_present("json-output"), |
dfeec247 XL |
656 | keep_stage: matches |
657 | .opt_strs("keep-stage") | |
658 | .into_iter() | |
659 | .map(|j| j.parse().expect("`keep-stage` should be a number")) | |
8faf50e0 | 660 | .collect(), |
1b1a35ee XL |
661 | keep_stage_std: matches |
662 | .opt_strs("keep-stage-std") | |
94b46f34 | 663 | .into_iter() |
1b1a35ee XL |
664 | .map(|j| j.parse().expect("`keep-stage-std` should be a number")) |
665 | .collect(), | |
666 | host: if matches.opt_present("host") { | |
667 | Some( | |
668 | split(&matches.opt_strs("host")) | |
669 | .into_iter() | |
670 | .map(|x| TargetSelection::from_user(&x)) | |
671 | .collect::<Vec<_>>(), | |
672 | ) | |
673 | } else { | |
674 | None | |
675 | }, | |
676 | target: if matches.opt_present("target") { | |
677 | Some( | |
678 | split(&matches.opt_strs("target")) | |
679 | .into_iter() | |
680 | .map(|x| TargetSelection::from_user(&x)) | |
681 | .collect::<Vec<_>>(), | |
682 | ) | |
683 | } else { | |
684 | None | |
685 | }, | |
7453a54e | 686 | config: cfg_file, |
60c5eb7d | 687 | jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")), |
3b2f2976 | 688 | cmd, |
cc61c64b | 689 | incremental: matches.opt_present("incremental"), |
a1dfa0c6 | 690 | exclude: split(&matches.opt_strs("exclude")) |
94b46f34 XL |
691 | .into_iter() |
692 | .map(|p| p.into()) | |
693 | .collect::<Vec<_>>(), | |
1b1a35ee | 694 | include_default_paths: matches.opt_present("include-default-paths"), |
416331ca | 695 | deny_warnings: parse_deny_warnings(&matches), |
dfeec247 XL |
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"), | |
698 | ), | |
fc512014 XL |
699 | color: matches |
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"), | |
94222f64 XL |
704 | llvm_profile_use: matches.opt_str("llvm-profile-use"), |
705 | llvm_profile_generate: matches.opt_present("llvm-profile-generate"), | |
7453a54e SL |
706 | } |
707 | } | |
708 | } | |
709 | ||
c30ab7b3 SL |
710 | impl Subcommand { |
711 | pub fn test_args(&self) -> Vec<&str> { | |
712 | match *self { | |
94b46f34 | 713 | Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => { |
dfeec247 | 714 | test_args.iter().flat_map(|s| s.split_whitespace()).collect() |
c30ab7b3 SL |
715 | } |
716 | _ => Vec::new(), | |
717 | } | |
7453a54e | 718 | } |
7cac9316 | 719 | |
2c00a5a8 XL |
720 | pub fn rustc_args(&self) -> Vec<&str> { |
721 | match *self { | |
dfeec247 XL |
722 | Subcommand::Test { ref rustc_args, .. } => { |
723 | rustc_args.iter().flat_map(|s| s.split_whitespace()).collect() | |
724 | } | |
2c00a5a8 XL |
725 | _ => Vec::new(), |
726 | } | |
727 | } | |
728 | ||
041b39d2 | 729 | pub fn fail_fast(&self) -> bool { |
7cac9316 | 730 | match *self { |
041b39d2 | 731 | Subcommand::Test { fail_fast, .. } => fail_fast, |
7cac9316 XL |
732 | _ => false, |
733 | } | |
734 | } | |
0531ce1d | 735 | |
83c7162d | 736 | pub fn doc_tests(&self) -> DocTests { |
0531ce1d XL |
737 | match *self { |
738 | Subcommand::Test { doc_tests, .. } => doc_tests, | |
83c7162d | 739 | _ => DocTests::Yes, |
0531ce1d XL |
740 | } |
741 | } | |
94b46f34 XL |
742 | |
743 | pub fn bless(&self) -> bool { | |
744 | match *self { | |
745 | Subcommand::Test { bless, .. } => bless, | |
746 | _ => false, | |
747 | } | |
748 | } | |
749 | ||
94222f64 XL |
750 | pub fn force_rerun(&self) -> bool { |
751 | match *self { | |
752 | Subcommand::Test { force_rerun, .. } => force_rerun, | |
753 | _ => false, | |
754 | } | |
755 | } | |
756 | ||
532ac7d7 XL |
757 | pub fn rustfix_coverage(&self) -> bool { |
758 | match *self { | |
759 | Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage, | |
760 | _ => false, | |
761 | } | |
762 | } | |
763 | ||
94b46f34 XL |
764 | pub fn compare_mode(&self) -> Option<&str> { |
765 | match *self { | |
dfeec247 | 766 | Subcommand::Test { ref compare_mode, .. } => compare_mode.as_ref().map(|s| &s[..]), |
94b46f34 XL |
767 | _ => None, |
768 | } | |
769 | } | |
dc9dc135 XL |
770 | |
771 | pub fn pass(&self) -> Option<&str> { | |
772 | match *self { | |
dfeec247 | 773 | Subcommand::Test { ref pass, .. } => pass.as_ref().map(|s| &s[..]), |
dc9dc135 XL |
774 | _ => None, |
775 | } | |
776 | } | |
f9f354fc | 777 | |
17df50a5 XL |
778 | pub fn run(&self) -> Option<&str> { |
779 | match *self { | |
780 | Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]), | |
781 | _ => None, | |
782 | } | |
783 | } | |
784 | ||
f9f354fc XL |
785 | pub fn open(&self) -> bool { |
786 | match *self { | |
787 | Subcommand::Doc { open, .. } => open, | |
788 | _ => false, | |
789 | } | |
790 | } | |
7453a54e | 791 | } |
32a655c1 | 792 | |
a1dfa0c6 | 793 | fn split(s: &[String]) -> Vec<String> { |
1b1a35ee | 794 | s.iter().flat_map(|s| s.split(',')).filter(|s| !s.is_empty()).map(|s| s.to_string()).collect() |
32a655c1 | 795 | } |
416331ca XL |
796 | |
797 | fn parse_deny_warnings(matches: &getopts::Matches) -> Option<bool> { | |
74b04a01 | 798 | match matches.opt_str("warnings").as_deref() { |
416331ca | 799 | Some("deny") => Some(true), |
e1599b0c | 800 | Some("warn") => Some(false), |
416331ca | 801 | Some(value) => { |
dfeec247 | 802 | eprintln!(r#"invalid value for --warnings: {:?}, expected "warn" or "deny""#, value,); |
416331ca | 803 | process::exit(1); |
dfeec247 | 804 | } |
416331ca XL |
805 | None => None, |
806 | } | |
807 | } |