]> git.lizzy.rs Git - rust.git/commitdiff
rustpkg: Make checked-out source files read-only, and overhaul where temporary files...
authorTim Chevalier <chevalier@alum.wellesley.edu>
Sat, 5 Oct 2013 20:40:01 +0000 (16:40 -0400)
committerTim Chevalier <chevalier@alum.wellesley.edu>
Thu, 10 Oct 2013 22:16:31 +0000 (15:16 -0700)
rustpkg now makes source files that it checks out automatically read-only, and stores
them under build/.

Also, refactored the `PkgSrc` type to keep track of separate source and destination
workspaces, as well as to have a `build_workspace` method that returns the workspace
to put temporary files in (usually the source, sometimes the destination -- see
comments for more details).

Closes #6480

doc/rustpkg.md
src/librustpkg/api.rs
src/librustpkg/conditions.rs
src/librustpkg/package_id.rs
src/librustpkg/package_source.rs
src/librustpkg/path_util.rs
src/librustpkg/rustpkg.rs
src/librustpkg/source_control.rs
src/librustpkg/tests.rs
src/librustpkg/util.rs
src/librustpkg/workspace.rs

index 399a76426654ac76dd2e82aca1cf590b6e1b2069..53829e4519641dec11e03fa6ec37091a761b1e51 100644 (file)
@@ -137,6 +137,9 @@ and builds it in any workspace(s) where it finds one.
 Supposing such packages are found in workspaces X, Y, and Z,
 the command leaves behind files in `X`'s, `Y`'s, and `Z`'s `build` directories,
 but not in their `lib` or `bin` directories.
+(The exception is when rustpkg fetches a package `foo`'s sources from a remote repository.
+In that case, it stores both the sources *and* the build artifacts for `foo`
+in the workspace that `foo` will install to (see ##install below)).
 
 ## clean
 
@@ -148,7 +151,11 @@ but not in their `lib` or `bin` directories.
 If `RUST_PATH` is declared as an environment variable, then rustpkg installs the
 libraries and executables into the `lib` and `bin` subdirectories
 of the first entry in `RUST_PATH`.
-Otherwise, it installs them into `foo`'s `lib` and `bin` directories.
+Otherwise, if the current working directory CWD is a workspace,
+it installs them into CWD's `lib` and `bin` subdirectories.
+Otherwise, if the current working directory is CWD,
+it installs them into the .rust/lib and .rust/bin subdirectories of CWD
+(creating them if necessary). 
 
 ## test
 
index de673972932d3847dda29c4ff991384adb09bb26..390a09d4aa33e8dc9eb85a187d48b83d132504b5 100644 (file)
@@ -16,6 +16,8 @@
 use version::Version;
 use workcache_support::*;
 
+pub use source_control::{safe_git_clone, git_clone_url};
+
 use std::os;
 use extra::arc::{Arc,RWArc};
 use extra::workcache;
@@ -68,15 +70,17 @@ pub fn build_lib(sysroot: Path, root: Path, name: ~str, version: Version,
                  lib: Path) {
     let cx = default_context(sysroot);
     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: ~[]
-        };
+        source_workspace: root.clone(),
+        build_in_destination: false,
+        destination_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.build(&cx, ~[]);
 }
 
@@ -84,7 +88,9 @@ pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version,
                  main: Path) {
     let cx = default_context(sysroot);
     let pkg_src = PkgSrc {
-        workspace: root.clone(),
+        source_workspace: root.clone(),
+        build_in_destination: false,
+        destination_workspace: root.clone(),
         start_dir: root.push("src").push(name),
         id: PkgId{ version: version, ..PkgId::new(name)},
         libs: ~[],
@@ -100,7 +106,7 @@ pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version,
 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), &Everything);
+    cx.install(PkgSrc::new(workspace.clone(), workspace, false, pkgid), &Everything);
 }
 
 fn mk_crate(p: Path) -> Crate {
index 2e049d252bcd792ceeafa223822bf54cea89ae4f..f40f48e851fbaceb7caa5c55490a7e60aabc94b7 100644 (file)
@@ -50,3 +50,7 @@
 condition! {
     pub failed_to_create_temp_dir: (~str) -> Path;
 }
+
+condition! {
+    pub git_checkout_failed: (~str, Path) -> ();
+}
index 68cfa3220f228599c034b8764532c4e31b21245e..c06e197787c3eb22990dfab828536585a007601e 100644 (file)
@@ -102,10 +102,7 @@ pub fn is_complex(&self) -> bool {
     }
 
     pub fn prefixes_iter(&self) -> Prefixes {
-        Prefixes {
-            components: self.path.components().to_owned(),
-            remaining: ~[]
-        }
+        prefixes_iter(&self.path)
     }
 
     // This is the workcache function name for the *installed*
@@ -116,6 +113,13 @@ pub fn install_tag(&self) -> ~str {
     }
 }
 
