]> git.lizzy.rs Git - rust.git/commitdiff
rustpkg: Make crates, not packages, the unit of rustpkg dependencies
authorTim Chevalier <chevalier@alum.wellesley.edu>
Tue, 17 Sep 2013 02:27:09 +0000 (19:27 -0700)
committerTim Chevalier <chevalier@alum.wellesley.edu>
Wed, 18 Sep 2013 22:30:41 +0000 (15:30 -0700)
Treating a package as the thing that can have other packages depend on it,
and depends on other packages, was wrong if a package has more than one
crate. Now, rustpkg knows about dependencies between crates in the same
package. This solves the problem reported in #7879 where rustpkg wrongly
discovered a circular dependency between thhe package and itself, and
recursed infinitely.

Closes #7879

src/librustpkg/api.rs
src/librustpkg/exit_codes.rs [new file with mode: 0644]
src/librustpkg/package_id.rs
src/librustpkg/package_source.rs
src/librustpkg/rustpkg.rs
src/librustpkg/target.rs
src/librustpkg/tests.rs
src/librustpkg/util.rs
src/librustpkg/workcache_support.rs

index 727bbcb30b4c51f6bf5a07430df1d5db126b311a..e1092458ffaae85e4a6f3d05a1282b89cfe72380 100644 (file)
@@ -12,6 +12,7 @@
 use crate::*;
 use package_id::*;
 use package_source::*;
+use target::*;
 use version::Version;
 use workcache_support::*;
 
@@ -63,56 +64,40 @@ pub fn new_workcache_context(p: &Path) -> workcache::Context {
 pub fn build_lib(sysroot: Path, root: Path, name: ~str, version: Version,
                  lib: Path) {
     let cx = default_context(sysroot);
-    let subroot = root.clone();
-    let subversion = version.clone();
-    let sublib = lib.clone();
-    do cx.workcache_context.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())],
+    let pkg_src = PkgSrc {
+            workspace: root.clone(),
+            start_dir: root.push("src").push(name),
+            id: PkgId{ version: version, ..PkgId::new(name)},
+            // n.b. This assumes the package only has one crate
+            libs: ~[mk_crate(lib)],
             mains: ~[],
             tests: ~[],
             benchs: ~[]
         };
-        pkg_src.declare_inputs(prep);
-        let subcx = cx.clone();
-        let subsrc = pkg_src.clone();
-        do prep.exec |exec| {
-            subsrc.build(exec, &subcx.clone(), ~[]);
-        }
-    };
+    pkg_src.build(&cx, ~[]);
 }
 
 pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version,
                  main: Path) {
     let cx = default_context(sysroot);
-    let subroot = root.clone();
-    let submain = main.clone();
-    do cx.workcache_context.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(), ~[]);
-        }
-    }
+    let pkg_src = PkgSrc {
+        workspace: root.clone(),
+        start_dir: root.push("src").push(name),
+        id: PkgId{ version: version, ..PkgId::new(name)},
+        libs: ~[],
+        // n.b. This assumes the package only has one crate
+        mains: ~[mk_crate(main)],
+        tests: ~[],
+        benchs: ~[]
+    };
+
+    pkg_src.build(&cx, ~[]);
 }
 
 pub fn install_pkg(sysroot: Path, workspace: Path, name: ~str, version: Version) {
     let cx = default_context(sysroot);
     let pkgid = PkgId{ version: version, ..PkgId::new(name)};
-    cx.install(PkgSrc::new(workspace, false, pkgid));
+    cx.install(PkgSrc::new(workspace, false, pkgid), &Everything);
 }
 
 fn mk_crate(p: Path) -> Crate {
diff --git a/src/librustpkg/exit_codes.rs b/src/librustpkg/exit_codes.rs
new file mode 100644 (file)
index 0000000..484f6bd
--- /dev/null
@@ -0,0 +1,11 @@
+// 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.
+
+pub static copy_failed_code: int = 65;
index bc2fcdd7fe9b2b19b30d71b5a69fb34afb3bab40..52b986cb6e7e6cad12a205dd360501958f201402 100644 (file)
@@ -108,6 +108,12 @@ pub fn prefixes_iter(&self) -> Prefixes {
         }
     }
 
