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