rustpkg now uses the workcache library to avoid recompilation.
Hooray!
#[test]
fn test() {
use std::io::WriterUtil;
- use std::run;
+ use std::{os, run};
let pth = Path("foo.c");
{
r.unwrap().write_str("int main() { return 0; }");
}
- let cx = Context::new(RWArc::new(Database::new(Path("db.json"))),
+ let db_path = os::self_exe_path().expect("workcache::test failed").pop().push("db.json");
+ if os::path_exists(&db_path) {
+ os::remove_file(&db_path);
+ }
+
+ let cx = Context::new(RWArc::new(Database::new(db_path)),
RWArc::new(Logger::new()),
Arc::new(TreeMap::new()));
use package_id::*;
use package_source::*;
use version::Version;
+use workcache_support::*;
+use extra::arc::{Arc,RWArc};
+use extra::workcache;
+use extra::workcache::*;
use std::os;
-use std::hashmap::*;
+use extra::treemap::TreeMap;
/// Convenience functions intended for calling from pkg.rs
+/// p is where to put the cache file for dependencies
+pub fn default_ctxt(p: Path) -> BuildCtx {
+ new_default_ctx(new_workcache_cx(&p), p)
+}
-fn default_ctxt(p: @Path) -> Ctx {
- Ctx {
- use_rust_path_hack: false,
- sysroot_opt: Some(p),
- json: false,
- dep_cache: @mut HashMap::new()
+pub fn new_default_ctx(c: Context, p: Path) -> BuildCtx {
+ BuildCtx {
+ cx: Ctx { use_rust_path_hack: false,
+ sysroot_opt: p },
+ workcache_cx: c
}
}
-pub fn build_lib(sysroot: @Path, root: Path, name: ~str, version: Version,
- lib: Path) {
+fn file_is_fresh(path: &str, in_hash: &str) -> bool {
+ in_hash == digest_file_with_date(&Path(path))
+}
- let pkg_src = PkgSrc {
- root: root,
- id: PkgId{ version: version, ..PkgId::new(name)},
- libs: ~[mk_crate(lib)],
- mains: ~[],
- tests: ~[],
- benchs: ~[]
- };
- pkg_src.build(&default_ctxt(sysroot), ~[]);
+fn binary_is_fresh(path: &str, in_hash: &str) -> bool {
+ in_hash == digest_only_date(&Path(path))
}
-pub fn build_exe(sysroot: @Path, root: Path, name: ~str, version: Version, main: Path) {
- let pkg_src = PkgSrc {
- root: root,
- id: PkgId{ version: version, ..PkgId::new(name)},
- libs: ~[],
- mains: ~[mk_crate(main)],
- tests: ~[],
- benchs: ~[]
+
+pub fn new_workcache_cx(p: &Path) -> Context {
+ let db_file = p.push("rustpkg_db.json"); // ??? probably wrong
+ debug!("Workcache database file: %s", db_file.to_str());
+ let db = RWArc::new(Database::new(db_file));
+ let lg = RWArc::new(Logger::new());
+ let cfg = Arc::new(TreeMap::new());
+ let mut rslt: FreshnessMap = TreeMap::new();
+ // Set up freshness functions for every type of dependency rustpkg
+ // knows about
+ rslt.insert(~"file", file_is_fresh);
+ rslt.insert(~"binary", binary_is_fresh);
+ workcache::Context::new_with_freshness(db, lg, cfg, Arc::new(rslt))
+}
+
+pub fn build_lib(sysroot: Path, root: Path, name: ~str, version: Version,
+ lib: Path) {
+ let cx = default_ctxt(sysroot);
+ let subroot = root.clone();
+ let subversion = version.clone();
+ let sublib = lib.clone();
+ do cx.workcache_cx.with_prep(name) |prep| {
+ let pkg_src = PkgSrc {
+ workspace: subroot.clone(),
+ start_dir: subroot.push("src").push(name),
+ id: PkgId{ version: subversion.clone(), ..PkgId::new(name)},
+ libs: ~[mk_crate(sublib.clone())],
+ mains: ~[],
+ tests: ~[],
+ benchs: ~[]
+ };
+ pkg_src.declare_inputs(prep);
+ let subcx = cx.clone();
+ let subsrc = pkg_src.clone();
+ do prep.exec |exec| {
+ subsrc.clone().build(exec, &subcx.clone(), ~[]);
+ }
};
- pkg_src.build(&default_ctxt(sysroot), ~[]);
+}
+pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version,
+ main: Path) {
+ let cx = default_ctxt(sysroot);
+ let subroot = root.clone();
+ let submain = main.clone();
+ do cx.workcache_cx.with_prep(name) |prep| {
+ let pkg_src = PkgSrc {
+ workspace: subroot.clone(),
+ start_dir: subroot.push("src").push(name),
+ id: PkgId{ version: version.clone(), ..PkgId::new(name)},
+ libs: ~[],
+ mains: ~[mk_crate(submain.clone())],
+ tests: ~[],
+ benchs: ~[]
+ };
+ pkg_src.declare_inputs(prep);
+ let subsrc = pkg_src.clone();
+ let subcx = cx.clone();
+ do prep.exec |exec| {
+ subsrc.clone().build(exec, &subcx.clone(), ~[]);
+ }
+ }
}
-pub fn install_lib(sysroot: @Path,
+pub fn install_lib(sysroot: Path,
workspace: Path,
name: ~str,
lib_path: Path,
debug!("workspace = %s", workspace.to_str());
// make a PkgSrc
let pkg_id = PkgId{ version: version, ..PkgId::new(name)};
- let pkg_src = PkgSrc {
- root: workspace.clone(),
- id: pkg_id.clone(),
- libs: ~[mk_crate(lib_path)],
- mains: ~[],
- tests: ~[],
- benchs: ~[]
- };
let cx = default_ctxt(sysroot);
- pkg_src.build(&cx, ~[]);
+ let subpath = lib_path.clone();
+ do cx.workcache_cx.with_prep(pkg_id.to_str()) |prep| {
+ let pkg_src = PkgSrc {
+ workspace: workspace.clone(),
+ start_dir: subpath.push("src").push(name),
+ id: pkg_id.clone(),
+ libs: ~[mk_crate(subpath.clone())],
+ mains: ~[],
+ tests: ~[],
+ benchs: ~[]
+ };
+ pkg_src.declare_inputs(prep);
+ let subcx = cx.clone();
+ let subpkg_src = pkg_src.clone();
+ do prep.exec |exec| {
+ subpkg_src.clone().build(exec, &subcx.clone(), ~[]);
+ }
+ }
cx.install_no_build(&workspace, &pkg_id);
}
-pub fn install_exe(sysroot: @Path, workspace: Path, name: ~str, version: Version) {
- default_ctxt(sysroot).install(&workspace, &PkgId{ version: version,
- ..PkgId::new(name)});
-
+pub fn install_exe(sysroot: Path, workspace: Path, name: ~str, version: Version) {
+ let cx = default_ctxt(sysroot);
+ debug!("install_exe calling with_prep");
+ let pkgid = PkgId{ version: version, ..PkgId::new(name)};
+ cx.install(PkgSrc::new(workspace, false, pkgid));
}
fn mk_crate(p: Path) -> Crate {
pub use std::path::Path;
pub use package_id::PkgId;
+pub use std::libc;
+pub use std::libc::stat;
condition! {
pub bad_path: (Path, ~str) -> Path;
}
+condition! {
+ pub bad_stat: (Path, ~str) -> stat;
+}
+
condition! {
pub nonexistent_package: (PkgId, ~str) -> Path;
}
// Context data structure used by rustpkg
-
-use std::hashmap::HashMap;
use std::os;
+use extra::workcache;
+#[deriving(Clone)]
pub struct Ctx {
// If use_rust_path_hack is true, rustpkg searches for sources
// in *package* directories that are in the RUST_PATH (for example,
// FOO/src/bar-0.1 instead of FOO). The flag doesn't affect where
// rustpkg stores build artifacts.
use_rust_path_hack: bool,
- // Sysroot -- if this is None, uses rustc filesearch's
- // idea of the default
- sysroot_opt: Option<@Path>,
- // I'm not sure what this is for
- json: bool,
- // Cache of hashes of things already installed
- // though I'm not sure why the value is a bool
- dep_cache: @mut HashMap<~str, bool>,
+ // The root directory containing the Rust standard libraries
+ sysroot_opt: Path
+}
+
+#[deriving(Clone)]
+pub struct BuildCtx {
+ // Context for workcache
+ workcache_cx: workcache::Context,
+ // Everything else
+ cx: Ctx
+}
+
+impl BuildCtx {
+ pub fn sysroot_opt(&self) -> Path {
+ self.cx.sysroot_opt.clone()
+ }
+
+ pub fn sysroot_to_use(&self) -> Path {
+ self.cx.sysroot_to_use()
+ }
+}
+
+impl Ctx {
+ pub fn sysroot_opt(&self) -> Path {
+ self.sysroot_opt.clone()
+ }
}
impl Ctx {
/// Debugging
pub fn sysroot_opt_str(&self) -> ~str {
- match self.sysroot_opt {
- None => ~"[none]",
- Some(p) => p.to_str()
- }
+ self.sysroot_opt.to_str()
}
// Hack so that rustpkg can run either out of a rustc target dir,
// or the host dir
- pub fn sysroot_to_use(&self) -> Option<@Path> {
- if !in_target(self.sysroot_opt) {
- self.sysroot_opt
+ pub fn sysroot_to_use(&self) -> Path {
+ if !in_target(&self.sysroot_opt) {
+ self.sysroot_opt.clone()
}
else {
- self.sysroot_opt.map(|p| { @p.pop().pop().pop() })
+ self.sysroot_opt.pop().pop().pop()
}
- }
+ }
}
/// We assume that if ../../rustc exists, then we're running
/// rustpkg from a Rust target directory. This is part of a
/// kludgy hack used to adjust the sysroot.
-pub fn in_target(sysroot_opt: Option<@Path>) -> bool {
- match sysroot_opt {
- None => false,
- Some(p) => {
- debug!("Checking whether %s is in target", p.to_str());
- os::path_is_dir(&p.pop().pop().push("rustc"))
- }
- }
+pub fn in_target(sysroot_opt: &Path) -> bool {
+ debug!("Checking whether %s is in target", sysroot_opt.to_str());
+ os::path_is_dir(&sysroot_opt.pop().pop().push("rustc"))
}
// Did the user request a specific version?
let s = match split_version(s) {
Some((path, v)) => {
- debug!("s = %s, path = %s, v = %s", s, path, v.to_str());
given_version = Some(v);
path
}
None => {
- debug!("%s has no explicit version", s);
s
}
};
}
};
- debug!("path = %s", path.to_str());
PkgId {
path: path.clone(),
short_name: short_name.to_owned(),
use crate::Crate;
use messages::*;
use source_control::{git_clone, git_clone_general};
-use path_util::{pkgid_src_in_workspace, find_dir_using_rust_path_hack, default_workspace};
+use path_util::{find_dir_using_rust_path_hack, default_workspace};
use util::compile_crate;
use workspace::is_workspace;
+use workcache_support;
+use extra::workcache;
// An enumeration of the unpacked source of a package workspace.
// This contains a list of files found in the source workspace.
+#[deriving(Clone)]
pub struct PkgSrc {
- root: Path, // root of where the package source code lives
+ workspace: Path, // root of where the package source code lives
+ start_dir: Path, // dir to start looking in for packages -- normally
+ // this is workspace/src/id but it may be just workspace
id: PkgId,
libs: ~[Crate],
mains: ~[Crate],
benchs: ~[Crate],
}
+impl ToStr for PkgSrc {
+ fn to_str(&self) -> ~str {
+ fmt!("Package ID %s in start dir %s [workspace = %s]",
+ self.id.to_str(),
+ self.start_dir.to_str(), self.workspace.to_str())
+ }
+}
condition! {
// #6009: should this be pub or not, when #8215 is fixed?
- build_err: (~str) -> ();
+ build_err: (~str) -> ~str;
}
impl PkgSrc {
- pub fn new(src_dir: &Path, id: &PkgId) -> PkgSrc {
- PkgSrc {
- root: (*src_dir).clone(),
- id: (*id).clone(),
- libs: ~[],
- mains: ~[],
- tests: ~[],
- benchs: ~[]
- }
- }
+ pub fn new(workspace: Path, use_rust_path_hack: bool, id: PkgId) -> PkgSrc {
+ use conditions::nonexistent_package::cond;
+ debug!("Checking package source for package ID %s, \
+ workspace = %s", id.to_str(), workspace.to_str());
- fn check_dir(&self, cx: &Ctx) -> Path {
- use conditions::nonexistent_package::cond;
+ let mut to_try = ~[];
+ if use_rust_path_hack {
+ to_try.push(workspace.clone());
+ }
+ else {
+ let result = workspace.push("src").push_rel(&id.path.pop()).push(fmt!("%s-%s",
+ id.short_name, id.version.to_str()));
+ to_try.push(result);
+ to_try.push(workspace.push("src").push_rel(&id.path));
+ }
- debug!("Pushing onto root: %s | %s", self.id.path.to_str(), self.root.to_str());
+ debug!("Checking dirs: %?", to_try.map(|s| s.to_str()).connect(":"));
- let dirs = pkgid_src_in_workspace(&self.id, &self.root);
- debug!("Checking dirs: %?", dirs.map(|s| s.to_str()).connect(":"));
- let path = dirs.iter().find(|&d| os::path_exists(d));
+ let path = to_try.iter().find(|&d| os::path_exists(d));
- let dir = match path {
+ let dir: Path = match path {
Some(d) => (*d).clone(),
None => {
- match self.fetch_git() {
+ let mut ok_d = None;
+ for w in to_try.iter() {
+ debug!("Calling fetch_git on %s", w.to_str());
+ let gf = PkgSrc::fetch_git(w, &id);
+ for p in gf.iter() {
+ ok_d = Some(p.clone());
+ break;
+ }
+ if ok_d.is_some() { break; }
+ }
+ match ok_d {
Some(d) => d,
None => {
- match find_dir_using_rust_path_hack(cx, &self.id) {
- Some(d) => d,
- None => cond.raise((self.id.clone(),
- ~"supplied path for package dir does not \
- exist, and couldn't interpret it as a URL fragment"))
+ if use_rust_path_hack {
+ match find_dir_using_rust_path_hack(&id) {
+ Some(d) => d,
+ None => cond.raise((id.clone(),
+ ~"supplied path for package dir does not \
+ exist, and couldn't interpret it as a URL fragment"))
+ }
+ }
+ else {
+ cond.raise((id.clone(),
+ ~"supplied path for package dir does not \
+ exist, and couldn't interpret it as a URL fragment"))
+
}
}
}
}
};
- debug!("For package id %s, returning %s", self.id.to_str(), dir.to_str());
+ debug!("For package id %s, returning %s", id.to_str(), dir.to_str());
+
if !os::path_is_dir(&dir) {
- cond.raise((self.id.clone(), ~"supplied path for package dir is a \
+ cond.raise((id.clone(), ~"supplied path for package dir is a \
non-directory"));
}
- dir
+ PkgSrc {
+ workspace: workspace,
+ start_dir: dir,
+ id: id,
+ libs: ~[],
+ mains: ~[],
+ tests: ~[],
+ benchs: ~[]
+ }
}
/// Try interpreting self's package id as a git repository, and try
/// if this was successful, None otherwise. Similarly, if the package id
/// refers to a git repo on the local version, also check it out.
/// (right now we only support git)
- pub fn fetch_git(&self) -> Option<Path> {
+ pub fn fetch_git(local: &Path, pkgid: &PkgId) -> Option<Path> {
use conditions::failed_to_create_temp_dir::cond;
// We use a temporary directory because if the git clone fails,
None => cond.raise(~"Failed to create temporary directory for fetching git sources")
};
- let mut local = self.root.push("src");
- local = local.push(self.id.to_str());
-
- debug!("Checking whether %s exists locally. Cwd = %s, does it? %?",
- self.id.path.to_str(),
+ debug!("Checking whether %s (path = %s) exists locally. Cwd = %s, does it? %?",
+ pkgid.to_str(), pkgid.path.to_str(),
os::getcwd().to_str(),
- os::path_exists(&self.id.path));
+ os::path_exists(&pkgid.path));
- if os::path_exists(&self.id.path) {
+ if os::path_exists(&pkgid.path) {
debug!("%s exists locally! Cloning it into %s",
- self.id.path.to_str(), local.to_str());
+ pkgid.path.to_str(), local.to_str());
// Ok to use local here; we know it will succeed
- git_clone(&self.id.path, &local, &self.id.version);
- return Some(local);
+ git_clone(&pkgid.path, local, &pkgid.version);
+ return Some(local.clone());
}
- if self.id.path.components().len() < 2 {
+ if pkgid.path.components().len() < 2 {
// If a non-URL, don't bother trying to fetch
return None;
}
- let url = fmt!("https://%s", self.id.path.to_str());
- note(fmt!("Fetching package: git clone %s %s [version=%s]",
- url, clone_target.to_str(), self.id.version.to_str()));
+ let url = fmt!("https://%s", pkgid.path.to_str());
+ debug!("Fetching package: git clone %s %s [version=%s]",
+ url, clone_target.to_str(), pkgid.version.to_str());
- if git_clone_general(url, &clone_target, &self.id.version) {
+ if git_clone_general(url, &clone_target, &pkgid.version) {
// since the operation succeeded, move clone_target to local
- if !os::rename_file(&clone_target, &local) {
+ if !os::rename_file(&clone_target, local) {
None
}
else {
- Some(local)
+ Some(local.clone())
}
}
else {
}
- // If a file named "pkg.rs" in the current directory exists,
+ // If a file named "pkg.rs" in the start directory exists,
// return the path for it. Otherwise, None
- pub fn package_script_option(&self, cwd: &Path) -> Option<Path> {
- let maybe_path = cwd.push("pkg.rs");
+ pub fn package_script_option(&self) -> Option<Path> {
+ let maybe_path = self.start_dir.push("pkg.rs");
+ debug!("package_script_option: checking whether %s exists", maybe_path.to_str());
if os::path_exists(&maybe_path) {
Some(maybe_path)
}
for c in p.components.slice(prefix, p.components.len()).iter() {
sub = sub.push(*c);
}
- debug!("found crate %s", sub.to_str());
+ debug!("Will compile crate %s", sub.to_str());
cs.push(Crate::new(&sub));
}
/// Infers crates to build. Called only in the case where there
/// is no custom build logic
- pub fn find_crates(&mut self, cx: &Ctx) {
+ pub fn find_crates(&mut self) {
use conditions::missing_pkg_files::cond;
- let dir = self.check_dir(cx);
- debug!("Called check_dir, I'm in %s", dir.to_str());
- let prefix = dir.components.len();
- debug!("Matching against %?", self.id.short_name);
- do os::walk_dir(&dir) |pth| {
+ let prefix = self.start_dir.components.len();
+ debug!("Matching against %s", self.id.short_name);
+ do os::walk_dir(&self.start_dir) |pth| {
let maybe_known_crate_set = match pth.filename() {
Some(filename) => match filename {
"lib.rs" => Some(&mut self.libs),
cond.raise(self.id.clone());
}
- debug!("found %u libs, %u mains, %u tests, %u benchs",
+ debug!("In %s, found %u libs, %u mains, %u tests, %u benchs",
+ self.start_dir.to_str(),
self.libs.len(),
self.mains.len(),
self.tests.len(),
}
fn build_crates(&self,
- ctx: &Ctx,
- src_dir: &Path,
+ ctx: &BuildCtx,
+ exec: &mut workcache::Exec,
destination_dir: &Path,
crates: &[Crate],
cfgs: &[~str],
what: OutputType) {
for crate in crates.iter() {
- let path = &src_dir.push_rel(&crate.file).normalize();
- note(fmt!("build_crates: compiling %s", path.to_str()));
- note(fmt!("build_crates: using as workspace %s", self.root.to_str()));
-
- let result = compile_crate(ctx,
- &self.id,
- path,
- // compile_crate wants the destination workspace
- destination_dir,
- crate.flags,
- crate.cfgs + cfgs,
- false,
- what);
- if !result {
- build_err::cond.raise(fmt!("build failure on %s",
- path.to_str()));
+ let path = self.start_dir.push_rel(&crate.file).normalize();
+ debug!("build_crates: compiling %s", path.to_str());
+ let path_str = path.to_str();
+ let cfgs = crate.cfgs + cfgs;
+
+ let result = {
+ // compile_crate should return the path of the output artifact
+ match compile_crate(ctx,
+ exec,
+ &self.id,
+ &path,
+ destination_dir,
+ crate.flags,
+ cfgs,
+ false,
+ what).map(|p| p.to_str()) {
+ Some(p) => p,
+ None => build_err::cond.raise(fmt!("build failure on %s",
+ path_str))
+
+ }
+ };
+ debug!("Result of compiling %s was %s", path_str, result);
+ }
+ }
+
+ /// Declare all the crate files in the package source as inputs
+ pub fn declare_inputs(&self, prep: &mut workcache::Prep) {
+ let to_do = ~[self.libs.clone(), self.mains.clone(),
+ self.tests.clone(), self.benchs.clone()];
+ for cs in to_do.iter() {
+ for c in cs.iter() {
+ let path = self.start_dir.push_rel(&c.file).normalize();
+ debug!("Declaring input: %s", path.to_str());
+ prep.declare_input("file",
+ path.to_str(),
+ workcache_support::digest_file_with_date(&path.clone()));
}
- debug!("Result of compiling %s was %?",
- path.to_str(), result);
}
}
- pub fn build(&self, ctx: &Ctx, cfgs: ~[~str]) -> Path {
+ // It would be better if build returned a Path, but then Path would have to derive
+ // Encodable.
+ pub fn build(&self, exec: &mut workcache::Exec, ctx: &BuildCtx, cfgs: ~[~str]) -> ~str {
use conditions::not_a_workspace::cond;
// Determine the destination workspace (which depends on whether
// we're using the rust_path_hack)
- let destination_workspace = if is_workspace(&self.root) {
- debug!("%s is indeed a workspace", self.root.to_str());
- self.root.clone()
+ let destination_workspace = if is_workspace(&self.workspace) {
+ debug!("%s is indeed a workspace", self.workspace.to_str());
+ self.workspace.clone()
}
- else {
+ else {
// It would be nice to have only one place in the code that checks
// for the use_rust_path_hack flag...
- if ctx.use_rust_path_hack {
+ if ctx.cx.use_rust_path_hack {
let rs = default_workspace();
debug!("Using hack: %s", rs.to_str());
rs
}
else {
cond.raise(fmt!("Package root %s is not a workspace; pass in --rust_path_hack \
- if you want to treat it as a package source", self.root.to_str()))
+ if you want to treat it as a package source",
+ self.workspace.to_str()))
}
};
- let dir = self.check_dir(ctx);
- debug!("Building libs in %s, destination = %s", dir.to_str(),
- destination_workspace.to_str());
- self.build_crates(ctx, &dir, &destination_workspace, self.libs, cfgs, Lib);
+ let libs = self.libs.clone();
+ let mains = self.mains.clone();
+ let tests = self.tests.clone();
+ let benchs = self.benchs.clone();
+ debug!("Building libs in %s, destination = %s",
+ destination_workspace.to_str(), destination_workspace.to_str());
+ self.build_crates(ctx, exec, &destination_workspace, libs, cfgs, Lib);
debug!("Building mains");
- self.build_crates(ctx, &dir, &destination_workspace, self.mains, cfgs, Main);
+ self.build_crates(ctx, exec, &destination_workspace, mains, cfgs, Main);
debug!("Building tests");
- self.build_crates(ctx, &dir, &destination_workspace, self.tests, cfgs, Test);
+ self.build_crates(ctx, exec, &destination_workspace, tests, cfgs, Test);
debug!("Building benches");
- self.build_crates(ctx, &dir, &destination_workspace, self.benchs, cfgs, Bench);
- destination_workspace
+ self.build_crates(ctx, exec, &destination_workspace, benchs, cfgs, Bench);
+ destination_workspace.to_str()
}
}
pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install};
pub use version::{Version, NoVersion, split_version_general, try_parsing_version};
pub use rustc::metadata::filesearch::rust_path;
-use context::Ctx;
use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
use std::os::mkdir_recursive;
let mut found = None;
do os::walk_dir(&src_dir) |p| {
- debug!("=> p = %s", p.to_str());
-
if os::path_is_dir(p) {
- debug!("p = %s, path = %s [%s]", p.to_str(), pkgid.path.to_str(),
- src_dir.push_rel(&pkgid.path).to_str());
-
if *p == src_dir.push_rel(&pkgid.path) || {
let pf = p.filename();
do pf.iter().any |pf| {
match split_version_general(g, '-') {
None => false,
Some((ref might_match, ref vers)) => {
- debug!("might_match = %s, vers = %s", *might_match,
- vers.to_str());
*might_match == pkgid.short_name
&& (pkgid.version == *vers || pkgid.version == NoVersion)
}
true
};
- debug!(if found.is_some() { fmt!("Found %s in %s", pkgid.to_str(), workspace.to_str()) }
- else { fmt!("Didn't find %s in %s", pkgid.to_str(), workspace.to_str()) });
+ if found.is_some() {
+ debug!("Found %s in %s", pkgid.to_str(), workspace.to_str());
+ }
+ else {
+ debug!("Didn't find %s in %s", pkgid.to_str(), workspace.to_str());
+ }
found
}
-/// Returns a list of possible directories
-/// for <pkgid>'s source files in <workspace>.
-/// Doesn't check that any of them exist.
-/// (for example, try both with and without the version)
-pub fn pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> ~[Path] {
- let mut results = ~[];
- let result = workspace.push("src").push(fmt!("%s-%s",
- pkgid.path.to_str(), pkgid.version.to_str()));
- results.push(result);
- results.push(workspace.push("src").push_rel(&pkgid.path));
- results
-}
-
-/// Returns a src for pkgid that does exist -- None if none of them do
-pub fn first_pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
- let rs = pkgid_src_in_workspace(pkgid, workspace);
- do rs.iter().find |&p| {
- os::path_exists(p)
- }.map(|p| (**p).clone())
-}
-
/// Figure out what the executable name for <pkgid> in <workspace>'s build
/// directory is, and if the file exists, return it.
pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
os::path_exists(&dir.push(file))
}
-pub fn find_dir_using_rust_path_hack(cx: &Ctx, p: &PkgId) -> Option<Path> {
- if !cx.use_rust_path_hack {
- return None;
- }
+pub fn find_dir_using_rust_path_hack(p: &PkgId) -> Option<Path> {
let rp = rust_path();
for dir in rp.iter() {
debug!("In find_dir_using_rust_path_hack: checking dir %s", dir.to_str());
use std::{io, os, result, run, str};
pub use std::path::Path;
-use std::hashmap::HashMap;
+use extra::workcache;
+use extra::arc::RWArc;
use rustc::driver::{driver, session};
use rustc::metadata::filesearch;
use rustc::metadata::filesearch::rust_path;
use syntax::{ast, diagnostic};
use util::*;
use messages::*;
-use path_util::{build_pkg_id_in_workspace, first_pkgid_src_in_workspace};
+use path_util::build_pkg_id_in_workspace;
use path_util::{U_RWX, in_rust_path};
use path_util::{built_executable_in_workspace, built_library_in_workspace, default_workspace};
use path_util::{target_executable_in_workspace, target_library_in_workspace};
use source_control::is_git_dir;
use workspace::{each_pkg_parent_workspace, pkg_parent_workspaces, cwd_to_workspace};
-use context::Ctx;
+use context::{BuildCtx, Ctx};
use package_id::PkgId;
use package_source::PkgSrc;
+use workcache_support::{discover_outputs, digest_only_date};
pub mod api;
mod conditions;
mod tests;
mod util;
mod version;
+pub mod workcache_support;
mod workspace;
pub mod usage;
struct PkgScript<'self> {
/// Uniquely identifies this package
id: &'self PkgId,
- // Used to have this field: deps: ~[(~str, Option<~str>)]
- // but I think it shouldn't be stored here
- /// The contents of the package script: either a file path,
- /// or a string containing the text of the input
- input: driver::input,
+ /// File path for the package script
+ input: Path,
/// The session to use *only* for compiling the custom
/// build script
sess: session::Session,
crate_type: session::bin_crate,
.. (*session::basic_options()).clone()
};
- let input = driver::file_input(script);
+ let input = driver::file_input(script.clone());
let sess = driver::build_session(options, diagnostic::emit);
let cfg = driver::build_configuration(sess);
let crate = driver::phase_1_parse_input(sess, cfg.clone(), &input);
PkgScript {
id: id,
- input: input,
+ input: script,
sess: sess,
cfg: cfg,
crate: crate,
/// is the command to pass to it (e.g., "build", "clean", "install")
/// Returns a pair of an exit code and list of configs (obtained by
/// calling the package script's configs() function if it exists
- // FIXME (#4432): Use workcache to only compile the script when changed
- fn run_custom(&self, sysroot: @Path) -> (~[~str], ExitCode) {
+ fn run_custom(&self, exec: &mut workcache::Exec, sysroot: &Path) -> (~[~str], ExitCode) {
let sess = self.sess;
debug!("Working directory = %s", self.build_dir.to_str());
// Collect together any user-defined commands in the package script
let crate = util::ready_crate(sess, self.crate);
debug!("Building output filenames with script name %s",
- driver::source_name(&self.input));
+ driver::source_name(&driver::file_input(self.input.clone())));
let exe = self.build_dir.push(~"pkg" + util::exe_suffix());
util::compile_crate_from_input(&self.input,
+ exec,
&self.build_dir,
sess,
crate);
debug!("Running program: %s %s %s", exe.to_str(),
sysroot.to_str(), "install");
+ // Discover the output
+ exec.discover_output("binary", exe.to_str(), digest_only_date(&exe));
// FIXME #7401 should support commands besides `install`
let status = run::process_status(exe.to_str(), [sysroot.to_str(), ~"install"]);
if status != 0 {
fn hash(&self) -> ~str {
self.id.hash()
}
-
}
pub trait CtxMethods {
fn run(&self, cmd: &str, args: ~[~str]);
fn do_cmd(&self, _cmd: &str, _pkgname: &str);
+ fn build_from_src(&self, pkg_src: PkgSrc);
/// Returns the destination workspace
- fn build(&self, workspace: &Path, pkgid: &PkgId) -> Path;
+ fn build(&self, exec: &mut workcache::Exec, pkg_src: PkgSrc) -> Path;
fn clean(&self, workspace: &Path, id: &PkgId);
fn info(&self);
- fn install(&self, workspace: &Path, id: &PkgId);
- fn install_no_build(&self, workspace: &Path, id: &PkgId);
+ fn install(&self, src: PkgSrc) -> (~[Path], ~[(~str, ~str)]);
+ /// Returns a list of installed files
+ fn install_no_build(&self, workspace: &Path, id: &PkgId) -> ~[Path];
fn prefer(&self, _id: &str, _vers: Option<~str>);
fn test(&self);
fn uninstall(&self, _id: &str, _vers: Option<~str>);
fn unprefer(&self, _id: &str, _vers: Option<~str>);
}
-impl CtxMethods for Ctx {
+impl CtxMethods for BuildCtx {
+ fn build_from_src(&self, pkg_src: PkgSrc) {
+ let tag = pkg_src.id.to_str();
+ debug!("package source = %s", pkg_src.to_str());
+ do self.workcache_cx.with_prep(tag) |prep| {
+ let subsrc = pkg_src.clone();
+ let subself = self.clone();
+ declare_package_script_dependency(prep, &subsrc);
+ pkg_src.declare_inputs(prep);
+ do prep.exec |exec| {
+ subself.build(exec, subsrc.clone());
+ }
+ }
+ }
fn run(&self, cmd: &str, args: ~[~str]) {
match cmd {
"build" => {
if args.len() < 1 {
match cwd_to_workspace() {
- None if self.use_rust_path_hack => {
+ None if self.cx.use_rust_path_hack => {
let cwd = os::getcwd();
- self.build(&cwd, &PkgId::new(cwd.components[cwd.components.len() - 1]));
+ let pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]);
+ self.build_from_src(PkgSrc::new(cwd, true, pkgid));
}
None => { usage::build(); return; }
- Some((ws, pkgid)) => { self.build(&ws, &pkgid); }
+ Some((ws, pkgid)) => {
+ self.build_from_src(PkgSrc::new(ws, false, pkgid));
+ }
}
}
else {
// The package id is presumed to be the first command-line
// argument
let pkgid = PkgId::new(args[0].clone());
- do each_pkg_parent_workspace(self, &pkgid) |workspace| {
+ do each_pkg_parent_workspace(&self.cx, &pkgid) |workspace| {
debug!("found pkg %s in workspace %s, trying to build",
pkgid.to_str(), workspace.to_str());
- self.build(workspace, &pkgid);
+ let pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone());
+ self.build_from_src(pkg_src);
true
};
}
self.info();
}
"install" => {
- if args.len() < 1 {
+ if args.len() < 1 {
match cwd_to_workspace() {
- None if self.use_rust_path_hack => {
+ None if self.cx.use_rust_path_hack => {
let cwd = os::getcwd();
- self.install(&cwd,
- &PkgId::new(cwd.components[cwd.components.len() - 1]));
+ let inferred_pkgid =
+ PkgId::new(cwd.components[cwd.components.len() - 1]);
+ self.install(PkgSrc::new(cwd, true, inferred_pkgid));
}
None => { usage::install(); return; }
- Some((ws, pkgid)) => self.install(&ws, &pkgid),
- }
+ Some((ws, pkgid)) => {
+ let pkg_src = PkgSrc::new(ws, false, pkgid);
+ self.install(pkg_src);
+ }
+ }
}
else {
// The package id is presumed to be the first command-line
// argument
let pkgid = PkgId::new(args[0]);
- let workspaces = pkg_parent_workspaces(self, &pkgid);
+ let workspaces = pkg_parent_workspaces(&self.cx, &pkgid);
debug!("package ID = %s, found it in %? workspaces",
pkgid.to_str(), workspaces.len());
if workspaces.is_empty() {
let rp = rust_path();
assert!(!rp.is_empty());
- let src = PkgSrc::new(&rp[0], &pkgid);
- src.fetch_git();
- self.install(&rp[0], &pkgid);
+ let src = PkgSrc::new(rp[0].clone(), false, pkgid.clone());
+ self.install(src);
}
else {
- do each_pkg_parent_workspace(self, &pkgid) |workspace| {
- self.install(workspace, &pkgid);
- true
+ for workspace in workspaces.iter() {
+ let src = PkgSrc::new(workspace.clone(),
+ self.cx.use_rust_path_hack,
+ pkgid.clone());
+ self.install(src);
};
}
}
else {
let rp = rust_path();
assert!(!rp.is_empty());
- do each_pkg_parent_workspace(self, &pkgid) |workspace| {
+ do each_pkg_parent_workspace(&self.cx, &pkgid) |workspace| {
path_util::uninstall_package_from(workspace, &pkgid);
note(fmt!("Uninstalled package %s (was installed in %s)",
pkgid.to_str(), workspace.to_str()));
/// Returns the destination workspace
/// In the case of a custom build, we don't know, so we just return the source workspace
- fn build(&self, workspace: &Path, pkgid: &PkgId) -> Path {
+ fn build(&self, exec: &mut workcache::Exec, pkg_src: PkgSrc) -> Path {
+
+ let pkg_src = &mut pkg_src.clone(); // :-o
+ let workspace = pkg_src.workspace.clone();
+ let pkgid = pkg_src.id.clone();
+
debug!("build: workspace = %s (in Rust path? %? is git dir? %? \
- pkgid = %s", workspace.to_str(),
- in_rust_path(workspace), is_git_dir(&workspace.push_rel(&pkgid.path)),
- pkgid.to_str());
- let src_dir = first_pkgid_src_in_workspace(pkgid, workspace);
+ pkgid = %s pkgsrc start_dir = %s", workspace.to_str(),
+ in_rust_path(&workspace), is_git_dir(&workspace.push_rel(&pkgid.path)),
+ pkgid.to_str(), pkg_src.start_dir.to_str());
// If workspace isn't in the RUST_PATH, and it's a git repo,
// then clone it into the first entry in RUST_PATH, and repeat
- debug!("%? %? %s", in_rust_path(workspace),
- is_git_dir(&workspace.push_rel(&pkgid.path)),
- workspace.to_str());
- if !in_rust_path(workspace) && is_git_dir(&workspace.push_rel(&pkgid.path)) {
+ if !in_rust_path(&workspace) && is_git_dir(&workspace.push_rel(&pkgid.path)) {
let out_dir = default_workspace().push("src").push_rel(&pkgid.path);
source_control::git_clone(&workspace.push_rel(&pkgid.path),
&out_dir, &pkgid.version);
let default_ws = default_workspace();
debug!("Calling build recursively with %? and %?", default_ws.to_str(),
pkgid.to_str());
- return self.build(&default_ws, pkgid);
+ return self.build(exec, PkgSrc::new(default_ws, false, pkgid.clone()));
}
- // Create the package source
- let mut src = PkgSrc::new(workspace, pkgid);
- debug!("Package src = %?", src);
-
// Is there custom build logic? If so, use it
- let pkg_src_dir = src_dir;
let mut custom = false;
- debug!("Package source directory = %?", pkg_src_dir);
- let cfgs = match pkg_src_dir.chain_ref(|p| src.package_script_option(p)) {
+ debug!("Package source directory = %s", pkg_src.to_str());
+ let opt = pkg_src.package_script_option();
+ debug!("Calling pkg_script_option on %?", opt);
+ let cfgs = match pkg_src.package_script_option() {
Some(package_script_path) => {
- let sysroot = self.sysroot_to_use().expect("custom build needs a sysroot");
- let pscript = PkgScript::parse(sysroot,
- package_script_path,
- workspace,
- pkgid);
- let (cfgs, hook_result) = pscript.run_custom(sysroot);
+ let sysroot = self.sysroot_to_use();
+ let (cfgs, hook_result) = {
+ let pscript = PkgScript::parse(@sysroot.clone(),
+ package_script_path.clone(),
+ &workspace.clone(),
+ &pkgid);
+ pscript.run_custom(exec, &sysroot)
+ };
debug!("Command return code = %?", hook_result);
if hook_result != 0 {
fail!("Error running custom build command")
// the build already. Otherwise...
if !custom {
// Find crates inside the workspace
- src.find_crates(self);
+ pkg_src.find_crates();
// Build it!
- src.build(self, cfgs)
+ let rs_path = pkg_src.build(exec, self, cfgs);
+ Path(rs_path)
}
else {
// Just return the source workspace
fail!("info not yet implemented");
}
- fn install(&self, workspace: &Path, id: &PkgId) {
- // Also should use workcache to not build if not necessary.
- let destination_workspace = self.build(workspace, id);
- // See #7402: This still isn't quite right yet; we want to
- // install to the first workspace in the RUST_PATH if there's
- // a non-default RUST_PATH. This code installs to the same
- // workspace the package was built in.
- debug!("install: destination workspace = %s, id = %s",
- destination_workspace.to_str(), id.to_str());
- self.install_no_build(&destination_workspace, id);
-
+ /// Returns a pair. First component is a list of installed paths,
+ /// second is a list of declared and discovered inputs
+ fn install(&self, pkg_src: PkgSrc) -> (~[Path], ~[(~str, ~str)]) {
+
+ let id = &pkg_src.id;
+
+ let installed_files = RWArc::new(~[]);
+ let inputs = RWArc::new(~[]);
+ // FIXME #7402: Use RUST_PATH to determine target dir
+ let f: &fn(&mut workcache::Prep) = |prep| {
+ let sub_inputs = inputs.clone();
+ let sub_files = installed_files.clone();
+ let subsrc = pkg_src.clone();
+ let subself = self.clone();
+ let id_str = id.to_str();
+ let sub_id = id.clone();
+ sub_inputs.write(|r| *r = prep.lookup_declared_inputs().map(|v|
+ { (~"file", (*v).clone()) }));
+ do prep.exec |exec| {
+ let destination_workspace = subself.build(exec, subsrc.clone()).to_str();
+ // See #7402: This still isn't quite right yet; we want to
+ // install to the first workspace in the RUST_PATH if there's
+ // a non-default RUST_PATH. This code installs to the same
+ // workspace the package was built in.
+ debug!("install: destination workspace = %s, id = %s",
+ destination_workspace, id_str);
+ let result = subself.install_no_build(&Path(destination_workspace), &sub_id);
+ debug!("install: id = %s, about to call discover_outputs, %?",
+ id_str, result.to_str());
+
+ discover_outputs(exec, result.clone());
+ sub_files.write(|r| { *r = result.clone(); });
+ sub_inputs.write(|r| { *r = *r + exec.lookup_discovered_inputs() });
+ }
+ };
+ self.workcache_cx.with_prep(id.to_str(), |p| pkg_src.declare_inputs(p));
+ self.workcache_cx.with_prep(id.to_str(), f);
+ (installed_files.unwrap(), inputs.unwrap())
}
- fn install_no_build(&self, workspace: &Path, id: &PkgId) {
+ fn install_no_build(&self, workspace: &Path, id: &PkgId) -> ~[Path] {
use conditions::copy_failed::cond;
// Now copy stuff into the install dirs
target_exec.to_str(), target_lib,
maybe_executable, maybe_library);
+ let mut outputs = ~[];
+
for exec in maybe_executable.iter() {
debug!("Copying: %s -> %s", exec.to_str(), target_exec.to_str());
if !(os::mkdir_recursive(&target_exec.dir_path(), U_RWX) &&
os::copy_file(exec, &target_exec)) {
cond.raise(((*exec).clone(), target_exec.clone()));
}
+ outputs.push(target_exec.clone());
}
for lib in maybe_library.iter() {
let target_lib = target_lib.clone().expect(fmt!("I built %s but apparently \
os::copy_file(lib, &target_lib)) {
cond.raise(((*lib).clone(), target_lib.clone()));
}
+ outputs.push(target_lib.clone());
}
+ outputs
}
fn prefer(&self, _id: &str, _vers: Option<~str>) {
}
}
-
pub fn main() {
io::println("WARNING: The Rust package manager is experimental and may be unstable");
let args = os::args();
pub fn main_args(args: &[~str]) {
let opts = ~[getopts::optflag("h"), getopts::optflag("help"),
- getopts::optflag("j"), getopts::optflag("json"),
getopts::optmulti("c"), getopts::optmulti("cfg"),
getopts::optflag("v"), getopts::optflag("version"),
getopts::optflag("r"), getopts::optflag("rust-path-hack")];
};
let help = getopts::opt_present(matches, "h") ||
getopts::opt_present(matches, "help");
- let json = getopts::opt_present(matches, "j") ||
- getopts::opt_present(matches, "json");
if getopts::opt_present(matches, "v") ||
getopts::opt_present(matches, "version") {
getopts::opt_present(matches, "rust-path-hack");
let mut args = matches.free.clone();
-
args.shift();
if (args.len() < 1) {
// I had to add this type annotation to get the code to typecheck
let mut remaining_args: ~[~str] = remaining_args.map(|s| (*s).clone()).collect();
remaining_args.shift();
- let sroot = Some(@filesearch::get_or_default_sysroot());
- debug!("Using sysroot: %?", sroot);
- Ctx {
- use_rust_path_hack: use_rust_path_hack,
- sysroot_opt: sroot, // Currently, only tests override this
- json: json,
- dep_cache: @mut HashMap::new()
+ let sroot = filesearch::get_or_default_sysroot();
+ debug!("Using sysroot: %s", sroot.to_str());
+ debug!("Will store workcache in %s", default_workspace().to_str());
+ BuildCtx {
+ cx: Ctx {
+ use_rust_path_hack: use_rust_path_hack,
+ sysroot_opt: sroot, // Currently, only tests override this
+ },
+ workcache_cx: api::default_ctxt(default_workspace()).workcache_cx // ???
}.run(*cmd, remaining_args)
}
pub fn src_dir() -> Path {
os::getcwd()
}
+
+fn declare_package_script_dependency(prep: &mut workcache::Prep, pkg_src: &PkgSrc) {
+ match pkg_src.package_script_option() {
+ Some(ref p) => prep.declare_input("file", p.to_str(),
+ workcache_support::digest_file_with_date(p)),
+ None => ()
+ }
+}
use path_util::{installed_library_in_workspace, rust_path};
use version::Version;
-/// If a library with path `p` matching pkg_id's name exists under sroot_opt,
-/// return Some(p). Return None if there's no such path or if sroot_opt is None.
-pub fn find_library_in_search_path(sroot_opt: Option<@Path>, short_name: &str) -> Option<Path> {
- do sroot_opt.chain |sroot| {
- debug!("Will search for a library with short name %s in \
- %s", short_name, (sroot.push("lib")).to_str());
- installed_library_in_workspace(short_name, sroot)
- }
-}
-
/// If some workspace `p` in the RUST_PATH contains a package matching short_name,
/// return Some(p) (returns the first one of there are multiple matches.) Return
/// None if there's no such path.
// rustpkg unit tests
-use context::Ctx;
-use std::hashmap::HashMap;
-use std::{io, libc, os, run, str};
+use context::{BuildCtx, Ctx};
+use std::{io, libc, os, run, str, task};
+use extra::arc::Arc;
+use extra::arc::RWArc;
use extra::tempfile::mkdtemp;
+use extra::workcache::{Context, Database, Logger};
+use extra::treemap::TreeMap;
use std::run::ProcessOutput;
use installed_packages::list_installed_packages;
use package_id::{PkgId};
use rustc::metadata::filesearch::rust_path;
use rustc::driver::driver::host_triple;
use target::*;
+use package_source::PkgSrc;
/// Returns the last-modified date as an Option
fn datestamp(p: &Path) -> Option<libc::time_t> {
p.stat().map(|stat| stat.st_mtime)
}
-fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx {
- Ctx {
- use_rust_path_hack: false,
- sysroot_opt: sysroot_opt,
- json: false,
- dep_cache: @mut HashMap::new()
+fn fake_ctxt(sysroot_opt: Path, workspace: &Path) -> BuildCtx {
+ let bcx = Context::new(RWArc::new(Database::new(workspace.push("rustpkg_db.json"))),
+ RWArc::new(Logger::new()),
+ Arc::new(TreeMap::new()));
+ BuildCtx {
+ workcache_cx: bcx,
+ cx: Ctx {
+ use_rust_path_hack: false,
+ sysroot_opt: sysroot_opt
+ }
}
}
}
fn output_file_name(workspace: &Path, short_name: &str) -> Path {
- workspace.push(fmt!("%s%s", short_name, os::EXE_SUFFIX))
+ workspace.push("build").push(short_name).push(fmt!("%s%s", short_name, os::EXE_SUFFIX))
}
fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
if run::process_output("touch", [p.to_str()]).status != 0 {
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
}
- break;
}
}
}
-/// Add a blank line at the end
+/// Add a comment at the end
fn frob_source_file(workspace: &Path, pkgid: &PkgId) {
use conditions::bad_path::cond;
let pkg_src_dir = workspace.push("src").push(pkgid.to_str());
let w = io::file_writer(p, &[io::Append]);
match w {
Err(s) => { let _ = cond.raise((p.clone(), fmt!("Bad path: %s", s))); }
- Ok(w) => w.write_line("")
+ Ok(w) => w.write_line("/* hi */")
}
}
None => fail!(fmt!("frob_source_file failed to find a source file in %s",
let sysroot = test_sysroot();
debug!("sysroot = %s", sysroot.to_str());
- let ctxt = fake_ctxt(Some(@sysroot));
let temp_pkg_id = fake_pkg();
let temp_workspace = mk_temp_workspace(&temp_pkg_id.path, &NoVersion).pop().pop();
+ let ctxt = fake_ctxt(sysroot, &temp_workspace);
debug!("temp_workspace = %s", temp_workspace.to_str());
// should have test, bench, lib, and main
- ctxt.install(&temp_workspace, &temp_pkg_id);
+ let src = PkgSrc::new(temp_workspace.clone(), false, temp_pkg_id.clone());
+ ctxt.install(src);
// Check that all files exist
let exec = target_executable_in_workspace(&temp_pkg_id, &temp_workspace);
debug!("exec = %s", exec.to_str());
#[test]
fn test_install_invalid() {
- use conditions::nonexistent_package::cond;
- use cond1 = conditions::missing_pkg_files::cond;
- use cond2 = conditions::not_a_workspace::cond;
-
- let ctxt = fake_ctxt(None);
+ let sysroot = test_sysroot();
let pkgid = fake_pkg();
let temp_workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir");
- let mut error_occurred = false;
- let mut error1_occurred = false;
- let mut error2_occurred = false;
- do cond1.trap(|_| {
- error1_occurred = true;
- }).inside {
- do cond.trap(|_| {
- error_occurred = true;
- temp_workspace.clone()
- }).inside {
- do cond2.trap(|_| {
- error2_occurred = true;
- temp_workspace.clone()
- }).inside {
- ctxt.install(&temp_workspace, &pkgid);
- }
- }
- }
- assert!(error_occurred && error1_occurred && error2_occurred);
+ let ctxt = fake_ctxt(sysroot, &temp_workspace);
+
+ // Uses task::try because of #9001
+ let result = do task::try {
+ let pkg_src = PkgSrc::new(temp_workspace.clone(), false, pkgid.clone());
+ ctxt.install(pkg_src);
+ };
+ // Not the best test -- doesn't test that we failed in the right way.
+ // Best we can do for now.
+ assert!(result == Err(()));
}
// Tests above should (maybe) be converted to shell out to rustpkg, too
}
#[test]
-#[ignore(reason = "Workcache not yet implemented -- see #7075")]
fn no_rebuilding() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
}
#[test]
-#[ignore(reason = "Workcache not yet implemented -- see #7075")]
fn no_rebuilding_dep() {
let p_id = PkgId::new("foo");
let dep_id = PkgId::new("bar");
let workspace = create_local_package_with_dep(&p_id, &dep_id);
command_line_test([~"build", ~"foo"], &workspace);
- let bar_date = datestamp(&lib_output_file_name(&workspace,
+ let bar_date_1 = datestamp(&lib_output_file_name(&workspace,
+ ".rust",
+ "bar"));
+ let foo_date_1 = datestamp(&output_file_name(&workspace, "foo"));
+
+ frob_source_file(&workspace, &p_id);
+ command_line_test([~"build", ~"foo"], &workspace);
+ let bar_date_2 = datestamp(&lib_output_file_name(&workspace,
".rust",
"bar"));
- let foo_date = datestamp(&output_file_name(&workspace, "foo"));
- assert!(bar_date < foo_date);
+ let foo_date_2 = datestamp(&output_file_name(&workspace, "foo"));
+ assert_eq!(bar_date_1, bar_date_2);
+ assert!(foo_date_1 < foo_date_2);
+ assert!(foo_date_1 > bar_date_1);
}
-// n.b. The following two tests are ignored; they worked "accidentally" before,
-// when the behavior was "always rebuild libraries" (now it's "never rebuild
-// libraries if they already exist"). They can be un-ignored once #7075 is done.
#[test]
-#[ignore(reason = "Workcache not yet implemented -- see #7075")]
fn do_rebuild_dep_dates_change() {
let p_id = PkgId::new("foo");
let dep_id = PkgId::new("bar");
}
#[test]
-#[ignore(reason = "Workcache not yet implemented -- see #7075")]
fn do_rebuild_dep_only_contents_change() {
let p_id = PkgId::new("foo");
let dep_id = PkgId::new("bar");
// except according to those terms.
use std::os;
+use extra::workcache;
use rustc::driver::{driver, session};
use extra::getopts::groups::getopts;
use syntax::ast_util::*;
use syntax::attr::AttrMetaMethods;
use rustc::back::link::output_type_exe;
use rustc::driver::session::{lib_crate, bin_crate};
-use context::{Ctx, in_target};
+use context::{in_target, BuildCtx};
use package_id::PkgId;
-use search::{find_library_in_search_path, find_installed_library_in_rust_path};
-use path_util::{target_library_in_workspace, U_RWX};
+use package_source::PkgSrc;
+use path_util::{installed_library_in_workspace, U_RWX};
+
pub use target::{OutputType, Main, Lib, Bench, Test};
-use version::NoVersion;
+use workcache_support::{digest_file_with_date, digest_only_date};
// It would be nice to have the list of commands in just one place -- for example,
// you could update the match in rustpkg.rc but forget to update this list. I think
@fold.fold_crate(crate)
}
-// FIXME (#4432): Use workcache to only compile when needed
-pub fn compile_input(ctxt: &Ctx,
+pub fn compile_input(ctxt: &BuildCtx,
+ exec: &mut workcache::Exec,
pkg_id: &PkgId,
in_file: &Path,
workspace: &Path,
flags: &[~str],
cfgs: &[~str],
opt: bool,
- what: OutputType) -> bool {
+ what: OutputType) -> Option<Path> {
assert!(in_file.components.len() > 1);
let input = driver::file_input((*in_file).clone());
debug!("flags: %s", flags.connect(" "));
debug!("cfgs: %s", cfgs.connect(" "));
- debug!("out_dir = %s", out_dir.to_str());
+ debug!("compile_input's sysroot = %s", ctxt.sysroot_opt().to_str());
let crate_type = match what {
Lib => lib_crate,
driver::optgroups()).unwrap();
// Hack so that rustpkg can run either out of a rustc target dir,
// or the host dir
- let sysroot_to_use = if !in_target(ctxt.sysroot_opt) {
- ctxt.sysroot_opt
+ let sysroot_to_use = @if !in_target(&ctxt.sysroot_opt()) {
+ ctxt.sysroot_opt()
}
else {
- ctxt.sysroot_opt.map(|p| { @p.pop().pop().pop() })
+ ctxt.sysroot_opt().pop().pop().pop()
};
- debug!("compile_input's sysroot = %?", ctxt.sysroot_opt_str());
- debug!("sysroot_to_use = %?", sysroot_to_use);
+ debug!("compile_input's sysroot = %s", ctxt.sysroot_opt().to_str());
+ debug!("sysroot_to_use = %s", sysroot_to_use.to_str());
let options = @session::options {
crate_type: crate_type,
optimize: if opt { session::Aggressive } else { session::No },
test: what == Test || what == Bench,
- maybe_sysroot: sysroot_to_use,
+ maybe_sysroot: Some(sysroot_to_use),
addl_lib_search_paths: @mut (~[out_dir.clone()]),
// output_type should be conditional
output_type: output_type_exe, // Use this to get a library? That's weird
// `extern mod` directives.
let cfg = driver::build_configuration(sess);
let mut crate = driver::phase_1_parse_input(sess, cfg.clone(), &input);
- crate = driver::phase_2_configure_and_expand(sess, cfg, crate);
+ crate = driver::phase_2_configure_and_expand(sess, cfg.clone(), crate);
// Not really right. Should search other workspaces too, and the installed
// database (which doesn't exist yet)
- find_and_install_dependencies(ctxt, sess, workspace, crate,
+ find_and_install_dependencies(ctxt, sess, exec, workspace, crate,
|p| {
debug!("a dependency: %s", p.to_str());
// Pass the directory containing a dependency
debug!("calling compile_crate_from_input, workspace = %s,
building_library = %?", out_dir.to_str(), sess.building_library);
- compile_crate_from_input(&input, &out_dir, sess, crate);
- true
+ compile_crate_from_input(in_file, exec, &out_dir, sess, crate)
}
// Should use workcache to avoid recompiling when not necessary
// If crate_opt is present, then finish compilation. If it's None, then
// call compile_upto and return the crate
// also, too many arguments
-pub fn compile_crate_from_input(input: &driver::input,
+pub fn compile_crate_from_input(input: &Path,
+ exec: &mut workcache::Exec,
// should be of the form <workspace>/build/<pkg id's path>
out_dir: &Path,
sess: session::Session,
- crate: @ast::Crate) {
+ crate: @ast::Crate) -> Option<Path> {
debug!("Calling build_output_filenames with %s, building library? %?",
out_dir.to_str(), sess.building_library);
// bad copy
debug!("out_dir = %s", out_dir.to_str());
- let outputs = driver::build_output_filenames(input, &Some(out_dir.clone()), &None,
+ let outputs = driver::build_output_filenames(&driver::file_input(input.clone()),
+ &Some(out_dir.clone()), &None,
crate.attrs, sess);
debug!("Outputs are out_filename: %s and obj_filename: %s and output type = %?",
&analysis,
outputs);
driver::phase_5_run_llvm_passes(sess, &translation, outputs);
- if driver::stop_after_phase_5(sess) { return; }
+ if driver::stop_after_phase_5(sess) { return Some(outputs.out_filename); }
driver::phase_6_link_output(sess, &translation, outputs);
+
+ // Register dependency on the source file
+ exec.discover_input("file", input.to_str(), digest_file_with_date(input));
+
+ Some(outputs.out_filename)
}
#[cfg(windows)]
pub fn exe_suffix() -> ~str { ~"" }
// Called by build_crates
-// FIXME (#4432): Use workcache to only compile when needed
-pub fn compile_crate(ctxt: &Ctx, pkg_id: &PkgId,
+pub fn compile_crate(ctxt: &BuildCtx,
+ exec: &mut workcache::Exec,
+ pkg_id: &PkgId,
crate: &Path, workspace: &Path,
flags: &[~str], cfgs: &[~str], opt: bool,
- what: OutputType) -> bool {
+ what: OutputType) -> Option<Path> {
debug!("compile_crate: crate=%s, workspace=%s", crate.to_str(), workspace.to_str());
debug!("compile_crate: short_name = %s, flags =...", pkg_id.to_str());
for fl in flags.iter() {
debug!("+++ %s", *fl);
}
- compile_input(ctxt, pkg_id, crate, workspace, flags, cfgs, opt, what)
+ compile_input(ctxt, exec, pkg_id, crate, workspace, flags, cfgs, opt, what)
}
/// Collect all `extern mod` directives in `c`, then
/// try to install their targets, failing if any target
/// can't be found.
-pub fn find_and_install_dependencies(ctxt: &Ctx,
+pub fn find_and_install_dependencies(ctxt: &BuildCtx,
sess: session::Session,
+ exec: &mut workcache::Exec,
workspace: &Path,
c: &ast::Crate,
save: @fn(Path)
) {
- // :-(
- debug!("In find_and_install_dependencies...");
- let my_workspace = (*workspace).clone();
- let my_ctxt = *ctxt;
- do c.each_view_item() |vi: &ast::view_item| {
+ debug!("Finding and installing dependencies...");
+ do c.each_view_item |vi| {
debug!("A view item!");
match vi.node {
// ignore metadata, I guess
ast::view_item_extern_mod(lib_ident, path_opt, _, _) => {
- match my_ctxt.sysroot_opt {
- Some(ref x) => debug!("*** sysroot: %s", x.to_str()),
- None => debug!("No sysroot given")
- };
- let lib_name = match path_opt { // ???
+ let lib_name = match path_opt {
Some(p) => p, None => sess.str_of(lib_ident) };
- match find_library_in_search_path(my_ctxt.sysroot_opt, lib_name) {
- Some(installed_path) => {
+ match installed_library_in_workspace(lib_name, &ctxt.sysroot_opt()) {
+ Some(ref installed_path) => {
debug!("It exists: %s", installed_path.to_str());
+ // Say that [path for c] has a discovered dependency on
+ // installed_path
+ // For binary files, we only hash the datestamp, not the contents.
+ // I'm not sure what the right thing is.
+ // Now we know that this crate has a discovered dependency on
+ // installed_path
+ exec.discover_input("binary", installed_path.to_str(),
+ digest_only_date(installed_path));
}
None => {
// FIXME #8711: need to parse version out of path_opt
- match find_installed_library_in_rust_path(lib_name, &NoVersion) {
- Some(installed_path) => {
- debug!("Found library %s, not rebuilding it",
- installed_path.to_str());
- // Once workcache is implemented, we'll actually check
- // whether or not the library at installed_path is fresh
- save(installed_path.pop());
+ debug!("Trying to install library %s, rebuilding it",
+ lib_name.to_str());
+ // Try to install it
+ let pkg_id = PkgId::new(lib_name);
+ let (outputs_disc, inputs_disc) =
+ ctxt.install(PkgSrc::new(workspace.clone(), false, pkg_id));
+ debug!("Installed %s, returned %? dependencies and \
+ %? transitive dependencies",
+ lib_name, outputs_disc.len(), inputs_disc.len());
+ for dep in outputs_disc.iter() {
+ debug!("Discovering a binary input: %s", dep.to_str());
+ exec.discover_input("binary", dep.to_str(),
+ digest_only_date(dep));
+ }
+ for &(ref what, ref dep) in inputs_disc.iter() {
+ if *what == ~"file" {
+ exec.discover_input(*what, *dep,
+ digest_file_with_date(&Path(*dep)));
}
- None => {
- debug!("Trying to install library %s, rebuilding it",
- lib_name.to_str());
- // Try to install it
- let pkg_id = PkgId::new(lib_name);
- my_ctxt.install(&my_workspace, &pkg_id);
- // Also, add an additional search path
- debug!("let installed_path...")
- let installed_path = target_library_in_workspace(&pkg_id,
- &my_workspace).pop();
- debug!("Great, I installed %s, and it's in %s",
- lib_name, installed_path.to_str());
- save(installed_path);
- }
+ else if *what == ~"binary" {
+ exec.discover_input(*what, *dep,
+ digest_only_date(&Path(*dep)));
+ }
+ else {
+ fail!("Bad kind: %s", *what);
+ }
+ }
+ // Also, add an additional search path
+ let installed_library =
+ installed_library_in_workspace(lib_name, workspace)
+ .expect( fmt!("rustpkg failed to install dependency %s",
+ lib_name));
+ let install_dir = installed_library.pop();
+ debug!("Installed %s into %s", lib_name, install_dir.to_str());
+ save(install_dir);
}
- }
- }
- }
+ }}
// Ignore `use`s
_ => ()
}
let rustpath = rust_path();
for rp in rustpath.iter() {
let local_path = rp.push_rel(local_path);
- debug!("in try_getting_local_version");
+ let git_dir = local_path.push(".git");
+ if !os::path_is_dir(&git_dir) {
+ loop;
+ }
let outp = run::process_output("git",
- [fmt!("--git-dir=%s", local_path.push(".git").to_str()),
- ~"tag", ~"-l"]);
+ [fmt!("--git-dir=%s", git_dir.to_str()), ~"tag", ~"-l"]);
- debug!("git --git-dir=%s tag -l ~~~> %?", local_path.push(".git").to_str(), outp.status);
+ debug!("git --git-dir=%s tag -l ~~~> %?", git_dir.to_str(), outp.status);
if outp.status != 0 {
loop;
/// and the most recent tag in that repo denotes a version, return it;
/// otherwise, `None`
pub fn try_getting_version(remote_path: &Path) -> Option<Version> {
- debug!("try_getting_version: %s", remote_path.to_str());
if is_url_like(remote_path) {
- debug!("Trying to fetch its sources..");
let tmp_dir = mkdtemp(&os::tmpdir(),
"test").expect("try_getting_version: couldn't create temp dir");
debug!("(to get version) executing {git clone https://%s %s}",
pub fn split_version_general<'a>(s: &'a str, sep: char) -> Option<(&'a str, Version)> {
match s.rfind(sep) {
Some(i) => {
- debug!("in %s, i = %?", s, i);
let path = s.slice(0, i);
- debug!("path = %s", path);
// n.b. for now, assuming an exact revision is intended, not a SemVer
Some((path, ExactRevision(s.slice(i + 1, s.len()).to_owned())))
}
None => {
- debug!("%s doesn't look like an explicit-version thing", s);
None
}
}
--- /dev/null
+// Copyright 2013 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.
+
+use extra::sha1::Sha1;
+use extra::digest::Digest;
+use extra::workcache;
+use std::io;
+
+/// Hashes the file contents along with the last-modified time
+pub fn digest_file_with_date(path: &Path) -> ~str {
+ use conditions::bad_path::cond;
+ use cond1 = conditions::bad_stat::cond;
+
+ let mut sha = ~Sha1::new();
+ let s = io::read_whole_file_str(path);
+ match s {
+ Ok(s) => {
+ (*sha).input_str(s);
+ let st = match path.stat() {
+ Some(st) => st,
+ None => cond1.raise((path.clone(), fmt!("Couldn't get file access time")))
+ };
+ (*sha).input_str(st.st_mtime.to_str());
+ (*sha).result_str()
+ }
+ Err(e) => cond.raise((path.clone(), fmt!("Couldn't read file: %s", e))).to_str()
+ }
+}
+
+/// Hashes only the last-modified time
+pub fn digest_only_date(path: &Path) -> ~str {
+ use cond = conditions::bad_stat::cond;
+
+ let mut sha = ~Sha1::new();
+ let st = match path.stat() {
+ Some(st) => st,
+ None => cond.raise((path.clone(), fmt!("Couldn't get file access time")))
+ };
+ (*sha).input_str(st.st_mtime.to_str());
+ (*sha).result_str()
+}
+
+/// Adds multiple discovered outputs
+pub fn discover_outputs(e: &mut workcache::Exec, outputs: ~[Path]) {
+ debug!("Discovering %? outputs", outputs.len());
+ for p in outputs.iter() {
+ debug!("Discovering output! %s", p.to_str());
+ // For now, assume that all discovered outputs are binaries
+ e.discover_output("binary", p.to_str(), digest_only_date(p));
+ }
+}
.filter(|ws| workspace_contains_package_id(pkgid, ws))
.collect();
if cx.use_rust_path_hack {
- rs + option_to_vec(find_dir_using_rust_path_hack(cx, pkgid))
+ rs + option_to_vec(find_dir_using_rust_path_hack(pkgid))
}
else {
rs
use codemap::{Span, dummy_sp};
use opt_vec;
use parse::token;
-use visit::{SimpleVisitor, SimpleVisitorVisitor, Visitor};
+use visit::{SimpleVisitor, Visitor};
use visit;
use std::hashmap::HashMap;
}
pub trait EachViewItem {
- fn each_view_item(&self, f: @fn(&ast::view_item) -> bool) -> bool;
+ fn each_view_item(&self, f: &fn(&ast::view_item) -> bool) -> bool;
}
-struct EachViewItemData {
- callback: @fn(&ast::view_item) -> bool,
+struct EachViewItemData<'self> {
+ callback: &'self fn(&ast::view_item) -> bool,
}
-impl SimpleVisitor for EachViewItemData {
- fn visit_mod(&mut self, _: &_mod, _: Span, _: NodeId) {
- // XXX: Default method.
- }
- fn visit_view_item(&mut self, view_item: &view_item) {
+impl<'self> Visitor<()> for EachViewItemData<'self> {
+ fn visit_view_item(&mut self, view_item: &ast::view_item, _: ()) {
let _ = (self.callback)(view_item);
}
- fn visit_foreign_item(&mut self, _: @foreign_item) {
- // XXX: Default method.
- }
- fn visit_item(&mut self, _: @item) {
- // XXX: Default method.
- }
- fn visit_local(&mut self, _: @Local) {
- // XXX: Default method.
- }
- fn visit_block(&mut self, _: &Block) {
- // XXX: Default method.
- }
- fn visit_stmt(&mut self, _: @Stmt) {
- // XXX: Default method.
- }
- fn visit_arm(&mut self, _: &Arm) {
- // XXX: Default method.
- }
- fn visit_pat(&mut self, _: @Pat) {
- // XXX: Default method.
- }
- fn visit_decl(&mut self, _: @Decl) {
- // XXX: Default method.
- }
- fn visit_expr(&mut self, _: @Expr) {
- // XXX: Default method.
- }
- fn visit_expr_post(&mut self, _: @Expr) {
- // XXX: Default method.
- }
- fn visit_ty(&mut self, _: &Ty) {
- // XXX: Default method.
- }
- fn visit_generics(&mut self, _: &Generics) {
- // XXX: Default method.
- }
- fn visit_fn(&mut self,
- _: &visit::fn_kind,
- _: &fn_decl,
- _: &Block,
- _: Span,
- _: NodeId) {
- // XXX: Default method.
- }
- fn visit_ty_method(&mut self, _: &TypeMethod) {
- // XXX: Default method.
- }
- fn visit_trait_method(&mut self, _: &trait_method) {
- // XXX: Default method.
- }
- fn visit_struct_def(&mut self,
- _: @struct_def,
- _: Ident,
- _: &Generics,
- _: NodeId) {
- // XXX: Default method.
- }
- fn visit_struct_field(&mut self, _: @struct_field) {
- // XXX: Default method.
- }
- fn visit_struct_method(&mut self, _: @method) {
- // XXX: Default method.
- }
}
impl EachViewItem for ast::Crate {
- fn each_view_item(&self, f: @fn(&ast::view_item) -> bool) -> bool {
- let data = @mut EachViewItemData {
+ fn each_view_item(&self, f: &fn(&ast::view_item) -> bool) -> bool {
+ let mut visit = EachViewItemData {
callback: f,
};
- let visitor = @mut SimpleVisitorVisitor {
- simple_visitor: data as @mut SimpleVisitor,
- };
- visit::walk_crate(visitor, self, ());
+ visit::walk_crate(&mut visit, self, ());
true
}
}