+    // This is the workcache function name for the *installed*
+    // binaries for this package (as opposed to the built ones,
+    // which are per-crate).
+    pub fn install_tag(&self) -> ~str {
+        fmt!("install(%s)", self.to_str())
+    }
 }
 
 struct Prefixes {
index b5ded6f3fafab0d209c3fc6ec9bad6c914e53478..4bf647b011d9dbc3b7cf24ae872b6e0ec21439a8 100644 (file)
@@ -22,6 +22,7 @@
 use util::compile_crate;
 use workspace::is_workspace;
 use workcache_support;
+use workcache_support::crate_tag;
 use extra::workcache;
 
 // An enumeration of the unpacked source of a package workspace.
@@ -231,7 +232,7 @@ fn stem_matches(&self, p: &Path) -> bool {
         p.filestem().map_default(false, |p| { p == &self.id.short_name.as_slice() })
     }
 
-    fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) {
+    pub fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) {
         assert!(p.components.len() > prefix);
         let mut sub = Path("");
         for c in p.components.slice(prefix, p.components.len()).iter() {
@@ -286,7 +287,6 @@ pub fn find_crates(&mut self) {
 
     fn build_crates(&self,
                     ctx: &BuildContext,
-                    exec: &mut workcache::Exec,
                     destination_dir: &Path,
                     crates: &[Crate],
                     cfgs: &[~str],
@@ -297,25 +297,40 @@ fn build_crates(&self,
             let path_str = path.to_str();
             let cfgs = crate.cfgs + cfgs;
 
-            let result =
-                // compile_crate should return the path of the output artifact
-                compile_crate(ctx,
-                              exec,
-                              &self.id,
-                              &path,
-                              destination_dir,
-                              crate.flags,
-                              cfgs,
-                              false,
-                              what).to_str();
-            debug!("Result of compiling %s was %s", path_str, result);
+            do ctx.workcache_context.with_prep(crate_tag(&path)) |prep| {
+                debug!("Building crate %s, declaring it as an input", path.to_str());
+                prep.declare_input("file", path.to_str(),
+                                   workcache_support::digest_file_with_date(&path));
+                let subpath = path.clone();
+                let subcfgs = cfgs.clone();
+                let subpath_str = path_str.clone();
+                let subcx = ctx.clone();
+                let id = self.id.clone();
+                let sub_dir = destination_dir.clone();
+                let sub_flags = crate.flags.clone();
+                do prep.exec |exec| {
+                    let result = compile_crate(&subcx,
+                                               exec,
+                                               &id,
+                                               &subpath,
+                                               &sub_dir,
+                                               sub_flags,
+                                               subcfgs,
+                                               false,
+                                               what).to_str();
+                    debug!("Result of compiling %s was %s", subpath_str, result);
+                    result
+                }
+            };
         }
     }
 
     /// Declare all the crate files in the package source as inputs
+    /// (to the package)
     pub fn declare_inputs(&self, prep: &mut workcache::Prep) {
         let to_do = ~[self.libs.clone(), self.mains.clone(),
                       self.tests.clone(), self.benchs.clone()];
+        debug!("In declare inputs, self = %s", self.to_str());
         for cs in to_do.iter() {
             for c in cs.iter() {
                 let path = self.start_dir.push_rel(&c.file).normalize();
@@ -330,7 +345,6 @@ pub fn declare_inputs(&self, prep: &mut workcache::Prep) {
     // 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,
                  build_context: &BuildContext,
                  cfgs: ~[~str]) -> ~str {
         use conditions::not_a_workspace::cond;
@@ -360,13 +374,23 @@ pub fn build(&self,
         let benchs = self.benchs.clone();
         debug!("Building libs in %s, destination = %s",
                destination_workspace.to_str(), destination_workspace.to_str());
-        self.build_crates(build_context, exec, &destination_workspace, libs, cfgs, Lib);
+        self.build_crates(build_context, &destination_workspace, libs, cfgs, Lib);
         debug!("Building mains");
-        self.build_crates(build_context, exec, &destination_workspace, mains, cfgs, Main);
+        self.build_crates(build_context, &destination_workspace, mains, cfgs, Main);
         debug!("Building tests");
-        self.build_crates(build_context, exec, &destination_workspace, tests, cfgs, Test);
+        self.build_crates(build_context, &destination_workspace, tests, cfgs, Test);
         debug!("Building benches");
-        self.build_crates(build_context, exec, &destination_workspace, benchs, cfgs, Bench);
+        self.build_crates(build_context, &destination_workspace, benchs, cfgs, Bench);
         destination_workspace.to_str()
     }
+
+    /// Debugging
+    pub fn dump_crates(&self) {
+        let crate_sets = [&self.libs, &self.mains, &self.tests, &self.benchs];
+        for crate_set in crate_sets.iter() {
+            for c in crate_set.iter() {
+                debug!("Built crate: %s", c.file.to_str())
+            }
+        }
+    }
 }
index 2a0cf5fea34fa6aa83887ad55aeb610dd2b08d64..7cd30c7af9e142b0056a37b08600bf61dfcffa9a 100644 (file)
 extern mod rustc;
 extern mod syntax;
 
-use std::{io, os, result, run, str};
+use std::{io, os, result, run, str, task};
 pub use std::path::Path;
 
 use extra::workcache;
-use extra::arc::RWArc;
 use rustc::driver::{driver, session};
 use rustc::metadata::filesearch;
 use rustc::metadata::filesearch::rust_path;
                        LLVMAssemble, LLVMCompileBitcode};
 use package_id::PkgId;
 use package_source::PkgSrc;
-use workcache_support::{discover_outputs, digest_only_date};
+use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench};
+// use workcache_support::{discover_outputs, digest_only_date};
+use workcache_support::digest_only_date;
+use exit_codes::copy_failed_code;
 
 pub mod api;
 mod conditions;
 mod context;
 mod crate;
+mod exit_codes;
 mod installed_packages;
 mod messages;
 mod package_id;
@@ -172,19 +175,18 @@ fn hash(&self) -> ~str {
 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, exec: &mut workcache::Exec, pkg_src: PkgSrc) -> Path;
+    fn build(&self, pkg_src: &mut PkgSrc, what: &WhatToBuild) -> Path;
     fn clean(&self, workspace: &Path, id: &PkgId);
     fn info(&self);
     /// Returns a pair. First component is a list of installed paths,
     /// second is a list of declared and discovered inputs
-    fn install(&self, src: PkgSrc) -> (~[Path], ~[(~str, ~str)]);
+    fn install(&self, src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]);
     /// Returns a list of installed files
     fn install_no_build(&self,
                         source_workspace: &Path,
                         target_workspace: &Path,
-                        id: &PkgId) -> ~[Path];
+                        id: &PkgId) -> ~[~str];
     fn prefer(&self, _id: &str, _vers: Option<~str>);
     fn test(&self);
     fn uninstall(&self, _id: &str, _vers: Option<~str>);
@@ -193,20 +195,6 @@ fn install_no_build(&self,
 }
 
 impl CtxMethods for BuildContext {
-    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_context.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" => {
@@ -215,11 +203,13 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                         None if self.context.use_rust_path_hack => {
                             let cwd = os::getcwd();
                             let pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]);
-                            self.build_from_src(PkgSrc::new(cwd, true, pkgid));
+                            let mut pkg_src = PkgSrc::new(cwd, true, pkgid);
+                            self.build(&mut pkg_src, &Everything);
                         }
                         None => { usage::build(); return; }
                         Some((ws, pkgid)) => {
-                            self.build_from_src(PkgSrc::new(ws, false, pkgid));
+                            let mut pkg_src = PkgSrc::new(ws, false, pkgid);
+                            self.build(&mut pkg_src, &Everything);
                         }
                     }
                 }
