]> git.proxmox.com Git - rustc.git/blame - src/bootstrap/flags.rs
New upstream version 1.19.0+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
c30ab7b3 16use std::env;
7453a54e
SL
17use std::fs;
18use std::path::PathBuf;
19use std::process;
7453a54e 20
cc61c64b 21use getopts::Options;
c30ab7b3
SL
22
23use Build;
24use config::Config;
25use metadata;
26use step;
7453a54e 27
a7813a04 28/// Deserialized version of all flags for this compile.
7453a54e 29pub struct Flags {
32a655c1 30 pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
8bb4bdeb 31 pub on_fail: Option<String>,
7453a54e 32 pub stage: Option<u32>,
476ff2be 33 pub keep_stage: Option<u32>,
7453a54e 34 pub build: String,
c30ab7b3
SL
35 pub host: Vec<String>,
36 pub target: Vec<String>,
7453a54e
SL
37 pub config: Option<PathBuf>,
38 pub src: Option<PathBuf>,
39 pub jobs: Option<u32>,
c30ab7b3 40 pub cmd: Subcommand,
32a655c1
SL
41 pub incremental: bool,
42}
43
44impl Flags {
45 pub fn verbose(&self) -> bool {
46 self.verbose > 0
47 }
48
49 pub fn very_verbose(&self) -> bool {
50 self.verbose > 1
51 }
7453a54e
SL
52}
53
c30ab7b3
SL
54pub enum Subcommand {
55 Build {
56 paths: Vec<PathBuf>,
57 },
58 Doc {
59 paths: Vec<PathBuf>,
60 },
61 Test {
62 paths: Vec<PathBuf>,
63 test_args: Vec<String>,
7cac9316 64 no_fail_fast: bool,
c30ab7b3 65 },
476ff2be
SL
66 Bench {
67 paths: Vec<PathBuf>,
68 test_args: Vec<String>,
69 },
c30ab7b3
SL
70 Clean,
71 Dist {
32a655c1 72 paths: Vec<PathBuf>,
7cac9316
XL
73 },
74 Install {
75 paths: Vec<PathBuf>,
c30ab7b3 76 },
7453a54e
SL
77}
78
79impl Flags {
80 pub fn parse(args: &[String]) -> Flags {
cc61c64b
XL
81 let mut extra_help = String::new();
82 let mut subcommand_help = format!("\
83Usage: x.py <subcommand> [options] [<paths>...]
84
85Subcommands:
86 build Compile either the compiler or libraries
87 test Build and run some test suites
88 bench Build and run some benchmarks
89 doc Build documentation
90 clean Clean out build directories
7cac9316
XL
91 dist Build distribution artifacts
92 install Install distribution artifacts
cc61c64b
XL
93
94To learn more about a subcommand, run `./x.py <subcommand> -h`");
95
7453a54e 96 let mut opts = Options::new();
cc61c64b 97 // Options common to all subcommands
32a655c1
SL
98 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
99 opts.optflag("i", "incremental", "use incremental compilation");
7453a54e 100 opts.optopt("", "config", "TOML configuration file for build", "FILE");
c30ab7b3 101 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
7453a54e 102 opts.optmulti("", "host", "host targets to build", "HOST");
c30ab7b3 103 opts.optmulti("", "target", "target targets to build", "TARGET");
8bb4bdeb 104 opts.optopt("", "on-fail", "command to run on failure", "CMD");
7453a54e 105 opts.optopt("", "stage", "stage to build", "N");
476ff2be 106 opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
c30ab7b3 107 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
7453a54e 108 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
7453a54e
SL
109 opts.optflag("h", "help", "print this help message");
110
cc61c64b
XL
111 // fn usage()
112 let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
113 println!("{}", opts.usage(subcommand_help));
114 if !extra_help.is_empty() {
115 println!("{}", extra_help);
116 }
117 process::exit(exit_code);
118 };
119
120 // We can't use getopt to parse the options until we have completed specifying which
121 // options are valid, but under the current implementation, some options are conditional on
122 // the subcommand. Therefore we must manually identify the subcommand first, so that we can
123 // complete the definition of the options. Then we can use the getopt::Matches object from
124 // there on out.
125 let mut possible_subcommands = args.iter().collect::<Vec<_>>();
126 possible_subcommands.retain(|&s|
127 (s == "build")
128 || (s == "test")
129 || (s == "bench")
130 || (s == "doc")
131 || (s == "clean")
7cac9316
XL
132 || (s == "dist")
133 || (s == "install"));
cc61c64b
XL
134 let subcommand = match possible_subcommands.first() {
135 Some(s) => s,
136 None => {
137 // No subcommand -- show the general usage and subcommand help
138 println!("{}\n", subcommand_help);
139 process::exit(0);
140 }
141 };
c30ab7b3 142
cc61c64b
XL
143 // Some subcommands get extra options
144 match subcommand.as_str() {
7cac9316
XL
145 "test" => {
146 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
147 opts.optmulti("", "test-args", "extra arguments", "ARGS");
148 },
cc61c64b 149 "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
cc61c64b
XL
150 _ => { },
151 };
152
153 // Done specifying what options are possible, so do the getopts parsing
154 let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
155 // Invalid argument/option format
156 println!("\n{}\n", e);
157 usage(1, &opts, &subcommand_help, &extra_help);
158 });
159 // Extra sanity check to make sure we didn't hit this crazy corner case:
160 //
161 // ./x.py --frobulate clean build
162 // ^-- option ^ ^- actual subcommand
163 // \_ arg to option could be mistaken as subcommand
164 let mut pass_sanity_check = true;
165 match matches.free.get(0) {
166 Some(check_subcommand) => {
167 if &check_subcommand != subcommand {
168 pass_sanity_check = false;
169 }
170 },
171 None => {
172 pass_sanity_check = false;
173 }
174 }
175 if !pass_sanity_check {
176 println!("{}\n", subcommand_help);
177 println!("Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
178 You may need to move some options to after the subcommand.\n");
179 process::exit(1);
180 }
181 // Extra help text for some commands
182 match subcommand.as_str() {
183 "build" => {
184 subcommand_help.push_str("\n
c30ab7b3 185Arguments:
cc61c64b
XL
186 This subcommand accepts a number of paths to directories to the crates
187 and/or artifacts to compile. For example:
c30ab7b3
SL
188
189 ./x.py build src/libcore
cc61c64b 190 ./x.py build src/libcore src/libproc_macro
c30ab7b3
SL
191 ./x.py build src/libstd --stage 1
192
193 If no arguments are passed then the complete artifacts for that stage are
194 also compiled.
195
196 ./x.py build
197 ./x.py build --stage 1
198
199 For a quick build with a usable compile, you can pass:
200
cc61c64b
XL
201 ./x.py build --stage 1 src/libtest");
202 }
203 "test" => {
204 subcommand_help.push_str("\n
c30ab7b3 205Arguments:
cc61c64b
XL
206 This subcommand accepts a number of paths to directories to tests that
207 should be compiled and run. For example:
c30ab7b3
SL
208
209 ./x.py test src/test/run-pass
c30ab7b3
SL
210 ./x.py test src/libstd --test-args hash_map
211 ./x.py test src/libstd --stage 0
212
213 If no arguments are passed then the complete artifacts for that stage are
214 compiled and tested.
215
216 ./x.py test
cc61c64b
XL
217 ./x.py test --stage 1");
218 }
219 "doc" => {
220 subcommand_help.push_str("\n
c30ab7b3 221Arguments:
cc61c64b
XL
222 This subcommand accepts a number of paths to directories of documentation
223 to build. For example:
c30ab7b3
SL
224
225 ./x.py doc src/doc/book
226 ./x.py doc src/doc/nomicon
cc61c64b 227 ./x.py doc src/doc/book src/libstd
c30ab7b3
SL
228
229 If no arguments are passed then everything is documented:
230
231 ./x.py doc
cc61c64b 232 ./x.py doc --stage 1");
c30ab7b3 233 }
cc61c64b 234 _ => { }
7453a54e 235 };
cc61c64b
XL
236 // Get any optional paths which occur after the subcommand
237 let cwd = t!(env::current_dir());
238 let paths = matches.free[1..].iter().map(|p| cwd.join(p)).collect::<Vec<_>>();
239
7cac9316
XL
240 let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
241 if fs::metadata("config.toml").is_ok() {
242 Some(PathBuf::from("config.toml"))
243 } else {
244 None
245 }
246 });
cc61c64b
XL
247
248 // All subcommands can have an optional "Available paths" section
249 if matches.opt_present("verbose") {
250 let flags = Flags::parse(&["build".to_string()]);
7cac9316 251 let mut config = Config::parse(&flags.build, cfg_file.clone());
cc61c64b
XL
252 config.build = flags.build.clone();
253 let mut build = Build::new(flags, config);
254 metadata::build(&mut build);
255 let maybe_rules_help = step::build_rules(&build).get_help(subcommand);
256 if maybe_rules_help.is_some() {
257 extra_help.push_str(maybe_rules_help.unwrap().as_str());
c30ab7b3 258 }
cc61c64b
XL
259 } else {
260 extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
261 subcommand).as_str());
262 }
c30ab7b3 263
cc61c64b
XL
264 // User passed in -h/--help?
265 if matches.opt_present("help") {
266 usage(0, &opts, &subcommand_help, &extra_help);
267 }
c30ab7b3 268
cc61c64b 269 let cmd = match subcommand.as_str() {
c30ab7b3 270 "build" => {
cc61c64b 271 Subcommand::Build { paths: paths }
c30ab7b3
SL
272 }
273 "test" => {
c30ab7b3 274 Subcommand::Test {
cc61c64b
XL
275 paths: paths,
276 test_args: matches.opt_strs("test-args"),
7cac9316 277 no_fail_fast: matches.opt_present("no-fail-fast"),
c30ab7b3
SL
278 }
279 }
476ff2be 280 "bench" => {
476ff2be 281 Subcommand::Bench {
cc61c64b
XL
282 paths: paths,
283 test_args: matches.opt_strs("test-args"),
476ff2be
SL
284 }
285 }
cc61c64b
XL
286 "doc" => {
287 Subcommand::Doc { paths: paths }
288 }
c30ab7b3 289 "clean" => {
cc61c64b
XL
290 if paths.len() > 0 {
291 println!("\nclean takes no arguments\n");
292 usage(1, &opts, &subcommand_help, &extra_help);
c30ab7b3
SL
293 }
294 Subcommand::Clean
295 }
296 "dist" => {
c30ab7b3 297 Subcommand::Dist {
cc61c64b 298 paths: paths,
7cac9316
XL
299 }
300 }
301 "install" => {
302 Subcommand::Install {
303 paths: paths,
c30ab7b3
SL
304 }
305 }
cc61c64b
XL
306 _ => {
307 usage(1, &opts, &subcommand_help, &extra_help);
c30ab7b3
SL
308 }
309 };
310
7453a54e 311
cc61c64b 312 let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
32a655c1 313
cc61c64b 314 if matches.opt_present("incremental") {
32a655c1
SL
315 if stage.is_none() {
316 stage = Some(1);
317 }
318 }
319
7453a54e 320 Flags {
cc61c64b 321 verbose: matches.opt_count("verbose"),
32a655c1 322 stage: stage,
cc61c64b
XL
323 on_fail: matches.opt_str("on-fail"),
324 keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
325 build: matches.opt_str("build").unwrap_or_else(|| {
c30ab7b3
SL
326 env::var("BUILD").unwrap()
327 }),
cc61c64b
XL
328 host: split(matches.opt_strs("host")),
329 target: split(matches.opt_strs("target")),
7453a54e 330 config: cfg_file,
cc61c64b
XL
331 src: matches.opt_str("src").map(PathBuf::from),
332 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
c30ab7b3 333 cmd: cmd,
cc61c64b 334 incremental: matches.opt_present("incremental"),
7453a54e
SL
335 }
336 }
337}
338
c30ab7b3
SL
339impl Subcommand {
340 pub fn test_args(&self) -> Vec<&str> {
341 match *self {
476ff2be
SL
342 Subcommand::Test { ref test_args, .. } |
343 Subcommand::Bench { ref test_args, .. } => {
c30ab7b3
SL
344 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
345 }
346 _ => Vec::new(),
347 }
7453a54e 348 }
7cac9316
XL
349
350 pub fn no_fail_fast(&self) -> bool {
351 match *self {
352 Subcommand::Test { no_fail_fast, .. } => no_fail_fast,
353 _ => false,
354 }
355 }
7453a54e 356}
32a655c1
SL
357
358fn split(s: Vec<String>) -> Vec<String> {
359 s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
360}