1
Fork 0

Auto merge of #48105 - Mark-Simulacrum:exclude-paths, r=alexcrichton

Implement excluding a build-step via --exclude

First step to fixing https://github.com/rust-lang/rust/issues/47911. This doesn't change any CI configuration, but implements what I believe necessary to make that feasible in rustbuild.

In theory this should be sufficient to allow someone to open a PR against .travis.yml and appveyor.yml which splits the Windows 32-bit tests and maybe the OS X tests into multiple builders (depending on what our cost-concerns are) to reduce runtimes.

r? @alexcrichton
cc @kennytm
This commit is contained in:
bors 2018-02-15 10:32:33 +00:00
commit c83fa5d91c
10 changed files with 452 additions and 335 deletions

View file

@ -95,7 +95,7 @@ pub struct RunConfig<'a> {
pub builder: &'a Builder<'a>, pub builder: &'a Builder<'a>,
pub host: Interned<String>, pub host: Interned<String>,
pub target: Interned<String>, pub target: Interned<String>,
pub path: Option<&'a Path>, pub path: PathBuf,
} }
struct StepDescription { struct StepDescription {
@ -105,6 +105,32 @@ struct StepDescription {
only_build: bool, only_build: bool,
should_run: fn(ShouldRun) -> ShouldRun, should_run: fn(ShouldRun) -> ShouldRun,
make_run: fn(RunConfig), make_run: fn(RunConfig),
name: &'static str,
}
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
struct PathSet {
set: BTreeSet<PathBuf>,
}
impl PathSet {
fn empty() -> PathSet {
PathSet { set: BTreeSet::new() }
}
fn one<P: Into<PathBuf>>(path: P) -> PathSet {
let mut set = BTreeSet::new();
set.insert(path.into());
PathSet { set }
}
fn has(&self, needle: &Path) -> bool {
self.set.iter().any(|p| p.ends_with(needle))
}
fn path(&self, builder: &Builder) -> PathBuf {
self.set.iter().next().unwrap_or(&builder.build.src).to_path_buf()
}
} }
impl StepDescription { impl StepDescription {
@ -116,10 +142,18 @@ impl StepDescription {
only_build: S::ONLY_BUILD, only_build: S::ONLY_BUILD,
should_run: S::should_run, should_run: S::should_run,
make_run: S::make_run, make_run: S::make_run,
name: unsafe { ::std::intrinsics::type_name::<S>() },
} }
} }
fn maybe_run(&self, builder: &Builder, path: Option<&Path>) { fn maybe_run(&self, builder: &Builder, pathset: &PathSet) {
if builder.config.exclude.iter().any(|e| pathset.has(e)) {
eprintln!("Skipping {:?} because it is excluded", pathset);
return;
} else if !builder.config.exclude.is_empty() {
eprintln!("{:?} not skipped for {:?} -- not in {:?}", pathset,
self.name, builder.config.exclude);
}
let build = builder.build; let build = builder.build;
let hosts = if self.only_build_targets || self.only_build { let hosts = if self.only_build_targets || self.only_build {
build.build_triple() build.build_triple()
@ -144,7 +178,7 @@ impl StepDescription {
for target in targets { for target in targets {
let run = RunConfig { let run = RunConfig {
builder, builder,
path, path: pathset.path(builder),
host: *host, host: *host,
target: *target, target: *target,
}; };
@ -157,19 +191,28 @@ impl StepDescription {
let should_runs = v.iter().map(|desc| { let should_runs = v.iter().map(|desc| {
(desc.should_run)(ShouldRun::new(builder)) (desc.should_run)(ShouldRun::new(builder))
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
// sanity checks on rules
for (desc, should_run) in v.iter().zip(&should_runs) {
assert!(!should_run.paths.is_empty(),
"{:?} should have at least one pathset", desc.name);
}
if paths.is_empty() { if paths.is_empty() {
for (desc, should_run) in v.iter().zip(should_runs) { for (desc, should_run) in v.iter().zip(should_runs) {
if desc.default && should_run.is_really_default { if desc.default && should_run.is_really_default {
desc.maybe_run(builder, None); for pathset in &should_run.paths {
desc.maybe_run(builder, pathset);
}
} }
} }
} else { } else {
for path in paths { for path in paths {
let mut attempted_run = false; let mut attempted_run = false;
for (desc, should_run) in v.iter().zip(&should_runs) { for (desc, should_run) in v.iter().zip(&should_runs) {
if should_run.run(path) { if let Some(pathset) = should_run.pathset_for_path(path) {
attempted_run = true; attempted_run = true;
desc.maybe_run(builder, Some(path)); desc.maybe_run(builder, pathset);
} }
} }
@ -185,7 +228,7 @@ impl StepDescription {
pub struct ShouldRun<'a> { pub struct ShouldRun<'a> {
pub builder: &'a Builder<'a>, pub builder: &'a Builder<'a>,
// use a BTreeSet to maintain sort order // use a BTreeSet to maintain sort order
paths: BTreeSet<PathBuf>, paths: BTreeSet<PathSet>,
// If this is a default rule, this is an additional constraint placed on // If this is a default rule, this is an additional constraint placed on
// it's run. Generally something like compiler docs being enabled. // it's run. Generally something like compiler docs being enabled.
@ -206,25 +249,46 @@ impl<'a> ShouldRun<'a> {
self self
} }
// Unlike `krate` this will create just one pathset. As such, it probably shouldn't actually
// ever be used, but as we transition to having all rules properly handle passing krate(...) by
// actually doing something different for every crate passed.
pub fn all_krates(mut self, name: &str) -> Self {
let mut set = BTreeSet::new();
for krate in self.builder.in_tree_crates(name) {
set.insert(PathBuf::from(&krate.path));
}
self.paths.insert(PathSet { set });
self
}
pub fn krate(mut self, name: &str) -> Self { pub fn krate(mut self, name: &str) -> Self {
for (_, krate_path) in self.builder.crates(name) { for krate in self.builder.in_tree_crates(name) {
self.paths.insert(PathBuf::from(krate_path)); self.paths.insert(PathSet::one(&krate.path));
} }
self self
} }
pub fn path(mut self, path: &str) -> Self { // single, non-aliased path
self.paths.insert(PathBuf::from(path)); pub fn path(self, path: &str) -> Self {
self.paths(&[path])
}
// multiple aliases for the same job
pub fn paths(mut self, paths: &[&str]) -> Self {
self.paths.insert(PathSet {
set: paths.iter().map(PathBuf::from).collect(),
});
self self
} }
// allows being more explicit about why should_run in Step returns the value passed to it // allows being more explicit about why should_run in Step returns the value passed to it
pub fn never(self) -> ShouldRun<'a> { pub fn never(mut self) -> ShouldRun<'a> {
self.paths.insert(PathSet::empty());
self self
} }
fn run(&self, path: &Path) -> bool { fn pathset_for_path(&self, path: &Path) -> Option<&PathSet> {
self.paths.iter().any(|p| path.ends_with(p)) self.paths.iter().find(|pathset| pathset.has(path))
} }
} }
@ -254,19 +318,23 @@ impl<'a> Builder<'a> {
tool::RustInstaller, tool::Cargo, tool::Rls, tool::Rustdoc, tool::Clippy, tool::RustInstaller, tool::Cargo, tool::Rls, tool::Rustdoc, tool::Clippy,
native::Llvm, tool::Rustfmt, tool::Miri), native::Llvm, tool::Rustfmt, tool::Miri),
Kind::Check => describe!(check::Std, check::Test, check::Rustc), Kind::Check => describe!(check::Std, check::Test, check::Rustc),
Kind::Test => describe!(test::Tidy, test::Bootstrap, test::DefaultCompiletest, Kind::Test => describe!(test::Tidy, test::Bootstrap, test::Ui, test::RunPass,
test::HostCompiletest, test::Crate, test::CrateLibrustc, test::Rustdoc, test::CompileFail, test::ParseFail, test::RunFail, test::RunPassValgrind,
test::Linkcheck, test::Cargotest, test::Cargo, test::Rls, test::Docs, test::MirOpt, test::Codegen, test::CodegenUnits, test::Incremental, test::Debuginfo,
test::ErrorIndex, test::Distcheck, test::Rustfmt, test::Miri, test::Clippy, test::UiFullDeps, test::RunPassFullDeps, test::RunFailFullDeps,
test::RustdocJS, test::RustdocTheme), test::CompileFailFullDeps, test::IncrementalFullDeps, test::Rustdoc, test::Pretty,
test::RunPassPretty, test::RunFailPretty, test::RunPassValgrindPretty,
test::RunPassFullDepsPretty, test::RunFailFullDepsPretty, test::RunMake,
test::Crate, test::CrateLibrustc, test::Rustdoc, test::Linkcheck, test::Cargotest,
test::Cargo, test::Rls, test::Docs, test::ErrorIndex, test::Distcheck,
test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme),
Kind::Bench => describe!(test::Crate, test::CrateLibrustc), Kind::Bench => describe!(test::Crate, test::CrateLibrustc),
Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook, Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook,
doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon, doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon,
doc::Reference, doc::Rustdoc, doc::RustByExample, doc::CargoBook), doc::Reference, doc::Rustdoc, doc::RustByExample, doc::CargoBook),
Kind::Dist => describe!(dist::Docs, dist::Mingw, dist::Rustc, dist::DebuggerScripts, Kind::Dist => describe!(dist::Docs, dist::Mingw, dist::Rustc, dist::DebuggerScripts,
dist::Std, dist::Analysis, dist::Src, dist::PlainSourceTarball, dist::Cargo, dist::Std, dist::Analysis, dist::Src, dist::PlainSourceTarball, dist::Cargo,
dist::Rls, dist::Rustfmt, dist::Extended, dist::HashSign, dist::Rls, dist::Rustfmt, dist::Extended, dist::HashSign),
dist::DontDistWithMiriEnabled),
Kind::Install => describe!(install::Docs, install::Std, install::Cargo, install::Rls, Kind::Install => describe!(install::Docs, install::Std, install::Cargo, install::Rls,
install::Rustfmt, install::Analysis, install::Src, install::Rustc), install::Rustfmt, install::Analysis, install::Src, install::Rustc),
} }
@ -297,8 +365,10 @@ impl<'a> Builder<'a> {
should_run = (desc.should_run)(should_run); should_run = (desc.should_run)(should_run);
} }
let mut help = String::from("Available paths:\n"); let mut help = String::from("Available paths:\n");
for path in should_run.paths { for pathset in should_run.paths {
help.push_str(format!(" ./x.py {} {}\n", subcommand, path.display()).as_str()); for path in pathset.set {
help.push_str(format!(" ./x.py {} {}\n", subcommand, path.display()).as_str());
}
} }
Some(help) Some(help)
} }
@ -323,6 +393,12 @@ impl<'a> Builder<'a> {
stack: RefCell::new(Vec::new()), stack: RefCell::new(Vec::new()),
}; };
if kind == Kind::Dist {
assert!(!build.config.test_miri, "Do not distribute with miri enabled.\n\
The distributed libraries would include all MIR (increasing binary size).
The distributed MIR would include validation statements.");
}
StepDescription::run(&Builder::get_step_descriptions(builder.kind), &builder, paths); StepDescription::run(&Builder::get_step_descriptions(builder.kind), &builder, paths);
} }

View file

@ -26,7 +26,7 @@ impl Step for Std {
const DEFAULT: bool = true; const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/libstd").krate("std") run.all_krates("std")
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
@ -67,7 +67,7 @@ impl Step for Rustc {
const DEFAULT: bool = true; const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/librustc").krate("rustc-main") run.all_krates("rustc-main")
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
@ -114,7 +114,7 @@ impl Step for Test {
const DEFAULT: bool = true; const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/libtest").krate("test") run.all_krates("test")
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {

View file

@ -48,7 +48,7 @@ impl Step for Std {
const DEFAULT: bool = true; const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/libstd").krate("std") run.all_krates("std")
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
@ -320,7 +320,7 @@ impl Step for Test {
const DEFAULT: bool = true; const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/libtest").krate("test") run.all_krates("test")
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
@ -436,7 +436,7 @@ impl Step for Rustc {
const DEFAULT: bool = true; const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/librustc").krate("rustc-main") run.all_krates("rustc-main")
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
@ -593,7 +593,7 @@ impl Step for CodegenBackend {
const DEFAULT: bool = true; const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/librustc_trans") run.all_krates("rustc_trans")
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
@ -828,7 +828,7 @@ impl Step for Assemble {
type Output = Compiler; type Output = Compiler;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/rustc") run.all_krates("rustc-main")
} }
/// Prepare a new compiler from the artifacts in `stage` /// Prepare a new compiler from the artifacts in `stage`

View file

@ -56,6 +56,7 @@ pub struct Config {
pub sanitizers: bool, pub sanitizers: bool,
pub profiler: bool, pub profiler: bool,
pub ignore_git: bool, pub ignore_git: bool,
pub exclude: Vec<PathBuf>,
pub run_host_only: bool, pub run_host_only: bool,
@ -311,6 +312,7 @@ impl Config {
let flags = Flags::parse(&args); let flags = Flags::parse(&args);
let file = flags.config.clone(); let file = flags.config.clone();
let mut config = Config::default(); let mut config = Config::default();
config.exclude = flags.exclude;
config.llvm_enabled = true; config.llvm_enabled = true;
config.llvm_optimize = true; config.llvm_optimize = true;
config.llvm_version_check = true; config.llvm_version_check = true;

View file

@ -1233,31 +1233,6 @@ impl Step for Rustfmt {
} }
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct DontDistWithMiriEnabled;
impl Step for DontDistWithMiriEnabled {
type Output = PathBuf;
const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
let build_miri = run.builder.build.config.test_miri;
run.default_condition(build_miri)
}
fn make_run(run: RunConfig) {
run.builder.ensure(DontDistWithMiriEnabled);
}
fn run(self, _: &Builder) -> PathBuf {
panic!("Do not distribute with miri enabled.\n\
The distributed libraries would include all MIR (increasing binary size).
The distributed MIR would include validation statements.");
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Extended { pub struct Extended {
stage: u32, stage: u32,

View file

@ -429,7 +429,7 @@ impl Step for Std {
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
let builder = run.builder; let builder = run.builder;
run.krate("std").default_condition(builder.build.config.docs) run.all_krates("std").default_condition(builder.build.config.docs)
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {

View file

@ -42,6 +42,7 @@ pub struct Flags {
pub jobs: Option<u32>, pub jobs: Option<u32>,
pub cmd: Subcommand, pub cmd: Subcommand,
pub incremental: bool, pub incremental: bool,
pub exclude: Vec<PathBuf>,
} }
pub enum Subcommand { pub enum Subcommand {
@ -109,6 +110,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
opts.optopt("", "build", "build target of the stage0 compiler", "BUILD"); opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
opts.optmulti("", "host", "host targets to build", "HOST"); opts.optmulti("", "host", "host targets to build", "HOST");
opts.optmulti("", "target", "target targets to build", "TARGET"); opts.optmulti("", "target", "target targets to build", "TARGET");
opts.optmulti("", "exclude", "build paths to exclude", "PATH");
opts.optopt("", "on-fail", "command to run on failure", "CMD"); opts.optopt("", "on-fail", "command to run on failure", "CMD");
opts.optopt("", "stage", "stage to build", "N"); opts.optopt("", "stage", "stage to build", "N");
opts.optopt("", "keep-stage", "stage to keep without recompiling", "N"); opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
@ -273,7 +275,12 @@ Arguments:
}; };
// Get any optional paths which occur after the subcommand // Get any optional paths which occur after the subcommand
let cwd = t!(env::current_dir()); let cwd = t!(env::current_dir());
let paths = matches.free[1..].iter().map(|p| cwd.join(p)).collect::<Vec<_>>(); let src = matches.opt_str("src").map(PathBuf::from)
.or_else(|| env::var_os("SRC").map(PathBuf::from))
.unwrap_or(cwd.clone());
let paths = matches.free[1..].iter().map(|p| {
cwd.join(p).strip_prefix(&src).expect("paths passed to be inside checkout").into()
}).collect::<Vec<PathBuf>>();
let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| { let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
if fs::metadata("config.toml").is_ok() { if fs::metadata("config.toml").is_ok() {
@ -358,11 +365,6 @@ Arguments:
stage = Some(1); stage = Some(1);
} }
let cwd = t!(env::current_dir());
let src = matches.opt_str("src").map(PathBuf::from)
.or_else(|| env::var_os("SRC").map(PathBuf::from))
.unwrap_or(cwd);
Flags { Flags {
verbose: matches.opt_count("verbose"), verbose: matches.opt_count("verbose"),
stage, stage,
@ -374,10 +376,14 @@ Arguments:
target: split(matches.opt_strs("target")) target: split(matches.opt_strs("target"))
.into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(), .into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
config: cfg_file, config: cfg_file,
src,
jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()), jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
cmd, cmd,
incremental: matches.opt_present("incremental"), incremental: matches.opt_present("incremental"),
exclude: split(matches.opt_strs("exclude"))
.into_iter().map(|p| {
cwd.join(p).strip_prefix(&src).expect("paths to be inside checkout").into()
}).collect::<Vec<_>>(),
src,
} }
} }
} }

View file

@ -113,9 +113,8 @@
//! More documentation can be found in each respective module below, and you can //! More documentation can be found in each respective module below, and you can
//! also check out the `src/bootstrap/README.md` file for more information. //! also check out the `src/bootstrap/README.md` file for more information.
#![deny(warnings)] //#![deny(warnings)]
#![allow(stable_features)] #![feature(core_intrinsics)]
#![feature(associated_consts)]
#[macro_use] #[macro_use]
extern crate build_helper; extern crate build_helper;
@ -267,6 +266,18 @@ struct Crate {
bench_step: String, bench_step: String,
} }
impl Crate {
fn is_local(&self, build: &Build) -> bool {
self.path.starts_with(&build.config.src) &&
!self.path.to_string_lossy().ends_with("_shim")
}
fn local_path(&self, build: &Build) -> PathBuf {
assert!(self.is_local(build));
self.path.strip_prefix(&build.config.src).unwrap().into()
}
}
/// The various "modes" of invoking Cargo. /// The various "modes" of invoking Cargo.
/// ///
/// These entries currently correspond to the various output directories of the /// These entries currently correspond to the various output directories of the
@ -949,22 +960,18 @@ impl Build {
} }
} }
/// Get a list of crates from a root crate. fn in_tree_crates(&self, root: &str) -> Vec<&Crate> {
///
/// Returns Vec<(crate, path to crate, is_root_crate)>
fn crates(&self, root: &str) -> Vec<(Interned<String>, &Path)> {
let interned = INTERNER.intern_string(root.to_owned());
let mut ret = Vec::new(); let mut ret = Vec::new();
let mut list = vec![interned]; let mut list = vec![INTERNER.intern_str(root)];
let mut visited = HashSet::new(); let mut visited = HashSet::new();
while let Some(krate) = list.pop() { while let Some(krate) = list.pop() {
let krate = &self.crates[&krate]; let krate = &self.crates[&krate];
// If we can't strip prefix, then out-of-tree path if krate.is_local(self) {
let path = krate.path.strip_prefix(&self.src).unwrap_or(&krate.path); ret.push(krate);
ret.push((krate.name, path)); for dep in &krate.deps {
for dep in &krate.deps { if visited.insert(dep) && dep != "build_helper" {
if visited.insert(dep) && dep != "build_helper" { list.push(*dep);
list.push(*dep); }
} }
} }
} }

View file

@ -51,9 +51,7 @@ impl Step for Llvm {
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
let emscripten = run.path.map(|p| { let emscripten = run.path.ends_with("llvm-emscripten");
p.ends_with("llvm-emscripten")
}).unwrap_or(false);
run.builder.ensure(Llvm { run.builder.ensure(Llvm {
target: run.target, target: run.target,
emscripten, emscripten,

View file

@ -13,7 +13,6 @@
//! This file implements the various regression test suites that we execute on //! This file implements the various regression test suites that we execute on
//! our CI. //! our CI.
use std::collections::HashSet;
use std::env; use std::env;
use std::ffi::OsString; use std::ffi::OsString;
use std::iter; use std::iter;
@ -26,6 +25,7 @@ use std::io::Read;
use build_helper::{self, output}; use build_helper::{self, output};
use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step}; use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step};
use Crate as CargoCrate;
use cache::{INTERNER, Interned}; use cache::{INTERNER, Interned};
use compile; use compile;
use dist; use dist;
@ -550,180 +550,213 @@ fn testdir(build: &Build, host: Interned<String>) -> PathBuf {
build.out.join(host).join("test") build.out.join(host).join("test")
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] macro_rules! default_test {
struct Test { ($name:ident { path: $path:expr, mode: $mode:expr, suite: $suite:expr }) => {
path: &'static str, test!($name { path: $path, mode: $mode, suite: $suite, default: true, host: false });
mode: &'static str, }
suite: &'static str,
} }
static DEFAULT_COMPILETESTS: &[Test] = &[ macro_rules! host_test {
Test { path: "src/test/ui", mode: "ui", suite: "ui" }, ($name:ident { path: $path:expr, mode: $mode:expr, suite: $suite:expr }) => {
Test { path: "src/test/run-pass", mode: "run-pass", suite: "run-pass" }, test!($name { path: $path, mode: $mode, suite: $suite, default: true, host: true });
Test { path: "src/test/compile-fail", mode: "compile-fail", suite: "compile-fail" }, }
Test { path: "src/test/parse-fail", mode: "parse-fail", suite: "parse-fail" }, }
Test { path: "src/test/run-fail", mode: "run-fail", suite: "run-fail" },
Test {
path: "src/test/run-pass-valgrind",
mode: "run-pass-valgrind",
suite: "run-pass-valgrind"
},
Test { path: "src/test/mir-opt", mode: "mir-opt", suite: "mir-opt" },
Test { path: "src/test/codegen", mode: "codegen", suite: "codegen" },
Test { path: "src/test/codegen-units", mode: "codegen-units", suite: "codegen-units" },
Test { path: "src/test/incremental", mode: "incremental", suite: "incremental" },
macro_rules! test {
($name:ident {
path: $path:expr,
mode: $mode:expr,
suite: $suite:expr,
default: $default:expr,
host: $host:expr
}) => {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct $name {
pub compiler: Compiler,
pub target: Interned<String>,
}
impl Step for $name {
type Output = ();
const DEFAULT: bool = $default;
const ONLY_HOSTS: bool = $host;
fn should_run(run: ShouldRun) -> ShouldRun {
run.path($path)
}
fn make_run(run: RunConfig) {
let compiler = run.builder.compiler(run.builder.top_stage, run.host);
run.builder.ensure($name {
compiler,
target: run.target,
});
}
fn run(self, builder: &Builder) {
builder.ensure(Compiletest {
compiler: self.compiler,
target: self.target,
mode: $mode,
suite: $suite,
})
}
}
}
}
default_test!(Ui {
path: "src/test/ui",
mode: "ui",
suite: "ui"
});
default_test!(RunPass {
path: "src/test/run-pass",
mode: "run-pass",
suite: "run-pass"
});
default_test!(CompileFail {
path: "src/test/compile-fail",
mode: "compile-fail",
suite: "compile-fail"
});
default_test!(ParseFail {
path: "src/test/parse-fail",
mode: "parse-fail",
suite: "parse-fail"
});
default_test!(RunFail {
path: "src/test/run-fail",
mode: "run-fail",
suite: "run-fail"
});
default_test!(RunPassValgrind {
path: "src/test/run-pass-valgrind",
mode: "run-pass-valgrind",
suite: "run-pass-valgrind"
});
default_test!(MirOpt {
path: "src/test/mir-opt",
mode: "mir-opt",
suite: "mir-opt"
});
default_test!(Codegen {
path: "src/test/codegen",
mode: "codegen",
suite: "codegen"
});
default_test!(CodegenUnits {
path: "src/test/codegen-units",
mode: "codegen-units",
suite: "codegen-units"
});
default_test!(Incremental {
path: "src/test/incremental",
mode: "incremental",
suite: "incremental"
});
default_test!(Debuginfo {
path: "src/test/debuginfo",
// What this runs varies depending on the native platform being apple // What this runs varies depending on the native platform being apple
Test { path: "src/test/debuginfo", mode: "debuginfo-XXX", suite: "debuginfo" }, mode: "debuginfo-XXX",
]; suite: "debuginfo"
});
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] host_test!(UiFullDeps {
pub struct DefaultCompiletest { path: "src/test/ui-fulldeps",
compiler: Compiler, mode: "ui",
target: Interned<String>, suite: "ui-fulldeps"
mode: &'static str, });
suite: &'static str,
}
impl Step for DefaultCompiletest { host_test!(RunPassFullDeps {
type Output = (); path: "src/test/run-pass-fulldeps",
const DEFAULT: bool = true; mode: "run-pass",
suite: "run-pass-fulldeps"
});
fn should_run(mut run: ShouldRun) -> ShouldRun { host_test!(RunFailFullDeps {
for test in DEFAULT_COMPILETESTS { path: "src/test/run-fail-fulldeps",
run = run.path(test.path); mode: "run-fail",
} suite: "run-fail-fulldeps"
run });
}
fn make_run(run: RunConfig) { host_test!(CompileFailFullDeps {
let compiler = run.builder.compiler(run.builder.top_stage, run.host); path: "src/test/compile-fail-fulldeps",
mode: "compile-fail",
suite: "compile-fail-fulldeps"
});
let test = run.path.map(|path| { host_test!(IncrementalFullDeps {
DEFAULT_COMPILETESTS.iter().find(|&&test| { path: "src/test/incremental-fulldeps",
path.ends_with(test.path) mode: "incremental",
}).unwrap_or_else(|| { suite: "incremental-fulldeps"
panic!("make_run in compile test to receive test path, received {:?}", path); });
})
});
if let Some(test) = test { host_test!(Rustdoc {
run.builder.ensure(DefaultCompiletest { path: "src/test/rustdoc",
compiler, mode: "rustdoc",
target: run.target, suite: "rustdoc"
mode: test.mode, });
suite: test.suite,
});
} else {
for test in DEFAULT_COMPILETESTS {
run.builder.ensure(DefaultCompiletest {
compiler,
target: run.target,
mode: test.mode,
suite: test.suite
});
}
}
}
fn run(self, builder: &Builder) { test!(Pretty {
builder.ensure(Compiletest { path: "src/test/pretty",
compiler: self.compiler, mode: "pretty",
target: self.target, suite: "pretty",
mode: self.mode, default: false,
suite: self.suite, host: true
}) });
} test!(RunPassPretty {
} path: "src/test/run-pass/pretty",
mode: "pretty",
suite: "run-pass",
default: false,
host: true
});
test!(RunFailPretty {
path: "src/test/run-fail/pretty",
mode: "pretty",
suite: "run-fail",
default: false,
host: true
});
test!(RunPassValgrindPretty {
path: "src/test/run-pass-valgrind/pretty",
mode: "pretty",
suite: "run-pass-valgrind",
default: false,
host: true
});
test!(RunPassFullDepsPretty {
path: "src/test/run-pass-fulldeps/pretty",
mode: "pretty",
suite: "run-pass-fulldeps",
default: false,
host: true
});
test!(RunFailFullDepsPretty {
path: "src/test/run-fail-fulldeps/pretty",
mode: "pretty",
suite: "run-fail-fulldeps",
default: false,
host: true
});
// Also default, but host-only. host_test!(RunMake {
static HOST_COMPILETESTS: &[Test] = &[ path: "src/test/run-make",
Test { path: "src/test/ui-fulldeps", mode: "ui", suite: "ui-fulldeps" }, mode: "run-make",
Test { path: "src/test/run-pass-fulldeps", mode: "run-pass", suite: "run-pass-fulldeps" }, suite: "run-make"
Test { path: "src/test/run-fail-fulldeps", mode: "run-fail", suite: "run-fail-fulldeps" }, });
Test {
path: "src/test/compile-fail-fulldeps",
mode: "compile-fail",
suite: "compile-fail-fulldeps",
},
Test {
path: "src/test/incremental-fulldeps",
mode: "incremental",
suite: "incremental-fulldeps",
},
Test { path: "src/test/rustdoc", mode: "rustdoc", suite: "rustdoc" },
Test { path: "src/test/pretty", mode: "pretty", suite: "pretty" },
Test { path: "src/test/run-pass/pretty", mode: "pretty", suite: "run-pass" },
Test { path: "src/test/run-fail/pretty", mode: "pretty", suite: "run-fail" },
Test { path: "src/test/run-pass-valgrind/pretty", mode: "pretty", suite: "run-pass-valgrind" },
Test { path: "src/test/run-pass-fulldeps/pretty", mode: "pretty", suite: "run-pass-fulldeps" },
Test { path: "src/test/run-fail-fulldeps/pretty", mode: "pretty", suite: "run-fail-fulldeps" },
Test { path: "src/test/run-make", mode: "run-make", suite: "run-make" },
];
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct HostCompiletest {
compiler: Compiler,
target: Interned<String>,
mode: &'static str,
suite: &'static str,
}
impl Step for HostCompiletest {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(mut run: ShouldRun) -> ShouldRun {
for test in HOST_COMPILETESTS {
run = run.path(test.path);
}
run
}
fn make_run(run: RunConfig) {
let compiler = run.builder.compiler(run.builder.top_stage, run.host);
let test = run.path.map(|path| {
HOST_COMPILETESTS.iter().find(|&&test| {
path.ends_with(test.path)
}).unwrap_or_else(|| {
panic!("make_run in compile test to receive test path, received {:?}", path);
})
});
if let Some(test) = test {
run.builder.ensure(HostCompiletest {
compiler,
target: run.target,
mode: test.mode,
suite: test.suite,
});
} else {
for test in HOST_COMPILETESTS {
if test.mode == "pretty" {
continue;
}
run.builder.ensure(HostCompiletest {
compiler,
target: run.target,
mode: test.mode,
suite: test.suite
});
}
}
}
fn run(self, builder: &Builder) {
builder.ensure(Compiletest {
compiler: self.compiler,
target: self.target,
mode: self.mode,
suite: self.suite,
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct Compiletest { struct Compiletest {
@ -902,7 +935,7 @@ impl Step for Compiletest {
} }
} }
if suite == "run-make" && !build.config.llvm_enabled { if suite == "run-make" && !build.config.llvm_enabled {
println!("Ignoring run-make test suite as they generally don't work without LLVM"); println!("Ignoring run-make test suite as they generally dont work without LLVM");
return; return;
} }
@ -1099,7 +1132,7 @@ pub struct CrateLibrustc {
compiler: Compiler, compiler: Compiler,
target: Interned<String>, target: Interned<String>,
test_kind: TestKind, test_kind: TestKind,
krate: Option<Interned<String>>, krate: Interned<String>,
} }
impl Step for CrateLibrustc { impl Step for CrateLibrustc {
@ -1115,35 +1148,26 @@ impl Step for CrateLibrustc {
let builder = run.builder; let builder = run.builder;
let compiler = builder.compiler(builder.top_stage, run.host); let compiler = builder.compiler(builder.top_stage, run.host);
let make = |name: Option<Interned<String>>| { for krate in builder.in_tree_crates("rustc-main") {
let test_kind = if builder.kind == Kind::Test { if run.path.ends_with(&krate.path) {
TestKind::Test let test_kind = if builder.kind == Kind::Test {
} else if builder.kind == Kind::Bench { TestKind::Test
TestKind::Bench } else if builder.kind == Kind::Bench {
} else { TestKind::Bench
panic!("unexpected builder.kind in crate: {:?}", builder.kind); } else {
}; panic!("unexpected builder.kind in crate: {:?}", builder.kind);
};
builder.ensure(CrateLibrustc { builder.ensure(CrateLibrustc {
compiler, compiler,
target: run.target, target: run.target,
test_kind, test_kind,
krate: name, krate: krate.name,
}); });
};
if let Some(path) = run.path {
for (name, krate_path) in builder.crates("rustc-main") {
if path.ends_with(krate_path) {
make(Some(name));
}
} }
} else {
make(None);
} }
} }
fn run(self, builder: &Builder) { fn run(self, builder: &Builder) {
builder.ensure(Crate { builder.ensure(Crate {
compiler: self.compiler, compiler: self.compiler,
@ -1156,27 +1180,95 @@ impl Step for CrateLibrustc {
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Crate { pub struct CrateNotDefault {
compiler: Compiler, compiler: Compiler,
target: Interned<String>, target: Interned<String>,
mode: Mode,
test_kind: TestKind, test_kind: TestKind,
krate: Option<Interned<String>>, krate: &'static str,
} }
impl Step for Crate { impl Step for CrateNotDefault {
type Output = (); type Output = ();
const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.krate("std").krate("test") run.path("src/liballoc_jemalloc")
.path("src/librustc_asan")
.path("src/librustc_lsan")
.path("src/librustc_msan")
.path("src/librustc_tsan")
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
let builder = run.builder; let builder = run.builder;
let compiler = builder.compiler(builder.top_stage, run.host); let compiler = builder.compiler(builder.top_stage, run.host);
let make = |mode: Mode, name: Option<Interned<String>>| { let test_kind = if builder.kind == Kind::Test {
TestKind::Test
} else if builder.kind == Kind::Bench {
TestKind::Bench
} else {
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
};
builder.ensure(CrateNotDefault {
compiler,
target: run.target,
test_kind,
krate: match run.path {
_ if run.path.ends_with("src/liballoc_jemalloc") => "alloc_jemalloc",
_ if run.path.ends_with("src/librustc_asan") => "rustc_asan",
_ if run.path.ends_with("src/librustc_lsan") => "rustc_lsan",
_ if run.path.ends_with("src/librustc_msan") => "rustc_msan",
_ if run.path.ends_with("src/librustc_tsan") => "rustc_tsan",
_ => panic!("unexpected path {:?}", run.path),
},
});
}
fn run(self, builder: &Builder) {
builder.ensure(Crate {
compiler: self.compiler,
target: self.target,
mode: Mode::Libstd,
test_kind: self.test_kind,
krate: INTERNER.intern_str(self.krate),
});
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Crate {
compiler: Compiler,
target: Interned<String>,
mode: Mode,
test_kind: TestKind,
krate: Interned<String>,
}
impl Step for Crate {
type Output = ();
const DEFAULT: bool = true;
fn should_run(mut run: ShouldRun) -> ShouldRun {
let builder = run.builder;
run = run.krate("test");
for krate in run.builder.in_tree_crates("std") {
if krate.is_local(&run.builder) &&
!krate.name.contains("jemalloc") &&
!(krate.name.starts_with("rustc_") && krate.name.ends_with("san")) &&
krate.name != "dlmalloc" {
run = run.path(krate.local_path(&builder).to_str().unwrap());
}
}
run
}
fn make_run(run: RunConfig) {
let builder = run.builder;
let compiler = builder.compiler(builder.top_stage, run.host);
let make = |mode: Mode, krate: &CargoCrate| {
let test_kind = if builder.kind == Kind::Test { let test_kind = if builder.kind == Kind::Test {
TestKind::Test TestKind::Test
} else if builder.kind == Kind::Bench { } else if builder.kind == Kind::Bench {
@ -1190,29 +1282,24 @@ impl Step for Crate {
target: run.target, target: run.target,
mode, mode,
test_kind, test_kind,
krate: name, krate: krate.name,
}); });
}; };
if let Some(path) = run.path { for krate in builder.in_tree_crates("std") {
for (name, krate_path) in builder.crates("std") { if run.path.ends_with(&krate.local_path(&builder)) {
if path.ends_with(krate_path) { make(Mode::Libstd, krate);
make(Mode::Libstd, Some(name));
}
} }
for (name, krate_path) in builder.crates("test") { }
if path.ends_with(krate_path) { for krate in builder.in_tree_crates("test") {
make(Mode::Libtest, Some(name)); if run.path.ends_with(&krate.local_path(&builder)) {
} make(Mode::Libtest, krate);
} }
} else {
make(Mode::Libstd, None);
make(Mode::Libtest, None);
} }
} }
/// Run all unit tests plus documentation tests for an entire crate DAG defined /// Run all unit tests plus documentation tests for a given crate defined
/// by a `Cargo.toml` /// by a `Cargo.toml` (single manifest)
/// ///
/// This is what runs tests for crates like the standard library, compiler, etc. /// This is what runs tests for crates like the standard library, compiler, etc.
/// It essentially is the driver for running `cargo test`. /// It essentially is the driver for running `cargo test`.
@ -1241,27 +1328,23 @@ impl Step for Crate {
}; };
let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand()); let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand());
let (name, root) = match mode { match mode {
Mode::Libstd => { Mode::Libstd => {
compile::std_cargo(build, &compiler, target, &mut cargo); compile::std_cargo(build, &compiler, target, &mut cargo);
("libstd", "std")
} }
Mode::Libtest => { Mode::Libtest => {
compile::test_cargo(build, &compiler, target, &mut cargo); compile::test_cargo(build, &compiler, target, &mut cargo);
("libtest", "test")
} }
Mode::Librustc => { Mode::Librustc => {
builder.ensure(compile::Rustc { compiler, target }); builder.ensure(compile::Rustc { compiler, target });
compile::rustc_cargo(build, &mut cargo); compile::rustc_cargo(build, &mut cargo);
("librustc", "rustc-main")
} }
_ => panic!("can only test libraries"), _ => panic!("can only test libraries"),
}; };
let root = INTERNER.intern_string(String::from(root));
let _folder = build.fold_output(|| { let _folder = build.fold_output(|| {
format!("{}_stage{}-{}", test_kind.subcommand(), compiler.stage, name) format!("{}_stage{}-{}", test_kind.subcommand(), compiler.stage, krate)
}); });
println!("{} {} stage{} ({} -> {})", test_kind, name, compiler.stage, println!("{} {} stage{} ({} -> {})", test_kind, krate, compiler.stage,
&compiler.host, target); &compiler.host, target);
// Build up the base `cargo test` command. // Build up the base `cargo test` command.
@ -1273,37 +1356,7 @@ impl Step for Crate {
cargo.arg("--no-fail-fast"); cargo.arg("--no-fail-fast");
} }
match krate { cargo.arg("-p").arg(krate);
Some(krate) => {
cargo.arg("-p").arg(krate);
}
None => {
let mut visited = HashSet::new();
let mut next = vec![root];
while let Some(name) = next.pop() {
// Right now jemalloc and the sanitizer crates are
// target-specific crate in the sense that it's not present
// on all platforms. Custom skip it here for now, but if we
// add more this probably wants to get more generalized.
//
// Also skip `build_helper` as it's not compiled normally
// for target during the bootstrap and it's just meant to be
// a helper crate, not tested. If it leaks through then it
// ends up messing with various mtime calculations and such.
if !name.contains("jemalloc") &&
*name != *"build_helper" &&
!(name.starts_with("rustc_") && name.ends_with("san")) &&
name != "dlmalloc" {
cargo.arg("-p").arg(&format!("{}:0.0.0", name));
}
for dep in build.crates[&name].deps.iter() {
if visited.insert(dep) {
next.push(*dep);
}
}
}
}
}
// The tests are going to run with the *target* libraries, so we need to // The tests are going to run with the *target* libraries, so we need to
// ensure that those libraries show up in the LD_LIBRARY_PATH equivalent. // ensure that those libraries show up in the LD_LIBRARY_PATH equivalent.
@ -1355,18 +1408,18 @@ impl Step for Crate {
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Rustdoc { pub struct CrateRustdoc {
host: Interned<String>, host: Interned<String>,
test_kind: TestKind, test_kind: TestKind,
} }
impl Step for Rustdoc { impl Step for CrateRustdoc {
type Output = (); type Output = ();
const DEFAULT: bool = true; const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true; const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun { fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/librustdoc").path("src/tools/rustdoc") run.paths(&["src/librustdoc", "src/tools/rustdoc"])
} }
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
@ -1380,7 +1433,7 @@ impl Step for Rustdoc {
panic!("unexpected builder.kind in crate: {:?}", builder.kind); panic!("unexpected builder.kind in crate: {:?}", builder.kind);
}; };
builder.ensure(Rustdoc { builder.ensure(CrateRustdoc {
host: run.host, host: run.host,
test_kind, test_kind,
}); });