@@ -230,8 +220,8 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                     do each_pkg_parent_workspace(&self.context, &pkgid) |workspace| {
                         debug!("found pkg %s in workspace %s, trying to build",
                                pkgid.to_str(), workspace.to_str());
-                        let pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone());
-                        self.build_from_src(pkg_src);
+                        let mut pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone());
+                        self.build(&mut pkg_src, &Everything);
                         true
                     };
                 }
@@ -271,12 +261,12 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                             let cwd = os::getcwd();
                             let inferred_pkgid =
                                 PkgId::new(cwd.components[cwd.components.len() - 1]);
-                            self.install(PkgSrc::new(cwd, true, inferred_pkgid));
+                            self.install(PkgSrc::new(cwd, true, inferred_pkgid), &Everything);
                         }
                         None  => { usage::install(); return; }
                         Some((ws, pkgid))                => {
                             let pkg_src = PkgSrc::new(ws, false, pkgid);
-                            self.install(pkg_src);
+                            self.install(pkg_src, &Everything);
                       }
                   }
                 }
@@ -291,14 +281,14 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                         let rp = rust_path();
                         assert!(!rp.is_empty());
                         let src = PkgSrc::new(rp[0].clone(), false, pkgid.clone());
-                        self.install(src);
+                        self.install(src, &Everything);
                     }
                     else {
                         for workspace in workspaces.iter() {
                             let src = PkgSrc::new(workspace.clone(),
                                                   self.context.use_rust_path_hack,
                                                   pkgid.clone());
-                            self.install(src);
+                            self.install(src, &Everything);
                         };
                     }
                 }
