]> git.proxmox.com Git - rustc.git/blob - src/bootstrap/flags.rs
New upstream version 1.19.0+dfsg1
[rustc.git] / src / bootstrap / flags.rs
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
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
16 use std::env;
17 use std::fs;
18 use std::path::PathBuf;
19 use std::process;
20
21 use getopts::Options;
22
23 use Build;
24 use config::Config;
25 use metadata;
26 use step;
27
28 /// Deserialized version of all flags for this compile.
29 pub struct Flags {
30 pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
31 pub on_fail: Option<String>,
32 pub stage: Option<u32>,
33 pub keep_stage: Option<u32>,
34 pub build: String,
35 pub host: Vec<String>,
36 pub target: Vec<String>,
37 pub config: Option<PathBuf>,
38 pub src: Option<PathBuf>,
39 pub jobs: Option<u32>,
40 pub cmd: Subcommand,
41 pub incremental: bool,
42 }
43
44 impl 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 }
52 }
53
54 pub 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>,
64 no_fail_fast: bool,
65 },
66 Bench {
67 paths: Vec<PathBuf>,
68 test_args: Vec<String>,
69 },
70 Clean,
71 Dist {
72 paths: Vec<PathBuf>,
73 },
74 Install {
75 paths: Vec<PathBuf>,
76 },
77 }
78
79 impl Flags {
80 pub fn parse(args: &[String]) -> Flags {
81 let mut extra_help = String::new();
82 let mut subcommand_help = format!("\
83 Usage: x.py <subcommand> [options] [<paths>...]
84
85 Subcommands:
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
91 dist Build distribution artifacts
92 install Install distribution artifacts
93
94 To learn more about a subcommand, run `./x.py <subcommand> -h`");
95
96 let mut opts = Options::new();
97 // Options common to all subcommands
98 opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
99 opts.optflag("i", "incremental", "use incremental compilation");
100 opts.optopt("", "config", "TOML configuration file for build", "FILE");
101 opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
102 opts.optmulti("", "host", "host targets to build", "HOST");
103 opts.optmulti("", "target", "target targets to build", "TARGET");
104 opts.optopt("", "on-fail", "command to run on failure", "CMD");
105 opts.optopt("", "stage", "stage to build", "N");
106 opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
107 opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
108 opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
109 opts.optflag("h", "help", "print this help message");
110
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")
132 || (s == "dist")
133 || (s == "install"));
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 };
142
143 // Some subcommands get extra options
144 match subcommand.as_str() {
145 "test" => {
146 opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
147 opts.optmulti("", "test-args", "extra arguments", "ARGS");
148 },
149 "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
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
185 Arguments:
186 This subcommand accepts a number of paths to directories to the crates
187 and/or artifacts to compile. For example:
188
189 ./x.py build src/libcore
190 ./x.py build src/libcore src/libproc_macro
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
201 ./x.py build --stage 1 src/libtest");
202 }
203 "test" => {
204 subcommand_help.push_str("\n
205 Arguments:
206 This subcommand accepts a number of paths to directories to tests that
207 should be compiled and run. For example:
208
209 ./x.py test src/test/run-pass
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
217 ./x.py test --stage 1");
218 }
219 "doc" => {
220 subcommand_help.push_str("\n
221 Arguments:
222 This subcommand accepts a number of paths to directories of documentation
223 to build. For example:
224
225 ./x.py doc src/doc/book
226 ./x.py doc src/doc/nomicon
227 ./x.py doc src/doc/book src/libstd
228
229 If no arguments are passed then everything is documented:
230
231 ./x.py doc
232 ./x.py doc --stage 1");
233 }
234 _ => { }
235 };
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
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 });
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()]);
251 let mut config = Config::parse(&flags.build, cfg_file.clone());
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());
258 }
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 }
263
264 // User passed in -h/--help?
265 if matches.opt_present("help") {
266 usage(0, &opts, &subcommand_help, &extra_help);
267 }
268
269 let cmd = match subcommand.as_str() {
270 "build" => {
271 Subcommand::Build { paths: paths }
272 }
273 "test" => {
274 Subcommand::Test {
275 paths: paths,
276 test_args: matches.opt_strs("test-args"),
277 no_fail_fast: matches.opt_present("no-fail-fast"),
278 }
279 }
280 "bench" => {
281 Subcommand::Bench {
282 paths: paths,
283 test_args: matches.opt_strs("test-args"),
284 }
285 }
286 "doc" => {
287 Subcommand::Doc { paths: paths }
288 }
289 "clean" => {
290 if paths.len() > 0 {
291 println!("\nclean takes no arguments\n");
292 usage(1, &opts, &subcommand_help, &extra_help);
293 }
294 Subcommand::Clean
295 }
296 "dist" => {
297 Subcommand::Dist {
298 paths: paths,
299 }
300 }
301 "install" => {
302 Subcommand::Install {
303 paths: paths,
304 }
305 }
306 _ => {
307 usage(1, &opts, &subcommand_help, &extra_help);
308 }
309 };
310
311
312 let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
313
314 if matches.opt_present("incremental") {
315 if stage.is_none() {
316 stage = Some(1);
317 }
318 }
319
320 Flags {
321 verbose: matches.opt_count("verbose"),
322 stage: stage,
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(|| {
326 env::var("BUILD").unwrap()
327 }),
328 host: split(matches.opt_strs("host")),
329 target: split(matches.opt_strs("target")),
330 config: cfg_file,
331 src: matches.opt_str("src").map(PathBuf::from),
332 jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
333 cmd: cmd,
334 incremental: matches.opt_present("incremental"),
335 }
336 }
337 }
338
339 impl Subcommand {
340 pub fn test_args(&self) -> Vec<&str> {
341 match *self {
342 Subcommand::Test { ref test_args, .. } |
343 Subcommand::Bench { ref test_args, .. } => {
344 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
345 }
346 _ => Vec::new(),
347 }
348 }
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 }
356 }
357
358 fn split(s: Vec<String>) -> Vec<String> {
359 s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
360 }