+pub fn prefixes_iter(p: &Path) -> Prefixes {
+    Prefixes {
+        components: p.components().to_owned(),
+        remaining: ~[]
+    }
+}
+
 struct Prefixes {
     priv components: ~[~str],
     priv remaining: ~[~str]
index 8428c373de6f67e888004b6556f83d55a6b5e0e0..86566020812eeec313747042dfd28a274dfa02ae 100644 (file)
 use context::*;
 use crate::Crate;
 use messages::*;
-use source_control::{git_clone, git_clone_general};
-use path_util::{find_dir_using_rust_path_hack, default_workspace, make_dir_rwx_recursive};
+use source_control::{safe_git_clone, git_clone_url, DirToUse, CheckedOutSources};
+use source_control::make_read_only;
+use path_util::{find_dir_using_rust_path_hack, make_dir_rwx_recursive};
+use path_util::{target_build_dir, versionize};
 use util::compile_crate;
-use workspace::is_workspace;
 use workcache_support;
 use workcache_support::crate_tag;
 use extra::workcache;
 #[deriving(Clone)]
 pub struct PkgSrc {
     /// Root of where the package source code lives
-    workspace: Path,
+    source_workspace: Path,
+    /// If build_in_destination is true, temporary results should
+    /// go in the build/ subdirectory of the destination workspace.
+    /// (Otherwise, they go in the build/ subdirectory of the
+    /// source workspace.) This happens if the "RUST_PATH hack" is
+    /// in effect, or if sources were fetched from a remote
+    /// repository.
+    build_in_destination: bool,
+    /// Where to install the results. May or may not be the same
+    /// as source_workspace
+    destination_workspace: Path,
     // Directory to start looking in for packages -- normally
     // this is workspace/src/id but it may be just workspace
     start_dir: Path,
@@ -41,11 +52,15 @@ pub struct PkgSrc {
     benchs: ~[Crate],
 }
 
+pub enum BuildSort { InPlace, Discovered }
+
 impl ToStr for PkgSrc {
     fn to_str(&self) -> ~str {
-        format!("Package ID {} in start dir {} [workspace = {}]",
+        format!("Package ID {} in start dir {} [workspaces = {} -> {}]",
                 self.id.to_str(),
-                self.start_dir.to_str(), self.workspace.to_str())
+                self.start_dir.to_str(),
+                self.source_workspace.to_str(),
+                self.destination_workspace.to_str())
     }
 }
 condition! {
@@ -55,27 +70,53 @@ fn to_str(&self) -> ~str {
 
 impl PkgSrc {
 
-    pub fn new(workspace: Path, use_rust_path_hack: bool, id: PkgId) -> PkgSrc {
+    pub fn new(mut source_workspace: Path,
+               destination_workspace: Path,
+               use_rust_path_hack: bool,
+               id: PkgId) -> PkgSrc {
         use conditions::nonexistent_package::cond;
 
         debug2!("Checking package source for package ID {}, \
-               workspace = {} use_rust_path_hack = {:?}",
-               id.to_str(), workspace.to_str(), use_rust_path_hack);
+                workspace = {} -> {}, use_rust_path_hack = {:?}",
+                id.to_str(),
+                source_workspace.to_str(),
+                destination_workspace.to_str(),
+                use_rust_path_hack);
+
+        let mut destination_workspace = destination_workspace.clone();
 
         let mut to_try = ~[];
+        let mut output_names = ~[];
+        let build_dir = target_build_dir(&source_workspace);
+
         if use_rust_path_hack {
-            to_try.push(workspace.clone());
+            to_try.push(source_workspace.clone());
         } else {
-            let result = workspace.push("src").push_rel(&id.path.pop()).push(format!("{}-{}",
+            // We search for sources under both src/ and build/ , because build/ is where
+            // automatically-checked-out sources go.
+            let result = source_workspace.push("src").push_rel(&id.path.pop()).push(format!("{}-{}",
                                                          id.short_name, id.version.to_str()));
             to_try.push(result);
-            to_try.push(workspace.push("src").push_rel(&id.path));
+            to_try.push(source_workspace.push("src").push_rel(&id.path));
+
+            let result = build_dir.push("src").push_rel(&id.path.pop()).push(format!("{}-{}",
+                                                         id.short_name, id.version.to_str()));
+            to_try.push(result.clone());
+            output_names.push(result);
+            let other_result = build_dir.push("src").push_rel(&id.path);
+            to_try.push(other_result.clone());
+            output_names.push(other_result);
+
         }
 
         debug2!("Checking dirs: {:?}", to_try.map(|s| s.to_str()).connect(":"));
 
         let path = to_try.iter().find(|&d| os::path_exists(d));
 
+        // See the comments on the definition of PkgSrc
+        let mut build_in_destination = use_rust_path_hack;
+        debug2!("1. build_in_destination = {:?}", build_in_destination);
+
         let dir: Path = match path {
             Some(d) => (*d).clone(),
             None => {
@@ -83,23 +124,33 @@ pub fn new(workspace: Path, use_rust_path_hack: bool, id: PkgId) -> PkgSrc {
                 // That is, is this a package ID that points into the middle of a workspace?
                 for (prefix, suffix) in id.prefixes_iter() {
                     let package_id = PkgId::new(prefix.to_str());
-                    let path = workspace.push("src").push_rel(&package_id.path);
+                    let path = build_dir.push_rel(&package_id.path);
                     debug2!("in loop: checking if {} is a directory", path.to_str());
                     if os::path_is_dir(&path) {
-                        let ps = PkgSrc::new(workspace.clone(),
+                        let ps = PkgSrc::new(source_workspace,
+                                             destination_workspace,
                                              use_rust_path_hack,
                                              PkgId::new(prefix.to_str()));
-                        debug2!("pkgsrc: Returning [{}|{}|{}]", workspace.to_str(),
-                                ps.start_dir.push_rel(&suffix).to_str(), ps.id.to_str());
-
-                        return PkgSrc {
-                            workspace: workspace,
-                            start_dir: ps.start_dir.push_rel(&suffix),
-                            id: ps.id,
-                            libs: ~[],
-                            mains: ~[],
-                            tests: ~[],
-                            benchs: ~[]
+                        match ps {
+                            PkgSrc {
+                                source_workspace: source,
+                                destination_workspace: destination,
+                                start_dir: start,
+                                id: id, _ } => {
+                                let result = PkgSrc {
+                                    source_workspace: source.clone(),
+                                    build_in_destination: build_in_destination,
+                                    destination_workspace: destination,
+                                    start_dir: start.push_rel(&suffix),
+                                    id: id,
+                                    libs: ~[],
+                                    mains: ~[],
+                                    tests: ~[],
+                                    benchs: ~[]
+                                };
+                                debug2!("pkgsrc: Returning {}", result.to_str());
+                                return result;
+                            }
                         }
 
                     };
@@ -107,14 +158,33 @@ pub fn new(workspace: Path, use_rust_path_hack: bool, id: PkgId) -> PkgSrc {
 
                 // Ok, no prefixes work, so try fetching from git
                 let mut ok_d = None;
-                for w in to_try.iter() {
+                for w in output_names.iter() {
                     debug2!("Calling fetch_git on {}", w.to_str());
-                    let gf = PkgSrc::fetch_git(w, &id);
-                    for p in gf.iter() {
+                    let target_dir_opt = PkgSrc::fetch_git(w, &id);
+                    for p in target_dir_opt.iter() {
                         ok_d = Some(p.clone());
+                        build_in_destination = true;
+                        debug2!("2. build_in_destination = {:?}", build_in_destination);
                         break;
                     }
-                    if ok_d.is_some() { break; }
+                    match ok_d {
+                        Some(ref d) => {
+                            if d.is_parent_of(&id.path)
+                                || d.is_parent_of(&versionize(&id.path, &id.version)) {
+                                // Strip off the package ID
+                                source_workspace = d.clone();
+                                for _ in id.path.components().iter() {
+                                    source_workspace = source_workspace.pop();
+                                }
+                                // Strip off the src/ part
+                                source_workspace = source_workspace.pop();
+                                // Strip off the build/<target-triple> part to get the workspace
+                                destination_workspace = source_workspace.pop().pop();
+                            }
+                            break;
+                        }
+                        None => ()
+                    }
                 }
                 match ok_d {
                     Some(d) => d,
@@ -138,6 +208,9 @@ pub fn new(workspace: Path, use_rust_path_hack: bool, id: PkgId) -> PkgSrc {
                 }
             }
         };
+        debug2!("3. build_in_destination = {:?}", build_in_destination);
+        debug2!("source: {} dest: {}", source_workspace.to_str(), destination_workspace.to_str());
+
         debug2!("For package id {}, returning {}", id.to_str(), dir.to_str());
 
         if !os::path_is_dir(&dir) {
@@ -145,11 +218,10 @@ pub fn new(workspace: Path, use_rust_path_hack: bool, id: PkgId) -> PkgSrc {
                                         non-directory"));
         }
 
-        debug2!("pkgsrc: Returning \\{{}|{}|{}\\}", workspace.to_str(),
-               dir.to_str(), id.to_str());
-
         PkgSrc {
-            workspace: workspace,
+            source_workspace: source_workspace.clone(),
+            build_in_destination: build_in_destination,
+            destination_workspace: destination_workspace,
             start_dir: dir,
             id: id,
             libs: ~[],
@@ -165,56 +237,53 @@ pub fn new(workspace: Path, use_rust_path_hack: bool, id: PkgId) -> PkgSrc {
     /// refers to a git repo on the local version, also check it out.
     /// (right now we only support git)
     pub fn fetch_git(local: &Path, pkgid: &PkgId) -> Option<Path> {
-        use conditions::failed_to_create_temp_dir::cond;
+        use conditions::git_checkout_failed::cond;
 
         // We use a temporary directory because if the git clone fails,
         // it creates the target directory anyway and doesn't delete it
 
-        let scratch_dir = extra::tempfile::mkdtemp(&os::tmpdir(), "rustpkg");
-        let clone_target = match scratch_dir {
-            Some(d) => d.push("rustpkg_temp"),
-            None    => cond.raise(~"Failed to create temporary directory for fetching git sources")
-        };
-
         debug2!("Checking whether {} (path = {}) exists locally. Cwd = {}, does it? {:?}",
-               pkgid.to_str(), pkgid.path.to_str(),
-               os::getcwd().to_str(),
-               os::path_exists(&pkgid.path));
-
-        if os::path_exists(&pkgid.path) {
-            debug2!("{} exists locally! Cloning it into {}",
-                   pkgid.path.to_str(), local.to_str());
-            // Ok to use local here; we know it will succeed
-            git_clone(&pkgid.path, local, &pkgid.version);
-            return Some(local.clone());
-        }
+                pkgid.to_str(), pkgid.path.to_str(),
+                os::getcwd().to_str(),
+                os::path_exists(&pkgid.path));
+
+        match safe_git_clone(&pkgid.path, &pkgid.version, local) {
+            CheckedOutSources => {
+                make_read_only(local);
+                Some(local.clone())
+            }
+            DirToUse(clone_target) => {
+                if pkgid.path.components().len() < 2 {
+                    // If a non-URL, don't bother trying to fetch
+                    return None;
+                }
 
-        if pkgid.path.components().len() < 2 {
-            // If a non-URL, don't bother trying to fetch
-            return None;
-        }
+                let url = format!("https://{}", pkgid.path.to_str());
+                debug2!("Fetching package: git clone {} {} [version={}]",
+                        url, clone_target.to_str(), pkgid.version.to_str());
 
-        let url = format!("https://{}", pkgid.path.to_str());
-        debug2!("Fetching package: git clone {} {} [version={}]",
-                  url, clone_target.to_str(), pkgid.version.to_str());
+                let mut failed = false;
 
-        if git_clone_general(url, &clone_target, &pkgid.version) {
-            // Since the operation succeeded, move clone_target to local.
-            // First, create all ancestor directories.
-            if make_dir_rwx_recursive(&local.pop())
-                && os::rename_file(&clone_target, local) {
-                 Some(local.clone())
-            }
-            else {
-                 None
+                do cond.trap(|_| {
+                    failed = true;
+                }).inside {
+                    git_clone_url(url, &clone_target, &pkgid.version);
+                };
+
+                if failed {
+                    return None;
+                }
+
+                // Move clone_target to local.
+                // First, create all ancestor directories.
+                let moved = make_dir_rwx_recursive(&local.pop())
+                    && os::rename_file(&clone_target, local);
+                if moved { Some(local.clone()) }
+                    else { None }
             }
         }
-        else {
-            None
-        }
     }
 
-
     // If a file named "pkg.rs" in the start directory exists,
     // return the path for it. Otherwise, None
     pub fn package_script_option(&self) -> Option<Path> {
@@ -292,7 +361,6 @@ pub fn find_crates_with_filter(&mut self, filter: &fn(&str) -> bool) {
 
     fn build_crates(&self,
                     ctx: &BuildContext,
-                    destination_dir: &Path,
                     crates: &[Crate],
                     cfgs: &[~str],
                     what: OutputType) {
@@ -311,7 +379,7 @@ fn build_crates(&self,
                 let subpath_str = path_str.clone();
                 let subcx = ctx.clone();
                 let id = self.id.clone();
-                let sub_dir = destination_dir.clone();
+                let sub_dir = self.build_workspace().clone();
                 let sub_flags = crate.flags.clone();
                 do prep.exec |exec| {
                     let result = compile_crate(&subcx,
@@ -351,42 +419,30 @@ pub fn declare_inputs(&self, prep: &mut workcache::Prep) {
     // Encodable.
     pub fn build(&self,
                  build_context: &BuildContext,
-                 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.workspace) {
-            debug2!("{} is indeed a workspace", self.workspace.to_str());
-            self.workspace.clone()
-        } else {
-            // It would be nice to have only one place in the code that checks
-            // for the use_rust_path_hack flag...
-            if build_context.context.use_rust_path_hack {
-                let rs = default_workspace();
-                debug2!("Using hack: {}", rs.to_str());
-                rs
-            } else {
-                cond.raise(format!("Package root {} is not a workspace; pass in --rust_path_hack \
-                                        if you want to treat it as a package source",
-                                self.workspace.to_str()))
-            }
-        };
-
+                 cfgs: ~[~str]) {
         let libs = self.libs.clone();
         let mains = self.mains.clone();
         let tests = self.tests.clone();
         let benchs = self.benchs.clone();
         debug2!("Building libs in {}, destination = {}",
-               destination_workspace.to_str(), destination_workspace.to_str());
-        self.build_crates(build_context, &destination_workspace, libs, cfgs, Lib);
+               self.source_workspace.to_str(), self.build_workspace().to_str());
+        self.build_crates(build_context, libs, cfgs, Lib);
         debug2!("Building mains");
-        self.build_crates(build_context, &destination_workspace, mains, cfgs, Main);
+        self.build_crates(build_context, mains, cfgs, Main);
         debug2!("Building tests");
-        self.build_crates(build_context, &destination_workspace, tests, cfgs, Test);
+        self.build_crates(build_context, tests, cfgs, Test);
         debug2!("Building benches");
-        self.build_crates(build_context, &destination_workspace, benchs, cfgs, Bench);
-        destination_workspace.to_str()
+        self.build_crates(build_context, benchs, cfgs, Bench);
+    }
+
+    /// Return the workspace to put temporary files in. See the comment on `PkgSrc`
+    pub fn build_workspace<'a>(&'a self) -> &'a Path {
+        if self.build_in_destination {
+            &self.destination_workspace
+        }
+        else {
+            &self.source_workspace
+        }
     }
 
     /// Debugging
index f0e9274144577f63d11f9c945bfd30f34e3d37bc..fbb2255ad1ccb3684e585a05b0244c8f81230c75 100644 (file)
@@ -16,6 +16,7 @@
 pub use rustc::metadata::filesearch::rust_path;
 use rustc::driver::driver::host_triple;
 
+use std::libc;
 use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
 use std::os::mkdir_recursive;
 use std::os;
@@ -447,9 +448,30 @@ pub fn user_set_rust_path() -> bool {
 }
 
 /// Append the version string onto the end of the path's filename
-fn versionize(p: &Path, v: &Version) -> Path {
+pub fn versionize(p: &Path, v: &Version) -> Path {
     let q = p.file_path().to_str();
     p.with_filename(format!("{}-{}", q, v.to_str()))
 }
 
 
+#[cfg(target_os = "win32")]
+pub fn chmod_read_only(p: &Path) -> bool {
+    #[fixed_stack_segment];
+    unsafe {
+        do p.to_str().with_c_str |src_buf| {
+            libc::chmod(src_buf, S_IRUSR as libc::c_int) == 0 as libc::c_int
+        }
+    }
+}
+
+#[cfg(not(target_os = "win32"))]
+pub fn chmod_read_only(p: &Path) -> bool {
+    #[fixed_stack_segment];
+    unsafe {
+        do p.to_str().with_c_str |src_buf| {
+            libc::chmod(src_buf, S_IRUSR as libc::mode_t) == 0
+                as libc::c_int
+        }
+    }
+}
+
index 42b8ebe495355df41e69a56e64fdce6cf6987b82..e6ce5ba9d499481b444ec2a4bfb078ea042ae3a3 100644 (file)
@@ -39,8 +39,9 @@
 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 source_control::{CheckedOutSources, is_git_dir, make_read_only};
 use workspace::{each_pkg_parent_workspace, pkg_parent_workspaces, cwd_to_workspace};
+use workspace::determine_destination;
 use context::{Context, BuildContext,
                        RustcFlags, Trans, Link, Nothing, Pretty, Analysis, Assemble,
                        LLVMAssemble, LLVMCompileBitcode};
@@ -183,7 +184,7 @@ pub trait CtxMethods {
     /// Returns a pair of the selected package ID, and the destination workspace
     fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(PkgId, Path)>;
     /// Returns the destination workspace
-    fn build(&self, pkg_src: &mut PkgSrc, what: &WhatToBuild) -> Path;
+    fn build(&self, pkg_src: &mut PkgSrc, what: &WhatToBuild);
     fn clean(&self, workspace: &Path, id: &PkgId);
     fn info(&self);
     /// Returns a pair. First component is a list of installed paths,
@@ -208,34 +209,47 @@ fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(PkgId, Path)>
                 None if self.context.use_rust_path_hack => {
                     let cwd = os::getcwd();
                     let pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]);
-                    let mut pkg_src = PkgSrc::new(cwd, true, pkgid);
-                    let dest_ws = self.build(&mut pkg_src, what);
-                    Some((pkg_src.id, dest_ws))
+                    let mut pkg_src = PkgSrc::new(cwd, default_workspace(), true, pkgid);
+                    self.build(&mut pkg_src, what);
+                    match pkg_src {
+                        PkgSrc { destination_workspace: ws,
+                                 id: id, _ } => {
+                            Some((id, ws))
+                        }
+                    }
                 }
                 None => { usage::build(); None }
                 Some((ws, pkgid)) => {
-                    let mut pkg_src = PkgSrc::new(ws, false, pkgid);
-                    let dest_ws = self.build(&mut pkg_src, what);
-                    Some((pkg_src.id, dest_ws))
+                    let mut pkg_src = PkgSrc::new(ws.clone(), ws, false, pkgid);
+                    self.build(&mut pkg_src, what);
+                    match pkg_src {
+                        PkgSrc { destination_workspace: ws,
+                                 id: id, _ } => {
+                            Some((id, ws))
+                        }
+                    }
                 }
             }
         } else {
             // The package id is presumed to be the first command-line
             // argument
             let pkgid = PkgId::new(args[0].clone());
-            let mut dest_ws = None;
+            let mut dest_ws = default_workspace();
             do each_pkg_parent_workspace(&self.context, &pkgid) |workspace| {
                 debug2!("found pkg {} in workspace {}, trying to build",
                        pkgid.to_str(), workspace.to_str());
-                let mut pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone());
-                dest_ws = Some(self.build(&mut pkg_src, what));
+                dest_ws = determine_destination(os::getcwd(),
+                                                self.context.use_rust_path_hack,
+                                                workspace);
+                let mut pkg_src = PkgSrc::new(workspace.clone(), dest_ws.clone(),
+                                              false, pkgid.clone());
+                self.build(&mut pkg_src, what);
                 true
             };
-            assert!(dest_ws.is_some());
             // n.b. If this builds multiple packages, it only returns the workspace for
             // the last one. The whole building-multiple-packages-with-the-same-ID is weird
             // anyway and there are no tests for it, so maybe take it out
-            Some((pkgid, dest_ws.unwrap()))
+            Some((pkgid, dest_ws))
         }
     }
     fn run(&self, cmd: &str, args: ~[~str]) {
@@ -278,11 +292,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), &Everything);
+                            self.install(PkgSrc::new(cwd, default_workspace(),
+                                                     true, inferred_pkgid), &Everything);
                         }
                         None  => { usage::install(); return; }
                         Some((ws, pkgid))                => {
-                            let pkg_src = PkgSrc::new(ws, false, pkgid);
+                            let pkg_src = PkgSrc::new(ws.clone(), ws.clone(), false, pkgid);
                             self.install(pkg_src, &Everything);
                       }
                   }
@@ -295,14 +310,17 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                     debug2!("package ID = {}, 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].clone(), false, pkgid.clone());
+                        let d = default_workspace();
+                        let src = PkgSrc::new(d.clone(), d, false, pkgid.clone());
                         self.install(src, &Everything);
                     }
                     else {
                         for workspace in workspaces.iter() {
+                            let dest = determine_destination(os::getcwd(),
+                                                             self.context.use_rust_path_hack,
+                                                             workspace);
                             let src = PkgSrc::new(workspace.clone(),
+                                                  dest,
                                                   self.context.use_rust_path_hack,
                                                   pkgid.clone());
                             self.install(src, &Everything);
@@ -382,12 +400,10 @@ fn do_cmd(&self, _cmd: &str, _pkgname: &str)  {
         fail2!("`do` not yet implemented");
     }
 
-    /// Returns the destination workspace
-    /// In the case of a custom build, we don't know, so we just return the source workspace
-    /// 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();
+    fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) {
+        use conditions::git_checkout_failed::cond;
+
+        let workspace = pkg_src.source_workspace.clone();
         let pkgid = pkg_src.id.clone();
 
         debug2!("build: workspace = {} (in Rust path? {:?} is git dir? {:?} \
@@ -399,12 +415,20 @@ fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) -> Path {
         // then clone it into the first entry in RUST_PATH, and repeat
         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 git_result = source_control::safe_git_clone(&workspace.push_rel(&pkgid.path),
+                                                            &pkgid.version,
+                                                            &out_dir);
+            match git_result {
+                CheckedOutSources => make_read_only(&out_dir),
+                _ => cond.raise((pkgid.path.to_str(), out_dir.clone()))
+            };
             let default_ws = default_workspace();
             debug2!("Calling build recursively with {:?} and {:?}", default_ws.to_str(),
                    pkgid.to_str());
-            return self.build(&mut PkgSrc::new(default_ws, false, pkgid.clone()), what_to_build);
+            return self.build(&mut PkgSrc::new(default_ws.clone(),
+                                               default_ws,
+                                               false,
+                                               pkgid.clone()), what_to_build);
         }
 
         // Is there custom build logic? If so, use it
@@ -469,17 +493,12 @@ fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) -> Path {
                         PkgSrc::push_crate(&mut pkg_src.benchs, 0, p);
                     } else {
                         warn(format!("Not building any crates for dependency {}", p.to_str()));
-                        return workspace.clone();
+                        return;
                     }
                 }
             }
             // Build it!
-            let rs_path = pkg_src.build(self, cfgs);
-            Path(rs_path)
-        }
-        else {
-            // Just return the source workspace
-            workspace.clone()
+            pkg_src.build(self, cfgs);
         }
     }
 
@@ -509,11 +528,13 @@ fn install(&self, mut pkg_src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str,
         let id = pkg_src.id.clone();
 
         let mut installed_files = ~[];
-        let inputs = ~[];
+        let mut inputs = ~[];
+
+        debug2!("Installing package source: {}", pkg_src.to_str());
 
         // 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();
+        self.build(&mut pkg_src, what);
 
         let to_do = ~[pkg_src.libs.clone(), pkg_src.mains.clone(),
                       pkg_src.tests.clone(), pkg_src.benchs.clone()];
@@ -522,41 +543,35 @@ fn install(&self, mut pkg_src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str,
             for c in cs.iter() {
                 let path = pkg_src.start_dir.push_rel(&c.file).normalize();
                 debug2!("Recording input: {}", path.to_str());
-                installed_files.push(path);
+                inputs.push((~"file", path.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)
-        };
-        debug2!("install: destination workspace = {}, id = {}, installing to {}",
-               destination_workspace, id.to_str(), actual_workspace.to_str());
-        let result = self.install_no_build(&Path(destination_workspace),
-                                           &actual_workspace,
+
+        let result = self.install_no_build(pkg_src.build_workspace(),
+                                           &pkg_src.destination_workspace,
                                            &id).map(|s| Path(*s));
         debug2!("install: id = {}, about to call discover_outputs, {:?}",
                id.to_str(), result.to_str());
         installed_files = installed_files + result;
-        note(format!("Installed package {} to {}", id.to_str(), actual_workspace.to_str()));
+        note(format!("Installed package {} to {}",
+                     id.to_str(),
+                     pkg_src.destination_workspace.to_str()));
         (installed_files, inputs)
     }
 
     // again, working around lack of Encodable for Path
     fn install_no_build(&self,
-                        source_workspace: &Path,
+                        build_workspace: &Path,
                         target_workspace: &Path,
                         id: &PkgId) -> ~[~str] {
         use conditions::copy_failed::cond;
 
+        debug2!("install_no_build: assuming {} comes from {} with target {}",
+               id.to_str(), build_workspace.to_str(), target_workspace.to_str());
+
         // Now copy stuff into the install dirs
-        let maybe_executable = built_executable_in_workspace(id, source_workspace);
-        let maybe_library = built_library_in_workspace(id, source_workspace);
+        let maybe_executable = built_executable_in_workspace(id, build_workspace);
+        let maybe_library = built_library_in_workspace(id, build_workspace);
         let target_exec = target_executable_in_workspace(id, target_workspace);
         let target_lib = maybe_library.as_ref()
             .map(|_| target_library_in_workspace(id, target_workspace));
@@ -602,11 +617,11 @@ fn install_no_build(&self,
                                              didn't install it!", lib.to_str()));
                     let target_lib = target_lib
                         .pop().push(lib.filename().expect("weird target lib"));
-                    debug2!("Copying: {} -> {}", 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()));
                     }
+                    debug2!("3. discovering output {}", target_lib.to_str());
                     exe_thing.discover_output("binary",
                                               target_lib.to_str(),
                                               workcache_support::digest_only_date(&target_lib));
@@ -841,25 +856,6 @@ pub fn main_args(args: &[~str]) -> int {
     return 0;
 }
 
-/**
- * Get the working directory of the package script.
- * Assumes that the package script has been compiled
- * in is the working directory.
- */
-pub fn work_dir() -> Path {
-    os::self_exe_path().unwrap()
-}
-
-/**
- * Get the source directory of the package (i.e.
- * where the crates are located). Assumes
- * that the cwd is changed to it before
- * running this executable.
- */
-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(),
index 3d03d89bc20ab10845bcb30dcdaaec3dd725b1a8..877247bccadf72758a2711fe8234a641f13b3a4f 100644 (file)
 
 use std::{io, os, run, str};
 use std::run::{ProcessOutput, ProcessOptions, Process};
+use extra::tempfile;
 use version::*;
+use path_util::chmod_read_only;
 
-/// For a local git repo
-pub fn git_clone(source: &Path, target: &Path, v: &Version) {
-    assert!(os::path_is_dir(source));
-    assert!(is_git_dir(source));
-    if !os::path_exists(target) {
-        debug2!("Running: git clone {} {}", source.to_str(), target.to_str());
-        let outp = run::process_output("git", [~"clone", source.to_str(), target.to_str()]);
-        if outp.status != 0 {
-            io::println(str::from_utf8_owned(outp.output.clone()));
-            io::println(str::from_utf8_owned(outp.error));
-            fail2!("Couldn't `git clone` {}", source.to_str());
-        }
-        else {
-            match v {
-                &ExactRevision(ref s) => {
-                    debug2!("`Running: git --work-tree={} --git-dir={} checkout {}",
-                           *s, target.to_str(), target.push(".git").to_str());
-                    let outp = run::process_output("git",
-                                   [format!("--work-tree={}", target.to_str()),
-                                    format!("--git-dir={}", target.push(".git").to_str()),
-                                    ~"checkout", format!("{}", *s)]);
-                    if outp.status != 0 {
-                        io::println(str::from_utf8_owned(outp.output.clone()));
-                        io::println(str::from_utf8_owned(outp.error));
-                        fail2!("Couldn't `git checkout {}` in {}",
-                              *s, target.to_str());
+/// Attempts to clone `source`, a local git repository, into `target`, a local
+/// directory that doesn't exist.
+/// Returns `DirToUse(p)` if the clone fails, where `p` is a newly created temporary
+/// directory (that the callee may use, for example, to check out remote sources into).
+/// Returns `CheckedOutSources` if the clone succeeded.
+pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult {
+    use conditions::failed_to_create_temp_dir::cond;
+
+    let scratch_dir = tempfile::mkdtemp(&os::tmpdir(), "rustpkg");
+    let clone_target = match scratch_dir {
+        Some(d) => d.push("rustpkg_temp"),
+        None    => cond.raise(~"Failed to create temporary directory for fetching git sources")
+    };
+
+    if os::path_exists(source) {
+        debug2!("{} exists locally! Cloning it into {}",
+                source.to_str(), target.to_str());
+        // Ok to use target here; we know it will succeed
+        assert!(os::path_is_dir(source));
+        assert!(is_git_dir(source));
+
+        if !os::path_exists(target) {
+            debug2!("Running: git clone {} {}", source.to_str(), target.to_str());
+            let outp = run::process_output("git", [~"clone", source.to_str(), target.to_str()]);
+            if outp.status != 0 {
+                io::println(str::from_utf8_owned(outp.output.clone()));
+                io::println(str::from_utf8_owned(outp.error));
+                return DirToUse(target.clone());
+            }
+                else {
+                match v {
+                    &ExactRevision(ref s) => {
+                        debug2!("`Running: git --work-tree={} --git-dir={} checkout {}",
+                                *s, target.to_str(), target.push(".git").to_str());
+                        let outp = run::process_output("git",
+                            [format!("--work-tree={}", target.to_str()),
+                             format!("--git-dir={}", target.push(".git").to_str()),
+                             ~"checkout", format!("{}", *s)]);
+                        if outp.status != 0 {
+                            io::println(str::from_utf8_owned(outp.output.clone()));
+                            io::println(str::from_utf8_owned(outp.error));
+                            return DirToUse(target.clone());
+                        }
                     }
+                    _ => ()
                 }
-                _ => ()
             }
+        } else {
+            // Check that no version was specified. There's no reason to not handle the
+            // case where a version was requested, but I haven't implemented it.
+            assert!(*v == NoVersion);
+            debug2!("Running: git --work-tree={} --git-dir={} pull --no-edit {}",
+                    target.to_str(), target.push(".git").to_str(), source.to_str());
+            let args = [format!("--work-tree={}", target.to_str()),
+                        format!("--git-dir={}", target.push(".git").to_str()),
+                        ~"pull", ~"--no-edit", source.to_str()];
+            let outp = run::process_output("git", args);
+            assert!(outp.status == 0);
         }
-    }
-    else {
-        // Check that no version was specified. There's no reason to not handle the
-        // case where a version was requested, but I haven't implemented it.
-        assert!(*v == NoVersion);
-        debug2!("Running: git --work-tree={} --git-dir={} pull --no-edit {}",
-               target.to_str(), target.push(".git").to_str(), source.to_str());
-        let args = [format!("--work-tree={}", target.to_str()),
-                    format!("--git-dir={}", target.push(".git").to_str()),
-                    ~"pull", ~"--no-edit", source.to_str()];
-        let outp = run::process_output("git", args);
-        assert!(outp.status == 0);
+        CheckedOutSources
+    } else {
+        DirToUse(clone_target)
     }
 }
 
+pub enum CloneResult {
+    DirToUse(Path), // Created this empty directory to use as the temp dir for git
+    CheckedOutSources // Successfully checked sources out into the given target dir
+}
+
+pub fn make_read_only(target: &Path) {
+    // Now, make all the files in the target dir read-only
+    do os::walk_dir(target) |p| {
+        if !os::path_is_dir(p) {
+            assert!(chmod_read_only(p));
+        };
+        true
+    };
+}
+
 /// Source can be either a URL or a local file path.
-/// true if successful
-pub fn git_clone_general(source: &str, target: &Path, v: &Version) -> bool {
+pub fn git_clone_url(source: &str, target: &Path, v: &Version) {
+    use conditions::git_checkout_failed::cond;
+
     let outp = run::process_output("git", [~"clone", source.to_str(), target.to_str()]);
     if outp.status != 0 {
          debug2!("{}", str::from_utf8_owned(outp.output.clone()));
          debug2!("{}", str::from_utf8_owned(outp.error));
-         false
+         cond.raise((source.to_owned(), target.clone()))
     }
     else {
         match v {
@@ -77,15 +114,12 @@ pub fn git_clone_general(source: &str, target: &Path, v: &Version) -> bool {
                     if outp.status != 0 {
                         debug2!("{}", str::from_utf8_owned(outp.output.clone()));
                         debug2!("{}", str::from_utf8_owned(outp.error));
-                        false
-                    }
-                    else {
-                        true
+                        cond.raise((source.to_owned(), target.clone()))
                     }
-                }
-                _ => true
             }
+            _ => ()
         }
+    }
 }
 
 fn process_output_in_cwd(prog: &str, args: &[~str], cwd: &Path) -> ProcessOutput {
index 6ef0b384a64397ca1e19294e8b947039a24e13ce..fea81abe0b6b84440b18ba6557ca3bd5cba25413 100644 (file)
@@ -11,7 +11,7 @@
 // rustpkg unit tests
 
 use context::{BuildContext, Context, RustcFlags};
-use std::{io, libc, os, run, str, task};
+use std::{io, os, run, str, task};
 use extra::arc::Arc;
 use extra::arc::RWArc;
 use extra::tempfile::mkdtemp;
                target_bench_in_workspace, make_dir_rwx, U_RWX,
                library_in_workspace, installed_library_in_workspace,
                built_bench_in_workspace, built_test_in_workspace,
-               built_library_in_workspace, built_executable_in_workspace, target_build_dir};
+               built_library_in_workspace, built_executable_in_workspace, target_build_dir,
+               chmod_read_only};
 use rustc::back::link::get_cc_prog;
 use rustc::metadata::filesearch::rust_path;
 use rustc::driver::driver::{build_session, build_session_options, host_triple, optgroups};
 use syntax::diagnostic;
 use target::*;
 use package_source::PkgSrc;
+use source_control::{CheckedOutSources, DirToUse, safe_git_clone};
 use util::datestamp;
 
 fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext {
@@ -83,6 +85,13 @@ fn writeFile(file_path: &Path, contents: &str) {
     out.write_line(contents);
 }
 
+fn mk_emptier_workspace(tag: &str) -> Path {
+    let workspace = mkdtemp(&os::tmpdir(), tag).expect("couldn't create temp dir");
+    let package_dir = workspace.push("src");
+    assert!(os::mkdir_recursive(&package_dir, U_RWX));
+    workspace
+}
+
 fn mk_empty_workspace(short_name: &Path, version: &Version, tag: &str) -> Path {
     let workspace_dir = mkdtemp(&os::tmpdir(), tag).expect("couldn't create temp dir");
     mk_workspace(&workspace_dir, short_name, version);
@@ -192,6 +201,18 @@ fn is_rwx(p: &Path) -> bool {
     }
 }
 
+fn is_read_only(p: &Path) -> bool {
+    use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
+
+    match p.get_mode() {
+        None => return false,
+        Some(m) =>
+            ((m & S_IRUSR as uint) == S_IRUSR as uint
+            && (m & S_IWUSR as uint) == 0 as uint
+            && (m & S_IXUSR as uint) == 0 as uint)
+    }
+}
+
 fn test_sysroot() -> Path {
     // Totally gross hack but it's just for test cases.
     // Infer the sysroot from the exe name and pray that it's right.
@@ -499,8 +520,8 @@ fn frob_source_file(workspace: &Path, pkgid: &PkgId, filename: &str) {
                 Ok(w)  => w.write_line("/* hi */")
             }
         }
-        None => fail2!(format!("frob_source_file failed to find a source file in {}",
-                           pkg_src_dir.to_str()))
+        None => fail2!("frob_source_file failed to find a source file in {}",
+                           pkg_src_dir.to_str())
     }
 }
 
@@ -528,7 +549,10 @@ fn test_install_valid() {
     let ctxt = fake_ctxt(sysroot, &temp_workspace);
     debug2!("temp_workspace = {}", temp_workspace.to_str());
     // should have test, bench, lib, and main
-    let src = PkgSrc::new(temp_workspace.clone(), false, temp_pkg_id.clone());
+    let src = PkgSrc::new(temp_workspace.clone(),
+                          temp_workspace.clone(),
+                          false,
+                          temp_pkg_id.clone());
     ctxt.install(src, &Everything);
     // Check that all files exist
     let exec = target_executable_in_workspace(&temp_pkg_id, &temp_workspace);
@@ -557,7 +581,10 @@ 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());
+        let pkg_src = PkgSrc::new(temp_workspace.clone(),
+                                  temp_workspace.clone(),
+                                  false,
+                                  pkgid.clone());
         ctxt.install(pkg_src, &Everything);
     };
     // Not the best test -- doesn't test that we failed in the right way.
@@ -568,8 +595,6 @@ fn test_install_invalid() {
 // Tests above should (maybe) be converted to shell out to rustpkg, too
 #[test]
 fn test_install_git() {
-    let sysroot = test_sysroot();
-    debug2!("sysroot = {}", sysroot.to_str());
     let temp_pkg_id = git_repo_pkg();
     let repo = init_git_repo(&temp_pkg_id.path);
     debug2!("repo = {}", repo.to_str());
@@ -724,12 +749,10 @@ fn test_package_request_version() {
     assert!(target_executable_in_workspace(&temp_pkg_id, &repo.push(".rust"))
             == repo.push_many([~".rust", ~"bin", ~"test_pkg_version"]));
 
-    let dir = &repo.push_many([~".rust",
-                               ~"src",
-                               ~"mockgithub.com",
-                               ~"catamorphism",
-                               ~"test_pkg_version-0.3"]);
-
+    let dir = target_build_dir(&repo.push(".rust"))
+        .push_rel(&Path("src/mockgithub.com/catamorphism/test_pkg_version-0.3"));
+    debug2!("dir = {}", dir.to_str());
+    assert!(os::path_is_dir(&dir));
     assert!(os::path_exists(&dir.push("version-0.3-file.txt")));
     assert!(!os::path_exists(&dir.push("version-0.4-file.txt")));
 }
@@ -988,8 +1011,7 @@ fn no_rebuilding_dep() {
         Fail(_) => fail2!("no_rebuilding_dep failed for some other reason")
     }
 
-    let bar_date_2 = datestamp(&lib_output_file_name(&workspace,
-                                                   "bar"));
+    let bar_date_2 = datestamp(&bar_lib);
     assert_eq!(bar_date_1, bar_date_2);
 }
 
@@ -1713,13 +1735,11 @@ fn test_target_specific_install_dir() {
 }
 
 #[test]
+#[ignore(reason = "See #7240")]
 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"),
@@ -1860,6 +1880,107 @@ fn test_no_rebuilding() {
     }
 }
 
+#[test]
+fn test_installed_read_only() {
+    // Install sources from a "remote" (actually a local github repo)
+    // Check that afterward, sources are read-only and installed under build/
+    let temp_pkg_id = git_repo_pkg();
+    let repo = init_git_repo(&temp_pkg_id.path);
+    debug2!("repo = {}", repo.to_str());
+    let repo_subdir = repo.push_many([~"mockgithub.com", ~"catamorphism", ~"test-pkg"]);
+    debug2!("repo_subdir = {}", repo_subdir.to_str());
+
+    writeFile(&repo_subdir.push("main.rs"),
+              "fn main() { let _x = (); }");
+    writeFile(&repo_subdir.push("lib.rs"),
+              "pub fn f() { let _x = (); }");
+    add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files
+
+    command_line_test([~"install", temp_pkg_id.path.to_str()], &repo);
+
+    let ws = repo.push(".rust");
+    // Check that all files exist
+    debug2!("Checking for files in {}", ws.to_str());
+    let exec = target_executable_in_workspace(&temp_pkg_id, &ws);
+    debug2!("exec = {}", exec.to_str());
+    assert!(os::path_exists(&exec));
+    assert!(is_rwx(&exec));
+    let built_lib =
+        built_library_in_workspace(&temp_pkg_id,
+                                   &ws).expect("test_install_git: built lib should exist");
+    assert!(os::path_exists(&built_lib));
+    assert!(is_rwx(&built_lib));
+
+    // Make sure sources are (a) under "build" and (b) read-only
+    let src1 = target_build_dir(&ws).push_many([~"src", temp_pkg_id.to_str(), ~"main.rs"]);
+    let src2 = target_build_dir(&ws).push_many([~"src", temp_pkg_id.to_str(), ~"lib.rs"]);
+    assert!(os::path_exists(&src1));
+    assert!(os::path_exists(&src2));
+    assert!(is_read_only(&src1));
+    assert!(is_read_only(&src2));
+}
+
+#[test]
+fn test_installed_local_changes() {
+    let temp_pkg_id = git_repo_pkg();
+    let repo = init_git_repo(&temp_pkg_id.path);
+    debug2!("repo = {}", repo.to_str());
+    let repo_subdir = repo.push_many([~"mockgithub.com", ~"catamorphism", ~"test-pkg"]);
+    debug2!("repo_subdir = {}", repo_subdir.to_str());
+    assert!(os::mkdir_recursive(&repo.push_many([".rust", "src"]), U_RWX));
+
+    writeFile(&repo_subdir.push("main.rs"),
+              "fn main() { let _x = (); }");
+    writeFile(&repo_subdir.push("lib.rs"),
+              "pub fn f() { let _x = (); }");
+    add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files
+
+    command_line_test([~"install", temp_pkg_id.path.to_str()], &repo);
+
+
+    // We installed the dependency.
+    // Now start a new workspace and clone it into it
+    let hacking_workspace = mk_emptier_workspace("hacking_workspace");
+    let target_dir = hacking_workspace.push_many([~"src",
+                                                  ~"mockgithub.com",
+                                                  ~"catamorphism",
+                                                  ~"test-pkg-0.1"]);
+    debug2!("---- git clone {} {}", repo_subdir.to_str(), target_dir.to_str());
+
+    let c_res = safe_git_clone(&repo_subdir, &NoVersion, &target_dir);
+
+    match c_res {
+        DirToUse(_) => fail2!("test_installed_local_changes failed"),
+        CheckedOutSources => ()
+    };
+
+    // Make a local change to it
+    writeFile(&target_dir.push("lib.rs"),
+              "pub fn g() { let _x = (); }");
+
+    // Finally, make *another* package that uses it
+    let importer_pkg_id = fake_pkg();
+    let main_subdir = create_local_package_in(&importer_pkg_id, &hacking_workspace);
+    writeFile(&main_subdir.push("main.rs"),
+              "extern mod test = \"mockgithub.com/catamorphism/test-pkg\"; \
+              use test::g;
+              fn main() { g(); }");
+    // And make sure we can build it
+
+    command_line_test([~"build", importer_pkg_id.path.to_str()], &hacking_workspace);
+}
+
+#[test]
+fn test_7402() {
+    let dir = create_local_package(&PkgId::new("foo"));
+    let dest_workspace = mkdtemp(&os::tmpdir(), "more_rust").expect("test_7402");
+    let rust_path = Some(~[(~"RUST_PATH",
+                            format!("{}:{}", dest_workspace.to_str(), dir.to_str()))]);
+    let cwd = os::getcwd();
+    command_line_test_with_env([~"install", ~"foo"], &cwd, rust_path);
+    assert_executable_exists(&dest_workspace, "foo");
+}
+
 /// Returns true if p exists and is executable
 fn is_executable(p: &Path) -> bool {
     use std::libc::consts::os::posix88::{S_IXUSR};
@@ -1869,25 +1990,3 @@ 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 8bd6b04864ba7f4de874cc6517cc6579cb919da4..d66dd847405cf9d7c3051c6f4b1ad351da7ed14a 100644 (file)
@@ -27,8 +27,8 @@
 use package_id::PkgId;
 use package_source::PkgSrc;
 use workspace::pkg_parent_workspaces;
-use path_util::{installed_library_in_workspace, U_RWX, system_library, target_build_dir};
-use path_util::default_workspace;
+use path_util::{U_RWX, system_library, target_build_dir};
+use path_util::{default_workspace, built_library_in_workspace};
 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};
 
@@ -298,7 +298,7 @@ pub fn compile_input(context: &BuildContext,
                                           crate);
     // Discover the output
     let discovered_output = if what == Lib  {
-        installed_library_in_workspace(&pkg_id.path, workspace)
+        built_library_in_workspace(pkg_id, workspace) // Huh???
     }
     else {
         result
@@ -306,6 +306,7 @@ pub fn compile_input(context: &BuildContext,
     debug2!("About to discover output {}", discovered_output.to_str());
     for p in discovered_output.iter() {
         if os::path_exists(p) {
+            debug2!("4. discovering output {}", p.to_str());
             exec.discover_output("binary", p.to_str(), digest_only_date(p));
         }
         // Nothing to do if it doesn't exist -- that could happen if we had the
@@ -443,7 +444,13 @@ fn visit_view_item(&mut self, vi: &ast::view_item, env: ()) {
                         let dest_workspace = if workspaces.is_empty() {
                             default_workspace()
                         } else { workspaces[0] };
-                        let pkg_src = PkgSrc::new(dest_workspace,
+                        // In this case, the source and destination workspaces are the same:
+                        // Either it's a remote package, so the local sources don't exist
+                        // and the `PkgSrc` constructor will detect that;
+                        // or else it's already in a workspace and we'll build into that
+                        // workspace
+                        let pkg_src = PkgSrc::new(dest_workspace.clone(),
+                                                  dest_workspace,
                         // Use the rust_path_hack to search for dependencies iff
                         // we were already using it
                                                   self.context.context.use_rust_path_hack,
@@ -453,14 +460,18 @@ fn visit_view_item(&mut self, vi: &ast::view_item, env: ()) {
                         debug2!("Installed {}, returned {:?} dependencies and \
                                {:?} transitive dependencies",
                                lib_name, outputs_disc.len(), inputs_disc.len());
+                        debug2!("discovered outputs = {:?} discovered_inputs = {:?}",
+                               outputs_disc, inputs_disc);
                         // It must have installed *something*...
                         assert!(!outputs_disc.is_empty());
-                        let target_workspace = outputs_disc[0].pop();
                         for dep in outputs_disc.iter() {
                             debug2!("Discovering a binary input: {}", dep.to_str());
                             self.exec.discover_input("binary",
                                                      dep.to_str(),
                                                      digest_only_date(dep));
+                            // Also, add an additional search path
+                            debug2!("Installed {} into {}", dep.to_str(), dep.pop().to_str());
+                            (self.save)(dep.pop());
                         }
                         for &(ref what, ref dep) in inputs_disc.iter() {
                             if *what == ~"file" {
@@ -477,9 +488,6 @@ fn visit_view_item(&mut self, vi: &ast::view_item, env: ()) {
                                 fail2!("Bad kind: {}", *what);
                             }
                         }
-                        // Also, add an additional search path
-                        debug2!("Installed {} into {}", lib_name, target_workspace.to_str());
-                        (self.save)(target_workspace);
                     }
                 }
             }
index 9642d004de316ecaa323e82d79a644710fd10721..d5dab15c02ac674c2f2741bbf556627bd0f14fb3 100644 (file)
 use std::{os,util};
 use std::path::Path;
 use context::Context;
-use path_util::{workspace_contains_package_id, find_dir_using_rust_path_hack};
+use path_util::{workspace_contains_package_id, find_dir_using_rust_path_hack, default_workspace};
+use path_util::rust_path;
 use util::option_to_vec;
 use package_id::PkgId;
 
-use path_util::rust_path;
-
 pub fn each_pkg_parent_workspace(cx: &Context, pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool {
     // Using the RUST_PATH, find workspaces that contain
     // this package ID
@@ -75,3 +74,14 @@ pub fn cwd_to_workspace() -> Option<(Path, PkgId)> {
     }
     None
 }
+
+/// If `workspace` is the same as `cwd`, and use_rust_path_hack is false,
+/// return `workspace`; otherwise, return the first workspace in the RUST_PATH.
+pub fn determine_destination(cwd: Path, use_rust_path_hack: bool, workspace: &Path) -> Path {
+    if workspace == &cwd && !use_rust_path_hack {
+        workspace.clone()
+    }
+    else {
+        default_workspace()
+    }
+}