]> git.lizzy.rs Git - rust.git/commitdiff
rustpkg: Use workcache
authorTim Chevalier <chevalier@alum.wellesley.edu>
Sat, 7 Sep 2013 03:29:16 +0000 (20:29 -0700)
committerTim Chevalier <chevalier@alum.wellesley.edu>
Mon, 9 Sep 2013 03:05:12 +0000 (20:05 -0700)
rustpkg now uses the workcache library to avoid recompilation.
Hooray!

15 files changed:
src/libextra/workcache.rs
src/librustpkg/api.rs
src/librustpkg/conditions.rs
src/librustpkg/context.rs
src/librustpkg/package_id.rs
src/librustpkg/package_source.rs
src/librustpkg/path_util.rs
src/librustpkg/rustpkg.rs
src/librustpkg/search.rs
src/librustpkg/tests.rs
src/librustpkg/util.rs
src/librustpkg/version.rs
src/librustpkg/workcache_support.rs [new file with mode: 0644]
src/librustpkg/workspace.rs
src/libsyntax/ast_util.rs

index bf897e938810ef9711cf6bd6fba10c75549a0309..d5d37a30f6bd8e8161e7808c1e890b544de92b37 100644 (file)
@@ -493,7 +493,7 @@ pub fn unwrap(self) -> T {
 #[test]
 fn test() {
     use std::io::WriterUtil;
-    use std::run;
+    use std::{os, run};
 
     let pth = Path("foo.c");
     {
@@ -501,7 +501,12 @@ fn test() {
         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()));
 
index dfe80674b7fa114bcbb457851c652a0c4d1f0b6f..42e5668489aa65a4ca37a98c1475b80a969ad045 100644 (file)
 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,
@@ -65,23 +117,33 @@ pub fn install_lib(sysroot: @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 {
index 4cb103deba79f778b34c872981699385d538296f..2e049d252bcd792ceeafa223822bf54cea89ae4f 100644 (file)
 
 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;
 }
index 4087fdd7ca5048820a19a567e89416985a349002..70f1e39efec23ef6fc665211ec6093c2f74b9ed0 100644 (file)
 
 // 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"))
 }
index ce1da58a2cb87441b654baf3ee3e32edb82712d2..f0f3673f1d030abdc2fe482fdd1befcbc1dc3c00 100644 (file)
@@ -51,12 +51,10 @@ pub fn new(s: &str) -> PkgId {
         // 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
             }
         };
@@ -81,7 +79,6 @@ pub fn new(s: &str) -> PkgId {
             }
         };
 
-        debug!("path = %s", path.to_str());
         PkgId {
             path: path.clone(),
             short_name: short_name.to_owned(),
index c20091158c4f8a0ab0aa55a086d7227ec2ea1cb8..45932283081b739c50a54b623ff43d55e3ade099 100644 (file)
 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],
@@ -33,57 +38,91 @@ pub struct PkgSrc {
     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
@@ -91,7 +130,7 @@ fn check_dir(&self, cx: &Ctx) -> Path {
     /// 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,
@@ -103,38 +142,35 @@ pub fn fetch_git(&self) -> Option<Path> {
             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 {
@@ -143,10 +179,11 @@ pub fn fetch_git(&self) -> Option<Path> {
     }
 
 
-    // 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)
         }
@@ -166,20 +203,18 @@ fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &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),
@@ -207,7 +242,8 @@ pub fn find_crates(&mut self, cx: &Ctx) {
             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(),
@@ -215,68 +251,93 @@ pub fn find_crates(&mut self, cx: &Ctx) {
     }
 
     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()
     }
 }
index cbe6c8f65c4326cf71d88bbed39e26184a6760ab..566e75df548c0d5ec230376a5f8c4c920918a66f 100644 (file)
@@ -14,7 +14,6 @@
 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;
@@ -62,12 +61,7 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path,
 
     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| {
@@ -75,8 +69,6 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path,
                     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)
                         }
@@ -90,32 +82,15 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path,
         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> {
@@ -402,10 +377,7 @@ fn dir_has_file(dir: &Path, file: &str) -> bool {
     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());
index 25a415df3027cda0e147f61ab395be96ccb24a74..27bde2bca6d33c2775acb1a6e765d66355d0d4e2 100644 (file)
@@ -24,8 +24,9 @@
 
 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;
@@ -59,6 +61,7 @@
 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,
@@ -104,7 +104,7 @@ fn parse<'a>(sysroot: @Path,
             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);
@@ -115,7 +115,7 @@ fn parse<'a>(sysroot: @Path,
 
         PkgScript {
             id: id,
-            input: input,
+            input: script,
             sess: sess,
             cfg: cfg,
             crate: crate,
@@ -127,22 +127,24 @@ fn parse<'a>(sysroot: @Path,
     /// 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 {
@@ -162,47 +164,65 @@ fn run_custom(&self, sysroot: @Path) -> (~[~str], ExitCode) {
     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
                     };
                 }
@@ -236,35 +256,40 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                 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);
                         };
                     }
                 }
