// 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::{TypeId, Any};
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;
+use tool;
pub use Compiler;
pub top_stage: u32,
pub kind: Kind,
cache: Cache,
- stack: RefCell<Vec<Key>>,
+ stack: RefCell<Vec<(TypeId, Box<Any>)>>,
}
impl<'a> Deref for Builder<'a> {
}
}
-pub trait Step<'a>: Sized {
- type Output: Serialize + Deserialize<'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.
+ type Output: Clone;
const DEFAULT: bool = false;
- /// Run this rule for all hosts, and just the same hosts as the targets.
+ /// Run this rule for all hosts without cross compiling.
const ONLY_HOSTS: bool = false;
/// Run this rule for all targets, but only with the native host.
/// Only run this step with the build triple as host and target.
const ONLY_BUILD: bool = false;
- fn run(self, builder: &'a Builder) -> Self::Output;
+ /// Primary function to execute this rule. Can call `builder.ensure(...)`
+ /// with other steps to run those.
+ fn run(self, builder: &Builder) -> Self::Output;
- fn should_run(_builder: &'a Builder, _path: &Path) -> bool { false }
+ /// 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: &Builder, _path: &Path) -> bool { false }
+ /// Build up a "root" rule, either as a default rule or from a path passed
+ /// to us.
+ ///
+ /// When path is `None`, we are executing in a context where no paths were
+ /// 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,
+ _host: Interned<String>,
+ _target: Interned<String>,
) { unimplemented!() }
}
}
macro_rules! check {
- (@inner $self:ident, $rule:ty, $path:expr) => {
- let build = $self.build;
- let hosts = if <$rule>::ONLY_BUILD_TARGETS || <$rule>::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 <$rule>::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 <$rule>::ONLY_BUILD {
- &build.config.host[..1]
- } else {
- &build.config.host[..]
- }
- } else {
- &build.targets
- };
-
- build.verbose(&format!("executing {} with hosts={:?}, targets={:?}",
- stringify!($rule), hosts, targets));
- for host in hosts {
- for target in targets {
- <$rule>::make_run($self, $path, host, target);
- }
- }
- };
($self:ident, $paths:ident, $($rule:ty),+ $(,)*) => {{
let paths = $paths;
if paths.is_empty() {
$({
if <$rule>::DEFAULT {
- check!(@inner $self, $rule, None);
+ $self.maybe_run::<$rule>(None);
}
})+
} else {
for path in paths {
$({
if <$rule>::should_run($self, path) {
- check!(@inner $self, $rule, Some(path));
+ $self.maybe_run::<$rule>(Some(path));
}
})+
}
let builder = &builder;
match builder.kind {
Kind::Build => check!(builder, paths, compile::Std, compile::Test, compile::Rustc,
- compile::StartupObjects),
+ 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),
doc::Nomicon, doc::Reference);
}
- pub fn compiler(&'a self, stage: u32, host: &'a str) -> Compiler<'a> {
+ /// 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(&self, stage: u32, host: Interned<String>) -> Compiler {
self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } })
}
- pub fn rustc(&self, compiler: Compiler) -> PathBuf {
- if compiler.is_snapshot(self) {
- self.build.initial_rustc.clone()
- } else {
- self.compiler(compiler.stage, 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
- }
-
- 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 {
- let sysroot = builder.sysroot(self.compiler)
- .join("lib").join("rustlib").join(self.target).join("lib");
+ impl Step for Libdir {
+ type Output = Interned<PathBuf>;
+ 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()
+ } else {
+ PathBuf::from("lib")
+ };
+ let sysroot = builder.sysroot(self.compiler).join(lib)
+ .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))
}
}
add_lib_path(vec![self.rustc_libdir(compiler)], cmd);
}
+ /// Get a path to the compiler specified.
+ pub fn rustc(&self, compiler: Compiler) -> PathBuf {
+ if compiler.is_snapshot(self) {
+ self.initial_rustc.clone()
+ } else {
+ self.sysroot(compiler).join("bin").join(exe("rustc", &compiler.host))
+ }
+ }
+
+ /// Get the `rustdoc` executable next to the specified compiler
+ pub fn rustdoc(&self, compiler: Compiler) -> PathBuf {
+ let mut rustdoc = self.rustc(compiler);
+ rustdoc.pop();
+ rustdoc.push(exe("rustdoc", &compiler.host));
+ rustdoc
+ }
+
/// Prepares an invocation of `cargo` to be run.
///
/// This will create a `Command` that represents a pending execution of
/// rustc compiler, its output will be scoped by `mode`'s output directory,
/// it will pass the `--target` flag for the specified `target`, and will be
/// executing the Cargo command `cmd`.
- fn cargo(&self,
+ 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);
// src/bootstrap/bin/{rustc.rs,rustdoc.rs}
cargo.env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
.env("RUSTC", self.out.join("bootstrap/debug/rustc"))
- .env("RUSTC_REAL", self.compiler_path(compiler))
+ .env("RUSTC_REAL", self.rustc(compiler))
.env("RUSTC_STAGE", stage.to_string())
.env("RUSTC_CODEGEN_UNITS",
self.config.rust_codegen_units.to_string())
cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc)
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
} else {
- cargo.env("RUSTC_SNAPSHOT", self.compiler_path(compiler))
+ cargo.env("RUSTC_SNAPSHOT", self.rustc(compiler))
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler));
}
cargo
}
- pub fn ensure<S: Step<'a> + Serialize>(&'a self, step: S) -> S::Output
- where
- S::Output: 'a
- {
- let key = Cache::to_key(&step);
+ fn maybe_run<S: Step>(&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 self, step: S) -> S::Output {
+ let type_id = TypeId::of::<S>();
{
let mut stack = self.stack.borrow_mut();
- if stack.contains(&key) {
+ for &(stack_type_id, ref stack_step) in stack.iter() {
+ if !(type_id == stack_type_id && step == *stack_step.downcast_ref().unwrap()) {
+ 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((type_id, 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_type_id, cur_step) = stack.pop().expect("step stack empty");
+ assert_eq!((cur_type_id, cur_step.downcast_ref()), (type_id, 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
}
}