a270b8014c
This commit is a rewrite of the user-facing interface to the rustbuild build system. The intention here is to make it much easier to compile/test the project without having to remember weird rule names and such. An overall view of the new interface is: # build everything ./x.py build # document everyting ./x.py doc # test everything ./x.py test # test libstd ./x.py test src/libstd # build libcore stage0 ./x.py build src/libcore --stage 0 # run stage1 run-pass tests ./x.py test src/test/run-pass --stage 1 The `src/bootstrap/bootstrap.py` script is now aliased as a top-level `x.py` script. This `x` was chosen to be both short and easily tab-completable (no collisions in that namespace!). The build system now accepts a "subcommand" of what to do next, the main ones being build/doc/test. Each subcommand then receives an optional list of arguments. These arguments are paths in the source repo of what to work with. That is, if you want to test a directory, you just pass that directory as an argument. The purpose of this rewrite is to do away with all of the arcane renames like "rpass" is the "run-pass" suite, "cfail" is the "compile-fail" suite, etc. By simply working with directories and files it's much more intuitive of how to run a test (just pass it as an argument). The rustbuild step/dependency management was also rewritten along the way to make this easy to work with and define, but that's largely just a refactoring of what was there before. The *intention* is that this support is extended for arbitrary files (e.g. `src/test/run-pass/my-test-case.rs`), but that isn't quite implemented just yet. Instead directories work for now but we can follow up with stricter path filtering logic to plumb through all the arguments.
269 lines
8.2 KiB
Rust
269 lines
8.2 KiB
Rust
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
//! Command-line interface of the rustbuild build system.
|
|
//!
|
|
//! This module implements the command-line parsing of the build system which
|
|
//! has various flags to configure how it's run.
|
|
|
|
use std::env;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
use std::process;
|
|
|
|
use getopts::{Matches, Options};
|
|
|
|
use Build;
|
|
use config::Config;
|
|
use metadata;
|
|
use step;
|
|
|
|
/// Deserialized version of all flags for this compile.
|
|
pub struct Flags {
|
|
pub verbose: bool,
|
|
pub stage: Option<u32>,
|
|
pub build: String,
|
|
pub host: Vec<String>,
|
|
pub target: Vec<String>,
|
|
pub config: Option<PathBuf>,
|
|
pub src: Option<PathBuf>,
|
|
pub jobs: Option<u32>,
|
|
pub cmd: Subcommand,
|
|
}
|
|
|
|
pub enum Subcommand {
|
|
Build {
|
|
paths: Vec<PathBuf>,
|
|
},
|
|
Doc {
|
|
paths: Vec<PathBuf>,
|
|
},
|
|
Test {
|
|
paths: Vec<PathBuf>,
|
|
test_args: Vec<String>,
|
|
},
|
|
Clean,
|
|
Dist {
|
|
install: bool,
|
|
},
|
|
}
|
|
|
|
impl Flags {
|
|
pub fn parse(args: &[String]) -> Flags {
|
|
let mut opts = Options::new();
|
|
opts.optflag("v", "verbose", "use verbose output");
|
|
opts.optopt("", "config", "TOML configuration file for build", "FILE");
|
|
opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
|
|
opts.optmulti("", "host", "host targets to build", "HOST");
|
|
opts.optmulti("", "target", "target targets to build", "TARGET");
|
|
opts.optopt("", "stage", "stage to build", "N");
|
|
opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
|
|
opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
|
|
opts.optflag("h", "help", "print this help message");
|
|
|
|
let usage = |n, opts: &Options| -> ! {
|
|
let command = args.get(0).map(|s| &**s);
|
|
let brief = format!("Usage: x.py {} [options] [<args>...]",
|
|
command.unwrap_or("<command>"));
|
|
|
|
println!("{}", opts.usage(&brief));
|
|
match command {
|
|
Some("build") => {
|
|
println!("\
|
|
Arguments:
|
|
This subcommand accepts a number of positional arguments of directories to
|
|
the crates and/or artifacts to compile. For example:
|
|
|
|
./x.py build src/libcore
|
|
./x.py build src/libproc_macro
|
|
./x.py build src/libstd --stage 1
|
|
|
|
If no arguments are passed then the complete artifacts for that stage are
|
|
also compiled.
|
|
|
|
./x.py build
|
|
./x.py build --stage 1
|
|
|
|
For a quick build with a usable compile, you can pass:
|
|
|
|
./x.py build --stage 1 src/libtest
|
|
");
|
|
}
|
|
|
|
Some("test") => {
|
|
println!("\
|
|
Arguments:
|
|
This subcommand accepts a number of positional arguments of directories to
|
|
tests that should be compiled and run. For example:
|
|
|
|
./x.py test src/test/run-pass
|
|
./x.py test src/test/run-pass/assert-*
|
|
./x.py test src/libstd --test-args hash_map
|
|
./x.py test src/libstd --stage 0
|
|
|
|
If no arguments are passed then the complete artifacts for that stage are
|
|
compiled and tested.
|
|
|
|
./x.py test
|
|
./x.py test --stage 1
|
|
");
|
|
}
|
|
|
|
Some("doc") => {
|
|
println!("\
|
|
Arguments:
|
|
This subcommand accepts a number of positional arguments of directories of
|
|
documentation to build. For example:
|
|
|
|
./x.py doc src/doc/book
|
|
./x.py doc src/doc/nomicon
|
|
./x.py doc src/libstd
|
|
|
|
If no arguments are passed then everything is documented:
|
|
|
|
./x.py doc
|
|
./x.py doc --stage 1
|
|
");
|
|
}
|
|
|
|
_ => {}
|
|
}
|
|
|
|
if let Some(command) = command {
|
|
if command == "build" ||
|
|
command == "dist" ||
|
|
command == "doc" ||
|
|
command == "test" ||
|
|
command == "clean" {
|
|
println!("Available invocations:");
|
|
if args.iter().any(|a| a == "-v") {
|
|
let flags = Flags::parse(&["build".to_string()]);
|
|
let mut config = Config::default();
|
|
config.build = flags.build.clone();
|
|
let mut build = Build::new(flags, config);
|
|
metadata::build(&mut build);
|
|
step::build_rules(&build).print_help(command);
|
|
} else {
|
|
println!(" ... elided, run `./x.py {} -h -v` to see",
|
|
command);
|
|
}
|
|
|
|
println!("");
|
|
}
|
|
}
|
|
|
|
println!("\
|
|
Subcommands:
|
|
build Compile either the compiler or libraries
|
|
test Build and run some test suites
|
|
doc Build documentation
|
|
clean Clean out build directories
|
|
dist Build and/or install distribution artifacts
|
|
|
|
To learn more about a subcommand, run `./x.py <command> -h`
|
|
");
|
|
|
|
process::exit(n);
|
|
};
|
|
if args.len() == 0 {
|
|
println!("a command must be passed");
|
|
usage(1, &opts);
|
|
}
|
|
let parse = |opts: &Options| {
|
|
let m = opts.parse(&args[1..]).unwrap_or_else(|e| {
|
|
println!("failed to parse options: {}", e);
|
|
usage(1, opts);
|
|
});
|
|
if m.opt_present("h") {
|
|
usage(0, opts);
|
|
}
|
|
return m
|
|
};
|
|
|
|
let cwd = t!(env::current_dir());
|
|
let remaining_as_path = |m: &Matches| {
|
|
m.free.iter().map(|p| cwd.join(p)).collect::<Vec<_>>()
|
|
};
|
|
|
|
let m: Matches;
|
|
let cmd = match &args[0][..] {
|
|
"build" => {
|
|
m = parse(&opts);
|
|
Subcommand::Build { paths: remaining_as_path(&m) }
|
|
}
|
|
"doc" => {
|
|
m = parse(&opts);
|
|
Subcommand::Doc { paths: remaining_as_path(&m) }
|
|
}
|
|
"test" => {
|
|
opts.optmulti("", "test-args", "extra arguments", "ARGS");
|
|
m = parse(&opts);
|
|
Subcommand::Test {
|
|
paths: remaining_as_path(&m),
|
|
test_args: m.opt_strs("test-args"),
|
|
}
|
|
}
|
|
"clean" => {
|
|
m = parse(&opts);
|
|
if m.free.len() > 0 {
|
|
println!("clean takes no arguments");
|
|
usage(1, &opts);
|
|
}
|
|
Subcommand::Clean
|
|
}
|
|
"dist" => {
|
|
opts.optflag("", "install", "run installer as well");
|
|
m = parse(&opts);
|
|
Subcommand::Dist {
|
|
install: m.opt_present("install"),
|
|
}
|
|
}
|
|
cmd => {
|
|
println!("unknown command: {}", cmd);
|
|
usage(1, &opts);
|
|
}
|
|
};
|
|
|
|
|
|
let cfg_file = m.opt_str("config").map(PathBuf::from).or_else(|| {
|
|
if fs::metadata("config.toml").is_ok() {
|
|
Some(PathBuf::from("config.toml"))
|
|
} else {
|
|
None
|
|
}
|
|
});
|
|
|
|
Flags {
|
|
verbose: m.opt_present("v"),
|
|
stage: m.opt_str("stage").map(|j| j.parse().unwrap()),
|
|
build: m.opt_str("build").unwrap_or_else(|| {
|
|
env::var("BUILD").unwrap()
|
|
}),
|
|
host: m.opt_strs("host"),
|
|
target: m.opt_strs("target"),
|
|
config: cfg_file,
|
|
src: m.opt_str("src").map(PathBuf::from),
|
|
jobs: m.opt_str("jobs").map(|j| j.parse().unwrap()),
|
|
cmd: cmd,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Subcommand {
|
|
pub fn test_args(&self) -> Vec<&str> {
|
|
match *self {
|
|
Subcommand::Test { ref test_args, .. } => {
|
|
test_args.iter().flat_map(|s| s.split_whitespace()).collect()
|
|
}
|
|
_ => Vec::new(),
|
|
}
|
|
}
|
|
}
|