]>
Commit | Line | Data |
---|---|---|
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 | 16 | use std::env; |
7453a54e SL |
17 | use std::fs; |
18 | use std::path::PathBuf; | |
19 | use std::process; | |
7453a54e | 20 | |
c30ab7b3 SL |
21 | use getopts::{Matches, Options}; |
22 | ||
23 | use Build; | |
24 | use config::Config; | |
25 | use metadata; | |
26 | use step; | |
7453a54e | 27 | |
a7813a04 | 28 | /// Deserialized version of all flags for this compile. |
7453a54e | 29 | pub 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 | ||
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 | } | |
7453a54e SL |
52 | } |
53 | ||
c30ab7b3 SL |
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 | }, | |
476ff2be SL |
65 | Bench { |
66 | paths: Vec<PathBuf>, | |
67 | test_args: Vec<String>, | |
68 | }, | |
c30ab7b3 SL |
69 | Clean, |
70 | Dist { | |
32a655c1 | 71 | paths: Vec<PathBuf>, |
c30ab7b3 SL |
72 | install: bool, |
73 | }, | |
7453a54e SL |
74 | } |
75 | ||
76 | impl Flags { | |
77 | pub fn parse(args: &[String]) -> Flags { | |
78 | let mut opts = Options::new(); | |
32a655c1 SL |
79 | opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)"); |
80 | opts.optflag("i", "incremental", "use incremental compilation"); | |
7453a54e | 81 | opts.optopt("", "config", "TOML configuration file for build", "FILE"); |
c30ab7b3 | 82 | opts.optopt("", "build", "build target of the stage0 compiler", "BUILD"); |
7453a54e | 83 | opts.optmulti("", "host", "host targets to build", "HOST"); |
c30ab7b3 | 84 | opts.optmulti("", "target", "target targets to build", "TARGET"); |
8bb4bdeb | 85 | opts.optopt("", "on-fail", "command to run on failure", "CMD"); |
7453a54e | 86 | opts.optopt("", "stage", "stage to build", "N"); |
476ff2be | 87 | opts.optopt("", "keep-stage", "stage to keep without recompiling", "N"); |
c30ab7b3 | 88 | opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); |
7453a54e | 89 | opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS"); |
7453a54e SL |
90 | opts.optflag("h", "help", "print this help message"); |
91 | ||
c30ab7b3 SL |
92 | let usage = |n, opts: &Options| -> ! { |
93 | let command = args.get(0).map(|s| &**s); | |
94 | let brief = format!("Usage: x.py {} [options] [<args>...]", | |
95 | command.unwrap_or("<command>")); | |
96 | ||
97 | println!("{}", opts.usage(&brief)); | |
98 | match command { | |
99 | Some("build") => { | |
100 | println!("\ | |
101 | Arguments: | |
102 | This subcommand accepts a number of positional arguments of directories to | |
103 | the crates and/or artifacts to compile. For example: | |
104 | ||
105 | ./x.py build src/libcore | |
106 | ./x.py build src/libproc_macro | |
107 | ./x.py build src/libstd --stage 1 | |
108 | ||
109 | If no arguments are passed then the complete artifacts for that stage are | |
110 | also compiled. | |
111 | ||
112 | ./x.py build | |
113 | ./x.py build --stage 1 | |
114 | ||
115 | For a quick build with a usable compile, you can pass: | |
116 | ||
117 | ./x.py build --stage 1 src/libtest | |
118 | "); | |
119 | } | |
120 | ||
121 | Some("test") => { | |
122 | println!("\ | |
123 | Arguments: | |
124 | This subcommand accepts a number of positional arguments of directories to | |
125 | tests that should be compiled and run. For example: | |
126 | ||
127 | ./x.py test src/test/run-pass | |
c30ab7b3 SL |
128 | ./x.py test src/libstd --test-args hash_map |
129 | ./x.py test src/libstd --stage 0 | |
130 | ||
131 | If no arguments are passed then the complete artifacts for that stage are | |
132 | compiled and tested. | |
133 | ||
134 | ./x.py test | |
135 | ./x.py test --stage 1 | |
136 | "); | |
137 | } | |
138 | ||
139 | Some("doc") => { | |
140 | println!("\ | |
141 | Arguments: | |
142 | This subcommand accepts a number of positional arguments of directories of | |
143 | documentation to build. For example: | |
144 | ||
145 | ./x.py doc src/doc/book | |
146 | ./x.py doc src/doc/nomicon | |
147 | ./x.py doc src/libstd | |
148 | ||
149 | If no arguments are passed then everything is documented: | |
150 | ||
151 | ./x.py doc | |
152 | ./x.py doc --stage 1 | |
153 | "); | |
154 | } | |
155 | ||
156 | _ => {} | |
157 | } | |
158 | ||
159 | if let Some(command) = command { | |
160 | if command == "build" || | |
161 | command == "dist" || | |
162 | command == "doc" || | |
163 | command == "test" || | |
476ff2be | 164 | command == "bench" || |
c30ab7b3 SL |
165 | command == "clean" { |
166 | println!("Available invocations:"); | |
167 | if args.iter().any(|a| a == "-v") { | |
168 | let flags = Flags::parse(&["build".to_string()]); | |
169 | let mut config = Config::default(); | |
170 | config.build = flags.build.clone(); | |
171 | let mut build = Build::new(flags, config); | |
172 | metadata::build(&mut build); | |
173 | step::build_rules(&build).print_help(command); | |
174 | } else { | |
175 | println!(" ... elided, run `./x.py {} -h -v` to see", | |
176 | command); | |
177 | } | |
178 | ||
179 | println!(""); | |
180 | } | |
181 | } | |
182 | ||
183 | println!("\ | |
184 | Subcommands: | |
185 | build Compile either the compiler or libraries | |
186 | test Build and run some test suites | |
476ff2be | 187 | bench Build and run some benchmarks |
c30ab7b3 SL |
188 | doc Build documentation |
189 | clean Clean out build directories | |
190 | dist Build and/or install distribution artifacts | |
191 | ||
192 | To learn more about a subcommand, run `./x.py <command> -h` | |
193 | "); | |
194 | ||
7453a54e SL |
195 | process::exit(n); |
196 | }; | |
c30ab7b3 SL |
197 | if args.len() == 0 { |
198 | println!("a command must be passed"); | |
199 | usage(1, &opts); | |
7453a54e | 200 | } |
c30ab7b3 SL |
201 | let parse = |opts: &Options| { |
202 | let m = opts.parse(&args[1..]).unwrap_or_else(|e| { | |
203 | println!("failed to parse options: {}", e); | |
204 | usage(1, opts); | |
205 | }); | |
206 | if m.opt_present("h") { | |
207 | usage(0, opts); | |
208 | } | |
209 | return m | |
210 | }; | |
211 | ||
212 | let cwd = t!(env::current_dir()); | |
213 | let remaining_as_path = |m: &Matches| { | |
214 | m.free.iter().map(|p| cwd.join(p)).collect::<Vec<_>>() | |
215 | }; | |
216 | ||
217 | let m: Matches; | |
218 | let cmd = match &args[0][..] { | |
219 | "build" => { | |
220 | m = parse(&opts); | |
221 | Subcommand::Build { paths: remaining_as_path(&m) } | |
222 | } | |
223 | "doc" => { | |
224 | m = parse(&opts); | |
225 | Subcommand::Doc { paths: remaining_as_path(&m) } | |
226 | } | |
227 | "test" => { | |
228 | opts.optmulti("", "test-args", "extra arguments", "ARGS"); | |
229 | m = parse(&opts); | |
230 | Subcommand::Test { | |
231 | paths: remaining_as_path(&m), | |
232 | test_args: m.opt_strs("test-args"), | |
233 | } | |
234 | } | |
476ff2be SL |
235 | "bench" => { |
236 | opts.optmulti("", "test-args", "extra arguments", "ARGS"); | |
237 | m = parse(&opts); | |
238 | Subcommand::Bench { | |
239 | paths: remaining_as_path(&m), | |
240 | test_args: m.opt_strs("test-args"), | |
241 | } | |
242 | } | |
c30ab7b3 SL |
243 | "clean" => { |
244 | m = parse(&opts); | |
245 | if m.free.len() > 0 { | |
246 | println!("clean takes no arguments"); | |
247 | usage(1, &opts); | |
248 | } | |
249 | Subcommand::Clean | |
250 | } | |
251 | "dist" => { | |
252 | opts.optflag("", "install", "run installer as well"); | |
253 | m = parse(&opts); | |
254 | Subcommand::Dist { | |
32a655c1 | 255 | paths: remaining_as_path(&m), |
c30ab7b3 SL |
256 | install: m.opt_present("install"), |
257 | } | |
258 | } | |
476ff2be | 259 | "--help" => usage(0, &opts), |
c30ab7b3 SL |
260 | cmd => { |
261 | println!("unknown command: {}", cmd); | |
262 | usage(1, &opts); | |
263 | } | |
264 | }; | |
265 | ||
7453a54e | 266 | |
7453a54e SL |
267 | let cfg_file = m.opt_str("config").map(PathBuf::from).or_else(|| { |
268 | if fs::metadata("config.toml").is_ok() { | |
269 | Some(PathBuf::from("config.toml")) | |
270 | } else { | |
271 | None | |
272 | } | |
273 | }); | |
274 | ||
32a655c1 SL |
275 | let mut stage = m.opt_str("stage").map(|j| j.parse().unwrap()); |
276 | ||
277 | let incremental = m.opt_present("i"); | |
278 | ||
279 | if incremental { | |
280 | if stage.is_none() { | |
281 | stage = Some(1); | |
282 | } | |
283 | } | |
284 | ||
7453a54e | 285 | Flags { |
32a655c1 SL |
286 | verbose: m.opt_count("v"), |
287 | stage: stage, | |
8bb4bdeb | 288 | on_fail: m.opt_str("on-fail"), |
476ff2be | 289 | keep_stage: m.opt_str("keep-stage").map(|j| j.parse().unwrap()), |
c30ab7b3 SL |
290 | build: m.opt_str("build").unwrap_or_else(|| { |
291 | env::var("BUILD").unwrap() | |
292 | }), | |
32a655c1 SL |
293 | host: split(m.opt_strs("host")), |
294 | target: split(m.opt_strs("target")), | |
7453a54e SL |
295 | config: cfg_file, |
296 | src: m.opt_str("src").map(PathBuf::from), | |
297 | jobs: m.opt_str("jobs").map(|j| j.parse().unwrap()), | |
c30ab7b3 | 298 | cmd: cmd, |
32a655c1 | 299 | incremental: incremental, |
7453a54e SL |
300 | } |
301 | } | |
302 | } | |
303 | ||
c30ab7b3 SL |
304 | impl Subcommand { |
305 | pub fn test_args(&self) -> Vec<&str> { | |
306 | match *self { | |
476ff2be SL |
307 | Subcommand::Test { ref test_args, .. } | |
308 | Subcommand::Bench { ref test_args, .. } => { | |
c30ab7b3 SL |
309 | test_args.iter().flat_map(|s| s.split_whitespace()).collect() |
310 | } | |
311 | _ => Vec::new(), | |
312 | } | |
7453a54e SL |
313 | } |
314 | } | |
32a655c1 SL |
315 | |
316 | fn split(s: Vec<String>) -> Vec<String> { | |
317 | s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect() | |
318 | } |