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