@@ -299,7 +324,7 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                 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()));
@@ -325,44 +350,44 @@ fn do_cmd(&self, _cmd: &str, _pkgname: &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")
@@ -381,9 +406,10 @@ fn build(&self, workspace: &Path, pkgid: &PkgId) -> Path {
         // 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
@@ -412,20 +438,47 @@ fn info(&self) {
         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
@@ -439,12 +492,15 @@ fn install_no_build(&self, workspace: &Path, id: &PkgId) {
                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 \
@@ -455,7 +511,9 @@ fn install_no_build(&self, workspace: &Path, id: &PkgId) {
                  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>)  {
@@ -476,7 +534,6 @@ fn unprefer(&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();
@@ -485,7 +542,6 @@ pub fn main() {
 
 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")];
@@ -499,8 +555,6 @@ pub fn main_args(args: &[~str]) {
     };
     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") {
@@ -512,7 +566,6 @@ pub fn main_args(args: &[~str]) {
                              getopts::opt_present(matches, "rust-path-hack");
 
     let mut args = matches.free.clone();
-
     args.shift();
 
     if (args.len() < 1) {
@@ -553,13 +606,15 @@ pub fn main_args(args: &[~str]) {
     // 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)
 }
 
@@ -581,3 +636,11 @@ pub fn work_dir() -> Path {
 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 => ()
+    }
+}
index 9862f870bcafb0a6d1afbe603c43837458d656c9..e5e2a0dbd714f2a5e08727cb95944c648b484271 100644 (file)
 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.
index a86f299276ab2c1c3f653eff9d9a2b88ef2517c6..67d8732cf32d2f687c0baf194ebcfa56837b8329 100644 (file)
 
 // 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
+        }
     }
 }
 
@@ -388,7 +396,7 @@ fn lib_output_file_name(workspace: &Path, parent: &str, short_name: &str) -> Pat
 }
 
 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) {
@@ -401,12 +409,11 @@ 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());
@@ -423,7 +430,7 @@ fn frob_source_file(workspace: &Path, pkgid: &PkgId) {
             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",
@@ -450,12 +457,13 @@ fn test_install_valid() {
 
     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());
@@ -476,32 +484,19 @@ fn test_install_valid() {
 
 #[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
@@ -898,7 +893,6 @@ fn install_check_duplicates() {
 }
 
 #[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);
@@ -912,24 +906,28 @@ fn no_rebuilding() {
 }
 
 #[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");
@@ -946,7 +944,6 @@ fn do_rebuild_dep_dates_change() {
 }
 
 #[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");
index 1b06f78abb911e84099e2c98451479ce6499d028..769b1bdf25dfee1858621a2faee9c6148a89eeef 100644 (file)
@@ -9,6 +9,7 @@
 // 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
@@ -151,15 +153,15 @@ pub fn ready_crate(sess: session::Session,
     @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());
@@ -173,7 +175,7 @@ pub fn compile_input(ctxt: &Ctx,
 
     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,
@@ -191,19 +193,19 @@ pub fn compile_input(ctxt: &Ctx,
                           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
@@ -228,11 +230,11 @@ pub fn compile_input(ctxt: &Ctx,
     // `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
@@ -269,8 +271,7 @@ pub fn compile_input(ctxt: &Ctx,
 
     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
@@ -278,17 +279,19 @@ pub fn compile_input(ctxt: &Ctx,
 // 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 = %?",
@@ -304,8 +307,13 @@ pub fn compile_crate_from_input(input: &driver::input,
                                                         &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)]
@@ -318,76 +326,90 @@ pub fn exe_suffix() -> ~str { ~".exe" }
 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
             _ => ()
         }
index d408177572f8ba4f9de793a63b801e023228117e..c2d87ddeb82bdd4a331e545b8ab9794b58b174c0 100644 (file)
@@ -99,12 +99,14 @@ pub fn try_getting_local_version(local_path: &Path) -> Option<Version> {
     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;
@@ -129,9 +131,7 @@ pub fn try_getting_local_version(local_path: &Path) -> Option<Version> {
 /// 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}",
@@ -218,14 +218,11 @@ pub fn split_version<'a>(s: &'a str) -> Option<(&'a str, Version)> {
 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
         }
     }
diff --git a/src/librustpkg/workcache_support.rs b/src/librustpkg/workcache_support.rs
new file mode 100644 (file)
index 0000000..e241678
--- /dev/null
@@ -0,0 +1,58 @@
+// 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));
+    }
+}
index 5ad2dfd6d2f4987157b9fa8202d0dbcdac36878a..cb947b0036c665d49b765cedde8fa48055f7af98 100644 (file)
@@ -43,7 +43,7 @@ pub fn pkg_parent_workspaces(cx: &Ctx, pkgid: &PkgId) -> ~[Path] {
         .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
index ee898c182e08a6f779d6a873a32997f03da81619..dcf655871dfb27148e6cf182e785eb91c86759c8 100644 (file)
@@ -14,7 +14,7 @@
 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;
@@ -684,94 +684,25 @@ pub fn walk_pat(pat: @Pat, it: &fn(@Pat) -> bool) -> bool {
 }
 
 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
     }
 }