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.
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.
11 //! Command-line interface of the rustbuild build system.
13 //! This module implements the command-line parsing of the build system which
14 //! has various flags to configure how it's run.
18 use std
::path
::PathBuf
;
28 /// Deserialized version of all flags for this compile.
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>,
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>,
41 pub incremental
: bool
,
45 pub fn verbose(&self) -> bool
{
49 pub fn very_verbose(&self) -> bool
{
63 test_args
: Vec
<String
>,
68 test_args
: Vec
<String
>,
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>...]
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
94 To learn more about a subcommand, run `./x.py <subcommand> -h`");
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");
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
);
117 process
::exit(exit_code
);
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
125 let mut possible_subcommands
= args
.iter().collect
::<Vec
<_
>>();
126 possible_subcommands
.retain(|&s
|
133 || (s
== "install"));
134 let subcommand
= match possible_subcommands
.first() {
137 // No subcommand -- show the general usage and subcommand help
138 println
!("{}\n", subcommand_help
);
143 // Some subcommands get extra options
144 match subcommand
.as_str() {
146 opts
.optflag("", "no-fail-fast", "Run all tests regardless of failure");
147 opts
.optmulti("", "test-args", "extra arguments", "ARGS");
149 "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); }
,
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
);
159 // Extra sanity check to make sure we didn't hit this crazy corner case:
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;
172 pass_sanity_check
= false;
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");
181 // Extra help text for some commands
182 match subcommand
.as_str() {
184 subcommand_help
.push_str("\n
186 This subcommand accepts a number of paths to directories to the crates
187 and/or artifacts to compile. For example:
189 ./x.py build src/libcore
190 ./x.py build src/libcore src/libproc_macro
191 ./x.py build src/libstd --stage 1
193 If no arguments are passed then the complete artifacts for that stage are
197 ./x.py build --stage 1
199 For a quick build with a usable compile, you can pass:
201 ./x.py build --stage 1 src/libtest");
204 subcommand_help
.push_str("\n
206 This subcommand accepts a number of paths to directories to tests that
207 should be compiled and run. For example:
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
213 If no arguments are passed then the complete artifacts for that stage are
217 ./x.py test --stage 1");
220 subcommand_help
.push_str("\n
222 This subcommand accepts a number of paths to directories of documentation
223 to build. For example:
225 ./x.py doc src/doc/book
226 ./x.py doc src/doc/nomicon
227 ./x.py doc src/doc/book src/libstd
229 If no arguments are passed then everything is documented:
232 ./x.py doc --stage 1");
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
<_
>>();
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"))
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());
260 extra_help
.push_str(format
!("Run `./x.py {} -h -v` to see a list of available paths.",
261 subcommand
).as_str());
264 // User passed in -h/--help?
265 if matches
.opt_present("help") {
266 usage(0, &opts
, &subcommand_help
, &extra_help
);
269 let cmd
= match subcommand
.as_str() {
271 Subcommand
::Build { paths: paths }
276 test_args
: matches
.opt_strs("test-args"),
277 no_fail_fast
: matches
.opt_present("no-fail-fast"),
283 test_args
: matches
.opt_strs("test-args"),
287 Subcommand
::Doc { paths: paths }
291 println
!("\nclean takes no arguments\n");
292 usage(1, &opts
, &subcommand_help
, &extra_help
);
302 Subcommand
::Install
{
307 usage(1, &opts
, &subcommand_help
, &extra_help
);
312 let mut stage
= matches
.opt_str("stage").map(|j
| j
.parse().unwrap());
314 if matches
.opt_present("incremental") {
321 verbose
: matches
.opt_count("verbose"),
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()
328 host
: split(matches
.opt_strs("host")),
329 target
: split(matches
.opt_strs("target")),
331 src
: matches
.opt_str("src").map(PathBuf
::from
),
332 jobs
: matches
.opt_str("jobs").map(|j
| j
.parse().unwrap()),
334 incremental
: matches
.opt_present("incremental"),
340 pub fn test_args(&self) -> Vec
<&str> {
342 Subcommand
::Test { ref test_args, .. }
|
343 Subcommand
::Bench { ref test_args, .. }
=> {
344 test_args
.iter().flat_map(|s
| s
.split_whitespace()).collect()
350 pub fn no_fail_fast(&self) -> bool
{
352 Subcommand
::Test { no_fail_fast, .. }
=> no_fail_fast
,
358 fn split(s
: Vec
<String
>) -> Vec
<String
> {
359 s
.iter().flat_map(|s
| s
.split('
,'
)).map(|s
| s
.to_string()).collect()