// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use serde::{Serialize, Deserialize};
-
+use std::fmt::Debug;
+use std::hash::Hash;
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::fs;
use std::ops::Deref;
+use std::any::Any;
+use std::collections::BTreeSet;
use compile;
use install;
use dist;
use util::{exe, libdir, add_lib_path};
use {Build, Mode};
-use cache::{Cache, Key};
+use cache::{INTERNER, Interned, Cache};
use check;
use flags::Subcommand;
use doc;
pub top_stage: u32,
pub kind: Kind,
cache: Cache,
- stack: RefCell<Vec<Key>>,
+ stack: RefCell<Vec<Box<Any>>>,
}
impl<'a> Deref for Builder<'a> {
}
}
-pub trait Step<'a>: Serialize + Sized {
- /// The output type of this step. This is used in a few places to return a
+pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
/// `PathBuf` when directories are created or to return a `Compiler` once
/// it's been assembled.
- ///
- /// When possible, this should be used instead of implicitly creating files
- /// in a prearranged directory that will later be used by the build system.
- /// It's not always practical, however, since it makes avoiding rebuilds
- /// somewhat harder.
- type Output: Serialize + Deserialize<'a> + 'a;
+ type Output: Clone;
const DEFAULT: bool = false;
/// Primary function to execute this rule. Can call `builder.ensure(...)`
/// with other steps to run those.
- fn run(self, builder: &'a Builder) -> Self::Output;
+ fn run(self, builder: &Builder) -> Self::Output;
/// When bootstrap is passed a set of paths, this controls whether this rule
/// will execute. However, it does not get called in a "default" context
/// when we are not passed any paths; in that case, make_run is called
/// directly.
- fn should_run(_builder: &'a Builder, _path: &Path) -> bool { false }
+ fn should_run(run: ShouldRun) -> ShouldRun;
/// Build up a "root" rule, either as a default rule or from a path passed
/// to us.
/// passed. When `./x.py build` is run, for example, this rule could get
/// called if it is in the correct list below with a path of `None`.
fn make_run(
- _builder: &'a Builder,
+ _builder: &Builder,
_path: Option<&Path>,
- _host: &'a str,
- _target: &'a str,
- ) { unimplemented!() }
+ _host: Interned<String>,
+ _target: Interned<String>,
+ ) {
+ // It is reasonable to not have an implementation of make_run for rules
+ // who do not want to get called from the root context. This means that
+ // they are likely dependencies (e.g., sysroot creation) or similar, and
+ // as such calling them from ./x.py isn't logical.
+ unimplemented!()
+ }
}
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum Kind {
- Build,
- Test,
- Bench,
- Dist,
- Doc,
- Install,
+struct StepDescription {
+ default: bool,
+ only_hosts: bool,
+ only_build_targets: bool,
+ only_build: bool,
+ should_run: fn(ShouldRun) -> ShouldRun,
+ make_run: fn(&Builder, Option<&Path>, Interned<String>, Interned<String>),
}
-macro_rules! check {
- ($self:ident, $paths:ident, $($rule:ty),+ $(,)*) => {{
- let paths = $paths;
+impl StepDescription {
+ fn from<S: Step>() -> StepDescription {
+ StepDescription {
+ default: S::DEFAULT,
+ only_hosts: S::ONLY_HOSTS,
+ only_build_targets: S::ONLY_BUILD_TARGETS,
+ only_build: S::ONLY_BUILD,
+ should_run: S::should_run,
+ make_run: S::make_run,
+ }
+ }
+
+ fn maybe_run(&self, builder: &Builder, path: Option<&Path>) {
+ let build = builder.build;
+ let hosts = if self.only_build_targets || self.only_build {
+ &build.config.host[..1]
+ } else {
+ &build.hosts
+ };
+
+ // Determine the actual targets participating in this rule.
+ // NOTE: We should keep the full projection from build triple to
+ // the hosts for the dist steps, now that the hosts array above is
+ // truncated to avoid duplication of work in that case. Therefore
+ // the original non-shadowed hosts array is used below.
+ let targets = if self.only_hosts {
+ // If --target was specified but --host wasn't specified,
+ // don't run any host-only tests. Also, respect any `--host`
+ // overrides as done for `hosts`.
+ if build.flags.host.len() > 0 {
+ &build.flags.host[..]
+ } else if build.flags.target.len() > 0 {
+ &[]
+ } else if self.only_build {
+ &build.config.host[..1]
+ } else {
+ &build.config.host[..]
+ }
+ } else {
+ &build.targets
+ };
+
+ for host in hosts {
+ for target in targets {
+ (self.make_run)(builder, path, *host, *target);
+ }
+ }
+ }
+
+ fn run(v: &[StepDescription], builder: &Builder, paths: &[PathBuf]) {
if paths.is_empty() {
- $({
- if <$rule>::DEFAULT {
- $self.maybe_run::<$rule>(None);
+ for desc in v {
+ if desc.default {
+ desc.maybe_run(builder, None);
}
- })+
+ }
} else {
for path in paths {
- $({
- if <$rule>::should_run($self, path) {
- $self.maybe_run::<$rule>(Some(path));
+ let mut attempted_run = false;
+ for desc in v {
+ if (desc.should_run)(ShouldRun::new(builder)).run(path) {
+ attempted_run = true;
+ desc.maybe_run(builder, Some(path));
}
- })+
+ }
+
+ if !attempted_run {
+ eprintln!("Warning: no rules matched {}.", path.display());
+ }
}
}
- }};
+ }
+}
+
+#[derive(Clone)]
+pub struct ShouldRun<'a> {
+ builder: &'a Builder<'a>,
+ // use a BTreeSet to maintain sort order
+ paths: BTreeSet<PathBuf>,
+}
+
+impl<'a> ShouldRun<'a> {
+ fn new(builder: &'a Builder) -> ShouldRun<'a> {
+ ShouldRun {
+ builder: builder,
+ paths: BTreeSet::new(),
+ }
+ }
+
+ pub fn krate(mut self, name: &str) -> Self {
+ for (_, krate_path) in self.builder.crates(name) {
+ self.paths.insert(PathBuf::from(krate_path));
+ }
+ self
+ }
+
+ pub fn path(mut self, path: &str) -> Self {
+ self.paths.insert(PathBuf::from(path));
+ self
+ }
+
+ // allows being more explicit about why should_run in Step returns the value passed to it
+ pub fn never(self) -> ShouldRun<'a> {
+ self
+ }
+
+ fn run(&self, path: &Path) -> bool {
+ self.paths.iter().any(|p| path.ends_with(p))
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum Kind {
+ Build,
+ Test,
+ Bench,
+ Dist,
+ Doc,
+ Install,
}
impl<'a> Builder<'a> {
+ fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> {
+ macro_rules! describe {
+ ($($rule:ty),+ $(,)*) => {{
+ vec![$(StepDescription::from::<$rule>()),+]
+ }};
+ }
+ match kind {
+ Kind::Build => describe!(compile::Std, compile::Test, compile::Rustc,
+ compile::StartupObjects, tool::BuildManifest, tool::Rustbook, tool::ErrorIndex,
+ tool::UnstableBookGen, tool::Tidy, tool::Linkchecker, tool::CargoTest,
+ tool::Compiletest, tool::RemoteTestServer, tool::RemoteTestClient,
+ tool::RustInstaller, tool::Cargo, tool::Rls),
+ Kind::Test => describe!(check::Tidy, check::Bootstrap, check::DefaultCompiletest,
+ check::HostCompiletest, check::Crate, check::CrateLibrustc, check::Linkcheck,
+ check::Cargotest, check::Cargo, check::Rls, check::Docs, check::ErrorIndex,
+ check::Distcheck),
+ Kind::Bench => describe!(check::Crate, check::CrateLibrustc),
+ Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook,
+ doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon,
+ doc::Reference),
+ Kind::Dist => describe!(dist::Docs, dist::Mingw, dist::Rustc, dist::DebuggerScripts,
+ dist::Std, dist::Analysis, dist::Src, dist::PlainSourceTarball, dist::Cargo,
+ dist::Rls, dist::Extended, dist::HashSign),
+ Kind::Install => describe!(install::Docs, install::Std, install::Cargo, install::Rls,
+ install::Analysis, install::Src, install::Rustc),
+ }
+ }
+
+ pub fn get_help(build: &Build, subcommand: &str) -> Option<String> {
+ let kind = match subcommand {
+ "build" => Kind::Build,
+ "doc" => Kind::Doc,
+ "test" => Kind::Test,
+ "bench" => Kind::Bench,
+ "dist" => Kind::Dist,
+ "install" => Kind::Install,
+ _ => return None,
+ };
+
+ let builder = Builder {
+ build: build,
+ top_stage: build.flags.stage.unwrap_or(2),
+ kind: kind,
+ cache: Cache::new(),
+ stack: RefCell::new(Vec::new()),
+ };
+
+ let builder = &builder;
+ let mut should_run = ShouldRun::new(builder);
+ for desc in Builder::get_step_descriptions(builder.kind) {
+ should_run = (desc.should_run)(should_run);
+ }
+ let mut help = String::from("Available paths:\n");
+ for path in should_run.paths {
+ help.push_str(format!(" ./x.py {} {}\n", subcommand, path.display()).as_str());
+ }
+ Some(help)
+ }
+
pub fn run(build: &Build) {
let (kind, paths) = match build.flags.cmd {
Subcommand::Build { ref paths } => (Kind::Build, &paths[..]),
stack: RefCell::new(Vec::new()),
};
- let builder = &builder;
- match builder.kind {
- Kind::Build => check!(builder, paths, compile::Std, compile::Test, compile::Rustc,
- compile::StartupObjects, tool::BuildManifest, tool::Rustbook, tool::ErrorIndex,
- tool::UnstableBookGen, tool::Tidy, tool::Linkchecker, tool::CargoTest,
- tool::Compiletest, tool::RemoteTestServer, tool::RemoteTestClient,
- tool::RustInstaller, tool::Cargo, tool::Rls),
- Kind::Test => check!(builder, paths, check::Tidy, check::Bootstrap, check::Compiletest,
- check::Krate, check::KrateLibrustc, check::Linkcheck, check::Cargotest,
- check::Cargo, check::Docs, check::ErrorIndex, check::Distcheck),
- Kind::Bench => check!(builder, paths, check::Krate, check::KrateLibrustc),
- Kind::Doc => builder.default_doc(Some(paths)),
- Kind::Dist => check!(builder, paths, dist::Docs, dist::Mingw, dist::Rustc,
- dist::DebuggerScripts, dist::Std, dist::Analysis, dist::Src,
- dist::PlainSourceTarball, dist::Cargo, dist::Rls, dist::Extended, dist::HashSign),
- Kind::Install => check!(builder, paths, install::Docs, install::Std, install::Cargo,
- install::Rls, install::Analysis, install::Src, install::Rustc),
- }
+ StepDescription::run(&Builder::get_step_descriptions(builder.kind), &builder, paths);
}
pub fn default_doc(&self, paths: Option<&[PathBuf]>) {
let paths = paths.unwrap_or(&[]);
- check!(self, paths, doc::UnstableBook, doc::UnstableBookGen, doc::Rustbook, doc::TheBook,
- doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex,
- doc::Nomicon, doc::Reference);
+ StepDescription::run(&Builder::get_step_descriptions(Kind::Doc), self, paths);
}
/// Obtain a compiler at a given stage and for a given host. Explictly does
/// not take `Compiler` since all `Compiler` instances are meant to be
/// obtained through this function, since it ensures that they are valid
/// (i.e., built and assembled).
- pub fn compiler(&'a self, stage: u32, host: &'a str) -> Compiler<'a> {
+ pub fn compiler(&self, stage: u32, host: Interned<String>) -> Compiler {
self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } })
}
- pub fn sysroot(&self, compiler: Compiler<'a>) -> PathBuf {
+ pub fn sysroot(&self, compiler: Compiler) -> Interned<PathBuf> {
self.ensure(compile::Sysroot { compiler })
}
/// Returns the libdir where the standard library and other artifacts are
/// found for a compiler's sysroot.
- pub fn sysroot_libdir(&self, compiler: Compiler<'a>, target: &'a str) -> PathBuf {
- #[derive(Serialize)]
- struct Libdir<'a> {
- compiler: Compiler<'a>,
- target: &'a str,
+ pub fn sysroot_libdir(
+ &self, compiler: Compiler, target: Interned<String>
+ ) -> Interned<PathBuf> {
+ #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+ struct Libdir {
+ compiler: Compiler,
+ target: Interned<String>,
}
- impl<'a> Step<'a> for Libdir<'a> {
- type Output = PathBuf;
- fn run(self, builder: &Builder) -> PathBuf {
+ impl Step for Libdir {
+ type Output = Interned<PathBuf>;
+
+ fn should_run(run: ShouldRun) -> ShouldRun {
+ run.never()
+ }
+
+ fn run(self, builder: &Builder) -> Interned<PathBuf> {
let compiler = self.compiler;
let lib = if compiler.stage >= 2 && builder.build.config.libdir_relative.is_some() {
builder.build.config.libdir_relative.clone().unwrap()
.join("rustlib").join(self.target).join("lib");
let _ = fs::remove_dir_all(&sysroot);
t!(fs::create_dir_all(&sysroot));
- sysroot
+ INTERNER.intern_path(sysroot)
}
}
self.ensure(Libdir { compiler, target })
if compiler.is_snapshot(self) {
self.build.rustc_snapshot_libdir()
} else {
- self.sysroot(compiler).join(libdir(compiler.host))
+ self.sysroot(compiler).join(libdir(&compiler.host))
}
}
if compiler.is_snapshot(self) {
self.initial_rustc.clone()
} else {
- self.sysroot(compiler).join("bin").join(exe("rustc", compiler.host))
+ self.sysroot(compiler).join("bin").join(exe("rustc", &compiler.host))
}
}
pub fn rustdoc(&self, compiler: Compiler) -> PathBuf {
let mut rustdoc = self.rustc(compiler);
rustdoc.pop();
- rustdoc.push(exe("rustdoc", compiler.host));
+ rustdoc.push(exe("rustdoc", &compiler.host));
rustdoc
}
pub fn cargo(&self,
compiler: Compiler,
mode: Mode,
- target: &str,
+ target: Interned<String>,
cmd: &str) -> Command {
let mut cargo = Command::new(&self.initial_cargo);
let out_dir = self.stage_out(compiler, mode);
cargo.env("RUSTC_SAVE_ANALYSIS", "api".to_string());
}
- // When being built Cargo will at some point call `nmake.exe` on Windows
- // MSVC. Unfortunately `nmake` will read these two environment variables
- // below and try to intepret them. We're likely being run, however, from
- // MSYS `make` which uses the same variables.
- //
- // As a result, to prevent confusion and errors, we remove these
- // variables from our environment to prevent passing MSYS make flags to
- // nmake, causing it to blow up.
- if cfg!(target_env = "msvc") {
- cargo.env_remove("MAKE");
- cargo.env_remove("MAKEFLAGS");
- }
-
// Environment variables *required* throughout the build
//
// FIXME: should update code to not require this env var
cargo
}
- fn maybe_run<S: Step<'a>>(&'a self, path: Option<&Path>) {
- let build = self.build;
- let hosts = if S::ONLY_BUILD_TARGETS || S::ONLY_BUILD {
- &build.config.host[..1]
- } else {
- &build.hosts
- };
-
- // Determine the actual targets participating in this rule.
- // NOTE: We should keep the full projection from build triple to
- // the hosts for the dist steps, now that the hosts array above is
- // truncated to avoid duplication of work in that case. Therefore
- // the original non-shadowed hosts array is used below.
- let targets = if S::ONLY_HOSTS {
- // If --target was specified but --host wasn't specified,
- // don't run any host-only tests. Also, respect any `--host`
- // overrides as done for `hosts`.
- if build.flags.host.len() > 0 {
- &build.flags.host[..]
- } else if build.flags.target.len() > 0 {
- &[]
- } else if S::ONLY_BUILD {
- &build.config.host[..1]
- } else {
- &build.config.host[..]
- }
- } else {
- &build.targets
- };
-
- for host in hosts {
- for target in targets {
- S::make_run(self, path, host, target);
- }
- }
- }
-
/// Ensure that a given step is built, returning it's output. This will
/// cache the step, so it is safe (and good!) to call this as often as
/// needed to ensure that all dependencies are built.
- pub fn ensure<S: Step<'a>>(&'a self, step: S) -> S::Output {
- let key = Cache::to_key(&step);
+ pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
{
let mut stack = self.stack.borrow_mut();
- if stack.contains(&key) {
+ for stack_step in stack.iter() {
+ // should skip
+ if stack_step.downcast_ref::<S>().map_or(true, |stack_step| *stack_step != step) {
+ continue;
+ }
let mut out = String::new();
- out += &format!("\n\nCycle in build detected when adding {:?}\n", key);
+ out += &format!("\n\nCycle in build detected when adding {:?}\n", step);
for el in stack.iter().rev() {
out += &format!("\t{:?}\n", el);
}
panic!(out);
}
- if let Some(out) = self.cache.get::<S::Output>(&key) {
- self.build.verbose(&format!("{}c {:?}", " ".repeat(stack.len()), key));
+ if let Some(out) = self.cache.get(&step) {
+ self.build.verbose(&format!("{}c {:?}", " ".repeat(stack.len()), step));
return out;
}
- self.build.verbose(&format!("{}> {:?}", " ".repeat(stack.len()), key));
- stack.push(key.clone());
+ self.build.verbose(&format!("{}> {:?}", " ".repeat(stack.len()), step));
+ stack.push(Box::new(step.clone()));
}
- let out = step.run(self);
+ let out = step.clone().run(self);
{
let mut stack = self.stack.borrow_mut();
- assert_eq!(stack.pop().as_ref(), Some(&key));
+ let cur_step = stack.pop().expect("step stack empty");
+ assert_eq!(cur_step.downcast_ref(), Some(&step));
}
- self.build.verbose(&format!("{}< {:?}", " ".repeat(self.stack.borrow().len()), key));
- self.cache.put(key.clone(), &out);
- self.cache.get::<S::Output>(&key).unwrap()
+ self.build.verbose(&format!("{}< {:?}", " ".repeat(self.stack.borrow().len()), step));
+ self.cache.put(step, out.clone());
+ out
}
}