]> git.proxmox.com Git - rustc.git/blame - src/bootstrap/flags.rs
New upstream version 1.28.0~beta.14+dfsg1
[rustc.git] / src / bootstrap / flags.rs
CommitLineData
7453a54e
SL
1// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
a7813a04
XL
11//! Command-line interface of the rustbuild build system.
12//!
13//! This module implements the command-line parsing of the build system which
14//! has various flags to configure how it's run.
15
7453a54e
SL
16use std::fs;
17use std::path::PathBuf;
18use std::process;
7453a54e 19
cc61c64b 20use getopts::Options;
c30ab7b3 21
94b46f34 22use builder::Builder;
c30ab7b3
SL
23use config::Config;
24use metadata;
94b46f34 25use {Build, DocTests};
3b2f2976
XL
26
27use cache::{Interned, INTERNER};
7453a54e 28
a7813a04 29/// Deserialized version of all flags for this compile.
7453a54e 30pub struct Flags {
0531ce1d 31 pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
8bb4bdeb 32 pub on_fail: Option<String>,
7453a54e 33 pub stage: Option<u32>,
476ff2be 34 pub keep_stage: Option<u32>,
3b2f2976
XL
35
36 pub host: Vec<Interned<String>>,
37 pub target: Vec<Interned<String>>,
7453a54e 38 pub config: Option<PathBuf>,
7453a54e 39 pub jobs: Option<u32>,
c30ab7b3 40 pub cmd: Subcommand,
32a655c1 41 pub incremental: bool,
2c00a5a8 42 pub exclude: Vec<PathBuf>,
0531ce1d 43 pub rustc_error_format: Option<String>,
83c7162d
XL
44 pub dry_run: bool,
45
46 // true => deny
47 pub warnings: Option<bool>,
32a655c1
SL
48}
49
c30ab7b3
SL
50pub enum Subcommand {
51 Build {
52 paths: Vec<PathBuf>,
53 },
2c00a5a8
XL
54 Check {
55 paths: Vec<PathBuf>,
56 },
c30ab7b3
SL
57 Doc {
58 paths: Vec<PathBuf>,
59 },
60 Test {
61 paths: Vec<PathBuf>,
94b46f34
XL
62 /// Whether to automatically update stderr/stdout files
63 bless: bool,
64 compare_mode: Option<String>,
c30ab7b3 65 test_args: Vec<String>,
2c00a5a8 66 rustc_args: Vec<String>,
041b39d2 67 fail_fast: bool,
83c7162d 68 doc_tests: DocTests,
c30ab7b3 69 },
476ff2be
SL
70 Bench {
71 paths: Vec<PathBuf>,
72 test_args: Vec<String>,
73 },
ea8adc8c
XL
74 Clean {
75 all: bool,
76 },
c30ab7b3 77 Dist {
32a655c1 78 paths: Vec<PathBuf>,
7cac9316
XL
79 },
80 Install {
81 paths: Vec<PathBuf>,
c30ab7b3 82 },
7453a54e
SL
83}
84
3b2f2976
XL
85impl Default for Subcommand {
86 fn default() -> Subcommand {
87 Subcommand::Build {
88 paths: vec![PathBuf::from("nowhere")],
89 }
90 }
91}
92
7453a54e
SL
93impl Flags {
94 pub fn parse(args: &[String]) -> Flags {
cc61c64b 95 let mut extra_help = String::new();
94b46f34
XL
96 let mut subcommand_help = format!(
97 "\
cc61c64b
XL
98Usage: x.py <subcommand> [options] [<paths>...]
99
100Subcommands:
101 build Compile either the compiler or libraries
2c00a5a8 102 check Compile either the compiler or libraries, using cargo check
cc61c64b
XL
103 test Build and run some test suites
104 bench Build and run some benchmarks
105 doc Build documentation
106 clean Clean out build directories
7cac9316
XL
107 dist Build distribution artifacts
108 install Install distribution artifacts
cc61c64b 109
94b46f34
XL
110To learn more about a subcommand, run `./x.py <subcommand> -h`"
111 );
cc61c64b 112
7453a54e 113 let mut opts = Options::new();
cc61c64b 114 // Options common to all subcommands
32a655c1
SL
115 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
116 opts.optflag("i", "incremental", "use incremental compilation");
7453a54e 117 opts.optopt("", "config", "TOML configuration file for build", "FILE");
c30ab7b3 118 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
7453a54e 119 opts.optmulti("", "host", "host targets to build", "HOST");
c30ab7b3 120 opts.optmulti("", "target", "target targets to build", "TARGET");
2c00a5a8 121 opts.optmulti("", "exclude", "build paths to exclude", "PATH");
8bb4bdeb 122 opts.optopt("", "on-fail", "command to run on failure", "CMD");
83c7162d 123 opts.optflag("", "dry-run", "dry run; don't build anything");
7453a54e 124 opts.optopt("", "stage", "stage to build", "N");
476ff2be 125 opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
c30ab7b3 126 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
7453a54e 127 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
7453a54e 128 opts.optflag("h", "help", "print this help message");
94b46f34
XL
129 opts.optopt(
130 "",
131 "warnings",
132 "if value is deny, will deny warnings, otherwise use default",
133 "VALUE",
134 );
0531ce1d 135 opts.optopt("", "error-format", "rustc error format", "FORMAT");
7453a54e 136
cc61c64b 137 // fn usage()
94b46f34
XL
138 let usage =
139 |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
140 println!("{}", opts.usage(subcommand_help));
141 if !extra_help.is_empty() {
142 println!("{}", extra_help);
143 }
144 process::exit(exit_code);
145 };
cc61c64b
XL
146
147 // We can't use getopt to parse the options until we have completed specifying which
148 // options are valid, but under the current implementation, some options are conditional on
149 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
150 // complete the definition of the options. Then we can use the getopt::Matches object from
151 // there on out.
94b46f34 152 let subcommand = args.iter().find(|&s| {
041b39d2 153 (s == "build")
94b46f34
XL
154 || (s == "check")
155 || (s == "test")
156 || (s == "bench")
157 || (s == "doc")
158 || (s == "clean")
159 || (s == "dist")
160 || (s == "install")
161 });
041b39d2 162 let subcommand = match subcommand {
cc61c64b
XL
163 Some(s) => s,
164 None => {
abe05a73
XL
165 // No or an invalid subcommand -- show the general usage and subcommand help
166 // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
167 // subcommand.
cc61c64b 168 println!("{}\n", subcommand_help);
abe05a73
XL
169 let exit_code = if args.is_empty() { 0 } else { 1 };
170 process::exit(exit_code);
cc61c64b
XL
171 }
172 };
c30ab7b3 173
cc61c64b
XL
174 // Some subcommands get extra options
175 match subcommand.as_str() {
94b46f34 176 "test" => {
7cac9316
XL
177 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
178 opts.optmulti("", "test-args", "extra arguments", "ARGS");
2c00a5a8
XL
179 opts.optmulti(
180 "",
181 "rustc-args",
182 "extra options to pass the compiler when running tests",
183 "ARGS",
184 );
83c7162d
XL
185 opts.optflag("", "no-doc", "do not run doc tests");
186 opts.optflag("", "doc", "only run doc tests");
94b46f34
XL
187 opts.optflag(
188 "",
189 "bless",
190 "update all stderr/stdout files of failing ui tests",
191 );
192 opts.optopt(
193 "",
194 "compare-mode",
195 "mode describing what file the actual ui output will be compared to",
196 "COMPARE MODE",
197 );
198 }
199 "bench" => {
200 opts.optmulti("", "test-args", "extra arguments", "ARGS");
201 }
202 "clean" => {
203 opts.optflag("", "all", "clean all build artifacts");
204 }
205 _ => {}
cc61c64b
XL
206 };
207
208 // Done specifying what options are possible, so do the getopts parsing
209 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
210 // Invalid argument/option format
211 println!("\n{}\n", e);
212 usage(1, &opts, &subcommand_help, &extra_help);
213 });
214 // Extra sanity check to make sure we didn't hit this crazy corner case:
215 //
216 // ./x.py --frobulate clean build
217 // ^-- option ^ ^- actual subcommand
218 // \_ arg to option could be mistaken as subcommand
219 let mut pass_sanity_check = true;
220 match matches.free.get(0) {
221 Some(check_subcommand) => {
041b39d2 222 if check_subcommand != subcommand {
cc61c64b
XL
223 pass_sanity_check = false;
224 }
94b46f34 225 }
cc61c64b
XL
226 None => {
227 pass_sanity_check = false;
228 }
229 }
230 if !pass_sanity_check {
231 println!("{}\n", subcommand_help);
94b46f34
XL
232 println!(
233 "Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
234 You may need to move some options to after the subcommand.\n"
235 );
cc61c64b
XL
236 process::exit(1);
237 }
238 // Extra help text for some commands
239 match subcommand.as_str() {
240 "build" => {
94b46f34
XL
241 subcommand_help.push_str(
242 "\n
c30ab7b3 243Arguments:
cc61c64b
XL
244 This subcommand accepts a number of paths to directories to the crates
245 and/or artifacts to compile. For example:
c30ab7b3
SL
246
247 ./x.py build src/libcore
cc61c64b 248 ./x.py build src/libcore src/libproc_macro
c30ab7b3
SL
249 ./x.py build src/libstd --stage 1
250
251 If no arguments are passed then the complete artifacts for that stage are
252 also compiled.
253
254 ./x.py build
255 ./x.py build --stage 1
256
041b39d2
XL
257 For a quick build of a usable compiler, you can pass:
258
259 ./x.py build --stage 1 src/libtest
c30ab7b3 260
041b39d2
XL
261 This will first build everything once (like --stage 0 without further
262 arguments would), and then use the compiler built in stage 0 to build
263 src/libtest and its dependencies.
94b46f34
XL
264 Once this is done, build/$ARCH/stage1 contains a usable compiler.",
265 );
2c00a5a8
XL
266 }
267 "check" => {
94b46f34
XL
268 subcommand_help.push_str(
269 "\n
2c00a5a8
XL
270Arguments:
271 This subcommand accepts a number of paths to directories to the crates
272 and/or artifacts to compile. For example:
273
274 ./x.py check src/libcore
275 ./x.py check src/libcore src/libproc_macro
276
277 If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
278 also that since we use `cargo check`, by default this will automatically enable incremental
279 compilation, so there's no need to pass it separately, though it won't hurt. We also completely
280 ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
94b46f34
XL
281 the compiler.",
282 );
cc61c64b
XL
283 }
284 "test" => {
94b46f34
XL
285 subcommand_help.push_str(
286 "\n
c30ab7b3 287Arguments:
cc61c64b
XL
288 This subcommand accepts a number of paths to directories to tests that
289 should be compiled and run. For example:
c30ab7b3
SL
290
291 ./x.py test src/test/run-pass
c30ab7b3
SL
292 ./x.py test src/libstd --test-args hash_map
293 ./x.py test src/libstd --stage 0
94b46f34
XL
294 ./x.py test src/test/ui --bless
295 ./x.py test src/test/ui --compare-mode nll
c30ab7b3
SL
296
297 If no arguments are passed then the complete artifacts for that stage are
298 compiled and tested.
299
300 ./x.py test
94b46f34
XL
301 ./x.py test --stage 1",
302 );
cc61c64b
XL
303 }
304 "doc" => {
94b46f34
XL
305 subcommand_help.push_str(
306 "\n
c30ab7b3 307Arguments:
cc61c64b
XL
308 This subcommand accepts a number of paths to directories of documentation
309 to build. For example:
c30ab7b3
SL
310
311 ./x.py doc src/doc/book
312 ./x.py doc src/doc/nomicon
cc61c64b 313 ./x.py doc src/doc/book src/libstd
c30ab7b3
SL
314
315 If no arguments are passed then everything is documented:
316
317 ./x.py doc
94b46f34
XL
318 ./x.py doc --stage 1",
319 );
c30ab7b3 320 }
94b46f34 321 _ => {}
7453a54e 322 };
cc61c64b 323 // Get any optional paths which occur after the subcommand
94b46f34
XL
324 let paths = matches.free[1..]
325 .iter()
326 .map(|p| p.into())
327 .collect::<Vec<PathBuf>>();
cc61c64b 328
7cac9316
XL
329 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
330 if fs::metadata("config.toml").is_ok() {
331 Some(PathBuf::from("config.toml"))
332 } else {
333 None
334 }
335 });
cc61c64b 336
ea8adc8c 337 // All subcommands except `clean` can have an optional "Available paths" section
cc61c64b 338 if matches.opt_present("verbose") {
3b2f2976
XL
339 let config = Config::parse(&["build".to_string()]);
340 let mut build = Build::new(config);
cc61c64b 341 metadata::build(&mut build);
3b2f2976
XL
342
343 let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
344 extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
ea8adc8c 345 } else if subcommand.as_str() != "clean" {
94b46f34
XL
346 extra_help.push_str(
347 format!(
348 "Run `./x.py {} -h -v` to see a list of available paths.",
349 subcommand
350 ).as_str(),
351 );
cc61c64b 352 }
c30ab7b3 353
cc61c64b
XL
354 // User passed in -h/--help?
355 if matches.opt_present("help") {
356 usage(0, &opts, &subcommand_help, &extra_help);
357 }
c30ab7b3 358
cc61c64b 359 let cmd = match subcommand.as_str() {
94b46f34
XL
360 "build" => Subcommand::Build { paths: paths },
361 "check" => Subcommand::Check { paths: paths },
362 "test" => Subcommand::Test {
363 paths,
364 bless: matches.opt_present("bless"),
365 compare_mode: matches.opt_str("compare-mode"),
366 test_args: matches.opt_strs("test-args"),
367 rustc_args: matches.opt_strs("rustc-args"),
368 fail_fast: !matches.opt_present("no-fail-fast"),
369 doc_tests: if matches.opt_present("doc") {
370 DocTests::Only
371 } else if matches.opt_present("no-doc") {
372 DocTests::No
373 } else {
374 DocTests::Yes
375 },
376 },
377 "bench" => Subcommand::Bench {
378 paths,
379 test_args: matches.opt_strs("test-args"),
380 },
381 "doc" => Subcommand::Doc { paths: paths },
c30ab7b3 382 "clean" => {
cc61c64b 383 if paths.len() > 0 {
ea8adc8c 384 println!("\nclean does not take a path argument\n");
cc61c64b 385 usage(1, &opts, &subcommand_help, &extra_help);
c30ab7b3 386 }
ea8adc8c
XL
387
388 Subcommand::Clean {
389 all: matches.opt_present("all"),
390 }
c30ab7b3 391 }
94b46f34
XL
392 "dist" => Subcommand::Dist { paths },
393 "install" => Subcommand::Install { paths },
cc61c64b
XL
394 _ => {
395 usage(1, &opts, &subcommand_help, &extra_help);
c30ab7b3
SL
396 }
397 };
398
7453a54e 399 Flags {
cc61c64b 400 verbose: matches.opt_count("verbose"),
83c7162d
XL
401 stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
402 dry_run: matches.opt_present("dry-run"),
cc61c64b 403 on_fail: matches.opt_str("on-fail"),
0531ce1d 404 rustc_error_format: matches.opt_str("error-format"),
cc61c64b 405 keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
3b2f2976 406 host: split(matches.opt_strs("host"))
94b46f34
XL
407 .into_iter()
408 .map(|x| INTERNER.intern_string(x))
409 .collect::<Vec<_>>(),
3b2f2976 410 target: split(matches.opt_strs("target"))
94b46f34
XL
411 .into_iter()
412 .map(|x| INTERNER.intern_string(x))
413 .collect::<Vec<_>>(),
7453a54e 414 config: cfg_file,
cc61c64b 415 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
3b2f2976 416 cmd,
cc61c64b 417 incremental: matches.opt_present("incremental"),
2c00a5a8 418 exclude: split(matches.opt_strs("exclude"))
94b46f34
XL
419 .into_iter()
420 .map(|p| p.into())
421 .collect::<Vec<_>>(),
83c7162d 422 warnings: matches.opt_str("warnings").map(|v| v == "deny"),
7453a54e
SL
423 }
424 }
425}
426
c30ab7b3
SL
427impl Subcommand {
428 pub fn test_args(&self) -> Vec<&str> {
429 match *self {
94b46f34
XL
430 Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
431 test_args
432 .iter()
433 .flat_map(|s| s.split_whitespace())
434 .collect()
c30ab7b3
SL
435 }
436 _ => Vec::new(),
437 }
7453a54e 438 }
7cac9316 439
2c00a5a8
XL
440 pub fn rustc_args(&self) -> Vec<&str> {
441 match *self {
94b46f34
XL
442 Subcommand::Test { ref rustc_args, .. } => rustc_args
443 .iter()
444 .flat_map(|s| s.split_whitespace())
445 .collect(),
2c00a5a8
XL
446 _ => Vec::new(),
447 }
448 }
449
041b39d2 450 pub fn fail_fast(&self) -> bool {
7cac9316 451 match *self {
041b39d2 452 Subcommand::Test { fail_fast, .. } => fail_fast,
7cac9316
XL
453 _ => false,
454 }
455 }
0531ce1d 456
83c7162d 457 pub fn doc_tests(&self) -> DocTests {
0531ce1d
XL
458 match *self {
459 Subcommand::Test { doc_tests, .. } => doc_tests,
83c7162d 460 _ => DocTests::Yes,
0531ce1d
XL
461 }
462 }
94b46f34
XL
463
464 pub fn bless(&self) -> bool {
465 match *self {
466 Subcommand::Test { bless, .. } => bless,
467 _ => false,
468 }
469 }
470
471 pub fn compare_mode(&self) -> Option<&str> {
472 match *self {
473 Subcommand::Test {
474 ref compare_mode, ..
475 } => compare_mode.as_ref().map(|s| &s[..]),
476 _ => None,
477 }
478 }
7453a54e 479}
32a655c1
SL
480
481fn split(s: Vec<String>) -> Vec<String> {
94b46f34
XL
482 s.iter()
483 .flat_map(|s| s.split(','))
484 .map(|s| s.to_string())
485 .collect()
32a655c1 486}