@@ -366,7 +356,9 @@ 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, exec: &mut workcache::Exec, mut pkg_src: PkgSrc) -> Path {
+    /// what_to_build says: "Just build the lib.rs file in one subdirectory,
+    /// don't walk anything recursively." Or else, everything.
+    fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) -> Path {
         let workspace = pkg_src.workspace.clone();
         let pkgid = pkg_src.id.clone();
 
@@ -384,7 +376,7 @@ fn build(&self, exec: &mut workcache::Exec, mut pkg_src: PkgSrc) -> Path {
             let default_ws = default_workspace();
             debug!("Calling build recursively with %? and %?", default_ws.to_str(),
                    pkgid.to_str());
-            return self.build(exec, PkgSrc::new(default_ws, false, pkgid.clone()));
+            return self.build(&mut PkgSrc::new(default_ws, false, pkgid.clone()), what_to_build);
         }
 
         // Is there custom build logic? If so, use it
@@ -395,12 +387,21 @@ fn build(&self, exec: &mut workcache::Exec, mut pkg_src: PkgSrc) -> Path {
         let cfgs = match pkg_src.package_script_option() {
             Some(package_script_path) => {
                 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)
+                let (cfgs, hook_result) =
+                    do self.workcache_context.with_prep(package_script_path.to_str()) |prep| {
+                    let sub_sysroot = sysroot.clone();
+                    let package_script_path_clone = package_script_path.clone();
+                    let sub_ws = workspace.clone();
+                    let sub_id = pkgid.clone();
+                    declare_package_script_dependency(prep, &*pkg_src);
+                    do prep.exec |exec| {
+                        let pscript = PkgScript::parse(@sub_sysroot.clone(),
+                                                       package_script_path_clone.clone(),
+                                                       &sub_ws,
+                                                       &sub_id);
+
+                        pscript.run_custom(exec, &sub_sysroot)
+                    }
                 };
                 debug!("Command return code = %?", hook_result);
                 if hook_result != 0 {
@@ -419,10 +420,31 @@ fn build(&self, exec: &mut workcache::Exec, mut pkg_src: PkgSrc) -> Path {
         // If there was a package script, it should have finished
         // the build already. Otherwise...
         if !custom {
-            // Find crates inside the workspace
-            pkg_src.find_crates();
+            match what_to_build {
+                // Find crates inside the workspace
+                &Everything => pkg_src.find_crates(),
+                // Don't infer any crates -- just build the one that was requested
+                &JustOne(ref p) => {
+                    // We expect that p is relative to the package source's start directory,
+                    // so check that assumption
+                    debug!("JustOne: p = %s", p.to_str());
+                    assert!(os::path_exists(&pkg_src.start_dir.push_rel(p)));
+                    if is_lib(p) {
+                        PkgSrc::push_crate(&mut pkg_src.libs, 0, p);
+                    } else if is_main(p) {
+                        PkgSrc::push_crate(&mut pkg_src.mains, 0, p);
+                    } else if is_test(p) {
+                        PkgSrc::push_crate(&mut pkg_src.tests, 0, p);
+                    } else if is_bench(p) {
+                        PkgSrc::push_crate(&mut pkg_src.benchs, 0, p);
+                    } else {
+                        warn(fmt!("Not building any crates for dependency %s", p.to_str()));
+                        return workspace.clone();
+                    }
+                }
+            }
             // Build it!
-            let rs_path = pkg_src.build(exec, self, cfgs);
+            let rs_path = pkg_src.build(self, cfgs);
             Path(rs_path)
         }
         else {
@@ -452,56 +474,54 @@ fn info(&self) {
         fail!("info not yet implemented");
     }
 
-    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
-        self.workcache_context.with_prep(id.to_str(), |p| pkg_src.declare_inputs(p));
-        do self.workcache_context.with_prep(id.to_str()) |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.
-                let actual_workspace = if path_util::user_set_rust_path() {
-                    default_workspace()
-                }
-                else {
-                    Path(destination_workspace)
-                };
-                debug!("install: destination workspace = %s, id = %s, installing to %s",
-                       destination_workspace, id_str, actual_workspace.to_str());
-                let result = subself.install_no_build(&Path(destination_workspace),
-                                                      &actual_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() });
-                note(fmt!("Installed package %s to %s", id_str, actual_workspace.to_str()));
+    fn install(&self, mut pkg_src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]) {
+
+        let id = pkg_src.id.clone();
+
+        let mut installed_files = ~[];
+        let inputs = ~[];
+
+        // workcache only knows about *crates*. Building a package
+        // just means inferring all the crates in it, then building each one.
+        let destination_workspace = self.build(&mut pkg_src, what).to_str();
+
+        let to_do = ~[pkg_src.libs.clone(), pkg_src.mains.clone(),
+                      pkg_src.tests.clone(), pkg_src.benchs.clone()];
+        debug!("In declare inputs for %s", id.to_str());
+        for cs in to_do.iter() {
+            for c in cs.iter() {
+                let path = pkg_src.start_dir.push_rel(&c.file).normalize();
+                debug!("Recording input: %s", path.to_str());
+                installed_files.push(path);
             }
+        }
+        // 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.
+        let actual_workspace = if path_util::user_set_rust_path() {
+            default_workspace()
+        }
+            else {
+            Path(destination_workspace)
         };
-        (installed_files.unwrap(), inputs.unwrap())
+        debug!("install: destination workspace = %s, id = %s, installing to %s",
+               destination_workspace, id.to_str(), actual_workspace.to_str());
+        let result = self.install_no_build(&Path(destination_workspace),
+                                           &actual_workspace,
+                                           &id).map(|s| Path(*s));
+        debug!("install: id = %s, about to call discover_outputs, %?",
+               id.to_str(), result.to_str());
+        installed_files = installed_files + result;
+        note(fmt!("Installed package %s to %s", id.to_str(), actual_workspace.to_str()));
+        (installed_files, inputs)
     }
 
+    // again, working around lack of Encodable for Path
     fn install_no_build(&self,
                         source_workspace: &Path,
                         target_workspace: &Path,
-                        id: &PkgId) -> ~[Path] {
+                        id: &PkgId) -> ~[~str] {
         use conditions::copy_failed::cond;
 
         // Now copy stuff into the install dirs
@@ -511,32 +531,59 @@ fn install_no_build(&self,
         let target_lib = maybe_library.map(|_p| target_library_in_workspace(id, target_workspace));
 
         debug!("target_exec = %s target_lib = %? \
-                maybe_executable = %? maybe_library = %?",
+               maybe_executable = %? maybe_library = %?",
                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()));
+        do self.workcache_context.with_prep(id.install_tag()) |prep| {
+            for ee in maybe_executable.iter() {
+                prep.declare_input("binary",
+                                   ee.to_str(),
+                                   workcache_support::digest_only_date(ee));
             }
-            outputs.push(target_exec.clone());
-        }
-        for lib in maybe_library.iter() {
-            let target_lib = target_lib.clone().expect(fmt!("I built %s but apparently \
-                                                didn't install it!", lib.to_str()));
-            let target_lib = target_lib.pop().push(lib.filename().expect("weird target lib"));
-            debug!("Copying: %s -> %s", lib.to_str(), target_lib.to_str());
-            if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) &&
-                 os::copy_file(lib, &target_lib)) {
-                cond.raise(((*lib).clone(), target_lib.clone()));
+            for ll in maybe_library.iter() {
+                prep.declare_input("binary",
+                                   ll.to_str(),
+                                   workcache_support::digest_only_date(ll));
+            }
+            let subex = maybe_executable.clone();
+            let sublib = maybe_library.clone();
+            let sub_target_ex = target_exec.clone();
+            let sub_target_lib = target_lib.clone();
+
+            do prep.exec |exe_thing| {
+                let mut outputs = ~[];
+
+                for exec in subex.iter() {
+                    debug!("Copying: %s -> %s", exec.to_str(), sub_target_ex.to_str());
+                    if !(os::mkdir_recursive(&sub_target_ex.dir_path(), U_RWX) &&
+                         os::copy_file(exec, &sub_target_ex)) {
+                        cond.raise(((*exec).clone(), sub_target_ex.clone()));
+                    }
+                    exe_thing.discover_output("binary",
+                        sub_target_ex.to_str(),
+                        workcache_support::digest_only_date(&sub_target_ex));
+                    outputs.push(sub_target_ex.to_str());
+                }
+                for lib in sublib.iter() {
+                    let target_lib = sub_target_lib
+                        .clone().expect(fmt!("I built %s but apparently \
+                                             didn't install it!", lib.to_str()));
+                    let target_lib = target_lib
+                        .pop().push(lib.filename().expect("weird target lib"));
+                    debug!("Copying: %s -> %s", lib.to_str(), sub_target_lib.to_str());
+                    if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) &&
+                         os::copy_file(lib, &target_lib)) {
+                        cond.raise(((*lib).clone(), target_lib.clone()));
+                    }
+                    exe_thing.discover_output("binary",
+                                              target_lib.to_str(),
+                                              workcache_support::digest_only_date(&target_lib));
+                    outputs.push(target_lib.to_str());
+                }
+                outputs
             }
-            outputs.push(target_lib.clone());
         }
-        outputs
     }
 
     fn prefer(&self, _id: &str, _vers: Option<~str>)  {
@@ -726,15 +773,27 @@ pub fn main_args(args: &[~str]) {
 
     debug!("Using sysroot: %s", sroot.to_str());
     debug!("Will store workcache in %s", default_workspace().to_str());
-    BuildContext {
-        context: Context {
-        cfgs: cfgs,
-        rustc_flags: rustc_flags,
-        use_rust_path_hack: use_rust_path_hack,
-        sysroot: sroot, // Currently, only tests override this
-    },
-        workcache_context: api::default_context(default_workspace()).workcache_context
-    }.run(*cmd, remaining_args)
+
+    let rm_args = remaining_args.clone();
+    let sub_cmd = cmd.clone();
+    // Wrap the rest in task::try in case of a condition failure in a task
+    let result = do task::try {
+        BuildContext {
+            context: Context {
+                cfgs: cfgs.clone(),
+                rustc_flags: rustc_flags.clone(),
+                use_rust_path_hack: use_rust_path_hack,
+                sysroot: sroot.clone(), // Currently, only tests override this
+            },
+            workcache_context: api::default_context(default_workspace()).workcache_context
+        }.run(sub_cmd, rm_args.clone())
+    };
+    // FIXME #9262: This is using the same error code for all errors,
+    // and at least one test case succeeds if rustpkg returns copy_failed_code,
+    // when actually, it might set the exit code for that even if a different
+    // unhandled condition got raised.
+    if result.is_err() { os::set_exit_status(copy_failed_code); }
+
 }
 
 /**
index 03c2f5a4fe42afa5ae5c2514cd461333aaae63ac..9d3ad1f39a74ac0cd6c9fece12a46e6be37cc48b 100644 (file)
@@ -16,8 +16,45 @@ pub enum OutputType { Main, Lib, Bench, Test }
 
 #[deriving(Eq)]
 pub enum Target {
-    // In-place build
+    /// In-place build
     Build,
-    // Install to bin/ or lib/ dir
+    /// Install to bin/ or lib/ dir
     Install
 }
+
+#[deriving(Eq, Clone)]
+pub enum WhatToBuild {
+    /// Build just one lib.rs file in `path`, which is relative to the active workspace's src/ dir
+    JustOne(Path),
+    /// Build everything
+    Everything
+}
+
+pub fn is_lib(p: &Path) -> bool {
+    file_is(p, "lib")
+}
+
+pub fn is_main(p: &Path) -> bool {
+    file_is(p, "main")
+}
+
+pub fn is_test(p: &Path) -> bool {
+    file_is(p, "test")
+}
+
+pub fn is_bench(p: &Path) -> bool {
+    file_is(p, "bench")
+}
+
+fn file_is(p: &Path, stem: &str) -> bool {
+    match p.filestem() {
+        Some(s) if s == stem => true,
+        _ => false
+    }
+}
+
+pub fn lib_name_of(p: &Path) -> Path {
+    p.push("lib.rs")
+}
+
+pub static lib_crate_filename: &'static str = "lib.rs";
index 952931fa97afc055eb7c7710abfe821d6ede8c01..918cc366799780cfe23867d28bac01084a4094e2 100644 (file)
 use syntax::diagnostic;
 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)
-}
+use util::datestamp;
 
 fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext {
     let context = workcache::Context::new(
@@ -224,18 +220,26 @@ fn rustpkg_exec() -> Path {
 }
 
 fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput {
-    command_line_test_with_env(args, cwd, None).expect("Command line test failed")
+    match command_line_test_with_env(args, cwd, None) {
+        Success(r) => r,
+        _ => fail!("Command line test failed")
+    }
 }
 
-fn command_line_test_partial(args: &[~str], cwd: &Path) -> Option<ProcessOutput> {
+fn command_line_test_partial(args: &[~str], cwd: &Path) -> ProcessResult {
     command_line_test_with_env(args, cwd, None)
 }
 
+enum ProcessResult {
+    Success(ProcessOutput),
+    Fail(int) // exit code
+}
+
 /// Runs `rustpkg` (based on the directory that this executable was
 /// invoked from) with the given arguments, in the given working directory.
 /// Returns the process's output.
 fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>)
-    -> Option<ProcessOutput> {
+    -> ProcessResult {
     let cmd = rustpkg_exec().to_str();
     let env_str = match env {
         Some(ref pairs) => pairs.map(|&(ref k, ref v)| { fmt!("%s=%s", *k, *v) }).connect(","),
@@ -266,10 +270,10 @@ fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~s
         debug!("Command %s %? failed with exit code %?; its output was {{{ %s }}}",
               cmd, args, output.status,
               str::from_utf8(output.output) + str::from_utf8(output.error));
-        None
+        Fail(output.status)
     }
     else {
-        Some(output)
+        Success(output)
     }
 }
 
@@ -410,8 +414,11 @@ fn command_line_test_output(args: &[~str]) -> ~[~str] {
 
 fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~str] {
     let mut result = ~[];
-    let p_output = command_line_test_with_env(args,
-        &os::getcwd(), Some(env)).expect("Command-line test failed");
+    let p_output = match command_line_test_with_env(args,
+        &os::getcwd(), Some(env)) {
+        Fail(_) => fail!("Command-line test failed"),
+        Success(r) => r
+    };
     let test_output = str::from_utf8(p_output.output);
     for s in test_output.split_iter('\n') {
         result.push(s.to_owned());
@@ -420,9 +427,9 @@ fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~
 }
 
 // assumes short_name and path are one and the same -- I should fix
-fn lib_output_file_name(workspace: &Path, parent: &str, short_name: &str) -> Path {
-    debug!("lib_output_file_name: given %s and parent %s and short name %s",
-           workspace.to_str(), parent, short_name);
+fn lib_output_file_name(workspace: &Path, short_name: &str) -> Path {
+    debug!("lib_output_file_name: given %s and short name %s",
+           workspace.to_str(), short_name);
     library_in_workspace(&Path(short_name),
                          short_name,
                          Build,
@@ -450,19 +457,18 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
 }
 
 /// Add a comment at the end
-fn frob_source_file(workspace: &Path, pkgid: &PkgId) {
+fn frob_source_file(workspace: &Path, pkgid: &PkgId, filename: &str) {
     use conditions::bad_path::cond;
     let pkg_src_dir = workspace.push_many([~"src", pkgid.to_str()]);
-    let contents = os::list_dir_path(&pkg_src_dir);
     let mut maybe_p = None;
-    for p in contents.iter() {
-        if p.filetype() == Some(".rs") {
-            maybe_p = Some(p);
-            break;
-        }
+    let maybe_file = pkg_src_dir.push(filename);
+    debug!("Trying to frob %s -- %s", pkg_src_dir.to_str(), filename);
+    if os::path_exists(&maybe_file) {
+        maybe_p = Some(maybe_file);
     }
+    debug!("Frobbed? %?", maybe_p);
     match maybe_p {
-        Some(p) => {
+        Some(ref p) => {
             let w = io::file_writer(p, &[io::Append]);
             match w {
                 Err(s) => { let _ = cond.raise((p.clone(), fmt!("Bad path: %s", s))); }
@@ -499,7 +505,7 @@ fn test_install_valid() {
     debug!("temp_workspace = %s", temp_workspace.to_str());
     // should have test, bench, lib, and main
     let src = PkgSrc::new(temp_workspace.clone(), false, temp_pkg_id.clone());
-    ctxt.install(src);
+    ctxt.install(src, &Everything);
     // Check that all files exist
     let exec = target_executable_in_workspace(&temp_pkg_id, &temp_workspace);
     debug!("exec = %s", exec.to_str());
@@ -528,7 +534,7 @@ fn test_install_invalid() {
     // 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);
+        ctxt.install(pkg_src, &Everything);
     };
     // Not the best test -- doesn't test that we failed in the right way.
     // Best we can do for now.
@@ -939,26 +945,28 @@ fn no_rebuilding() {
 }
 
 #[test]
-#[ignore]
 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_1 = datestamp(&lib_output_file_name(&workspace,
-                                                  ".rust",
-                                                  "bar"));
-    let foo_date_1 = datestamp(&output_file_name(&workspace, ~"foo"));
+    let bar_lib = lib_output_file_name(&workspace, "bar");
+    let bar_date_1 = datestamp(&bar_lib);
+
+    frob_source_file(&workspace, &p_id, "main.rs");
+
+    // Now make `bar` read-only so that subsequent rebuilds of it will fail
+    assert!(chmod_read_only(&bar_lib));
+
+    match command_line_test_partial([~"build", ~"foo"], &workspace) {
+        Success(*) => (), // ok
+        Fail(status) if status == 65 => fail!("no_rebuilding_dep failed: it tried to rebuild bar"),
+        Fail(_) => fail!("no_rebuilding_dep failed for some other reason")
+    }
 
-    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_2 = datestamp(&output_file_name(&workspace, ~"foo"));
+                                                   "bar"));
     assert_eq!(bar_date_1, bar_date_2);
-    assert!(foo_date_1 < foo_date_2);
-    assert!(foo_date_1 > bar_date_1);
 }
 
 #[test]
@@ -967,7 +975,7 @@ fn do_rebuild_dep_dates_change() {
     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_lib_name = lib_output_file_name(&workspace, "build", "bar");
+    let bar_lib_name = lib_output_file_name(&workspace, "bar");
     let bar_date = datestamp(&bar_lib_name);
     debug!("Datestamp on %s is %?", bar_lib_name.to_str(), bar_date);
     touch_source_file(&workspace, &dep_id);
@@ -983,11 +991,11 @@ fn do_rebuild_dep_only_contents_change() {
     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, "build", "bar"));
-    frob_source_file(&workspace, &dep_id);
+    let bar_date = datestamp(&lib_output_file_name(&workspace, "bar"));
+    frob_source_file(&workspace, &dep_id, "lib.rs");
     // should adjust the datestamp
     command_line_test([~"build", ~"foo"], &workspace);
-    let new_bar_date = datestamp(&lib_output_file_name(&workspace, "build", "bar"));
+    let new_bar_date = datestamp(&lib_output_file_name(&workspace, "bar"));
     assert!(new_bar_date > bar_date);
 }
 
@@ -1309,7 +1317,6 @@ fn rust_path_hack_build_no_arg() {
 }
 
 #[test]
-#[ignore (reason = "#7402 not yet implemented")]
 fn rust_path_install_target() {
     let dir_for_path = mkdtemp(&os::tmpdir(),
         "source_workspace").expect("rust_path_install_target failed");
@@ -1464,10 +1471,13 @@ fn test_cfg_fail() {
     let workspace = create_local_package(&p_id);
     writeFile(&workspace.push_many(["src", "foo-0.1", "main.rs"]),
                "#[cfg(quux)] fn main() {}");
-    assert!(command_line_test_partial([test_sysroot().to_str(),
+    match command_line_test_partial([test_sysroot().to_str(),
                        ~"build",
                        ~"foo"],
-                      &workspace).is_none());
+                      &workspace) {
+        Success(*) => fail!("test_cfg_fail failed"),
+        _          => ()
+    }
 }
 
 
@@ -1680,6 +1690,21 @@ fn test_target_specific_install_dir() {
     assert_executable_exists(&workspace, "foo");
 }
 
+#[test]
+fn test_dependencies_terminate() {
+ //   let a_id = PkgId::new("a");
+    let b_id = PkgId::new("b");
+//    let workspace = create_local_package_with_dep(&b_id, &a_id);
+    let workspace = create_local_package(&b_id);
+    let b_dir = workspace.push_many([~"src", ~"b-0.1"]);
+  //  writeFile(&b_dir.push("lib.rs"), "extern mod a; pub fn f() {}");
+    let b_subdir = b_dir.push("test");
+    assert!(os::mkdir_recursive(&b_subdir, U_RWX));
+    writeFile(&b_subdir.push("test.rs"),
+              "extern mod b; use b::f; #[test] fn g() { f() }");
+    command_line_test([~"install", ~"b"], &workspace);
+}
+
 /// Returns true if p exists and is executable
 fn is_executable(p: &Path) -> bool {
     use std::libc::consts::os::posix88::{S_IXUSR};
@@ -1689,3 +1714,25 @@ fn is_executable(p: &Path) -> bool {
         Some(mode) => mode & S_IXUSR as uint == S_IXUSR as uint
     }
 }
+
+#[cfg(target_os = "win32")]
+fn chmod_read_only(p: &Path) -> bool {
+    #[fixed_stack_segment];
+    unsafe {
+        do p.to_str().with_c_str |src_buf| {
+            libc::chmod(src_buf, libc::consts::os::posix88::S_IRUSR as c_int) == 0 as libc::c_int
+        }
+    }
+}
+
+#[cfg(not(target_os = "win32"))]
+fn chmod_read_only(p: &Path) -> bool {
+    #[fixed_stack_segment];
+    unsafe {
+        do p.to_str().with_c_str |src_buf| {
+            libc::chmod(src_buf,
+                        libc::consts::os::posix88::S_IRUSR as libc::mode_t) == 0
+                as libc::c_int
+        }
+    }
+}
index ab883b50f8c68e5ec6c69c3f58774d244ea34cc8..64f76dcdc605d25d9d51c5300ca3cdfaea07ea7d 100644 (file)
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::libc;
 use std::os;
 use extra::workcache;
 use rustc::driver::{driver, session};
@@ -26,7 +27,7 @@
 use path_util::{installed_library_in_workspace, U_RWX, rust_path, system_library, target_build_dir};
 use messages::error;
 
-pub use target::{OutputType, Main, Lib, Bench, Test};
+pub use target::{OutputType, Main, Lib, Bench, Test, JustOne, lib_name_of, lib_crate_filename};
 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,
@@ -171,6 +172,8 @@ pub fn compile_input(context: &BuildContext,
     // not sure if we should support anything else
 
     let out_dir = target_build_dir(workspace).push_rel(&pkg_id.path);
+    // Make the output directory if it doesn't exist already
+    assert!(os::mkdir_recursive(&out_dir, U_RWX));
 
     let binary = os::args()[0].to_managed();
 
@@ -220,7 +223,7 @@ pub fn compile_input(context: &BuildContext,
         optimize: if opt { session::Aggressive } else { session::No },
         test: what == Test || what == Bench,
         maybe_sysroot: Some(sysroot_to_use),
-        addl_lib_search_paths: @mut (~[out_dir.clone()]),
+        addl_lib_search_paths: @mut (~[]),
         output_type: output_type,
         .. (*driver::build_session_options(binary, &matches, diagnostic::emit)).clone()
     };
@@ -329,6 +332,9 @@ pub fn compile_crate_from_input(input: &Path,
     // Register dependency on the source file
     exec.discover_input("file", input.to_str(), digest_file_with_date(input));
 
+    debug!("Built %s, date = %?", outputs.out_filename.to_str(),
+           datestamp(&outputs.out_filename));
+
     Some(outputs.out_filename)
 }
 
@@ -409,7 +415,8 @@ pub fn find_and_install_dependencies(context: &BuildContext,
                             workspaces[0]
                         };
                         let (outputs_disc, inputs_disc) =
-                            context.install(PkgSrc::new(dep_workspace.clone(), false, pkg_id));
+                            context.install(PkgSrc::new(dep_workspace.clone(),
+                                false, pkg_id), &JustOne(Path(lib_crate_filename)));
                         debug!("Installed %s, returned %? dependencies and \
                                %? transitive dependencies",
                                lib_name, outputs_disc.len(), inputs_disc.len());
@@ -435,10 +442,11 @@ pub fn find_and_install_dependencies(context: &BuildContext,
                         debug!("Adding additional search path: %s", lib_name);
                         let installed_library =
                             installed_library_in_workspace(&Path(lib_name), &dep_workspace)
-                                .expect( fmt!("rustpkg failed to install dependency %s",
+                                .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());
+                        debug!("Installed %s into %s [%?]", lib_name, install_dir.to_str(),
+                               datestamp(&installed_library));
                         save(install_dir);
                     }
                 }}
@@ -449,37 +457,6 @@ pub fn find_and_install_dependencies(context: &BuildContext,
     };
 }
 
-#[cfg(windows)]
-pub fn link_exe(_src: &Path, _dest: &Path) -> bool {
-    #[fixed_stack_segment]; #[inline(never)];
-
-    /* FIXME (#1768): Investigate how to do this on win32
-       Node wraps symlinks by having a .bat,
-       but that won't work with minGW. */
-
-    false
-}
-
-#[cfg(target_os = "linux")]
-#[cfg(target_os = "android")]
-#[cfg(target_os = "freebsd")]
-#[cfg(target_os = "macos")]
-pub fn link_exe(src: &Path, dest: &Path) -> bool {
-    #[fixed_stack_segment]; #[inline(never)];
-
-    use std::c_str::ToCStr;
-    use std::libc;
-
-    unsafe {
-        do src.with_c_str |src_buf| {
-            do dest.with_c_str |dest_buf| {
-                libc::link(src_buf, dest_buf) == 0 as libc::c_int &&
-                    libc::chmod(dest_buf, 755) == 0 as libc::c_int
-            }
-        }
-    }
-}
-
 pub fn mk_string_lit(s: @str) -> ast::lit {
     Spanned {
         node: ast::lit_str(s),
@@ -516,3 +493,12 @@ pub fn option_to_vec<T>(x: Option<T>) -> ~[T] {
 // tjc: cheesy
 fn debug_flags() -> ~[~str] { ~[] }
 // static DEBUG_FLAGS: ~[~str] = ~[~"-Z", ~"time-passes"];
+
+
+/// Returns the last-modified date as an Option
+pub fn datestamp(p: &Path) -> Option<libc::time_t> {
+    debug!("Scrutinizing datestamp for %s - does it exist? %?", p.to_str(), os::path_exists(p));
+    let out = p.stat().map(|stat| stat.st_mtime);
+    debug!("Date = %?", out);
+    out.map(|t| { *t as libc::time_t })
+}
index e2416782d987cd43a42e079eeecb902f06caa57c..daf35c988c823861f7237e86841ea6e0d9de0eef 100644 (file)
@@ -56,3 +56,8 @@ pub fn discover_outputs(e: &mut workcache::Exec, outputs: ~[Path]) {
         e.discover_output("binary", p.to_str(), digest_only_date(p));
     }
 }
+
+/// Returns the function name for building a crate
+pub fn crate_tag(p: &Path) -> ~str {
+    p.to_str() // implicitly, it's "build(p)"...
+}