]> git.lizzy.rs Git - rust.git/commitdiff
rustpkg: Allow package directories to appear in the RUST_PATH
authorTim Chevalier <chevalier@alum.wellesley.edu>
Wed, 28 Aug 2013 21:52:37 +0000 (14:52 -0700)
committerTim Chevalier <chevalier@alum.wellesley.edu>
Fri, 30 Aug 2013 22:48:41 +0000 (15:48 -0700)
This commit adds a rustpkg flag, --rust-path-hack, that allows
rustpkg to *search* inside package directories if they appear in
the RUST_PATH, while *building* libraries and executables into a
different target directory.

This behavior is hidden behind a flag because I believe we only
want to support it temporarily, to make it easier to port servo to
rustpkg.

This commit also includes a fix for how rustpkg fetches sources
from git repositories -- it uses a temporary directory as the target
when invoking `git clone`, then moves that directory into the workspace
if the clone was successful. (The old behavior was that when the
`git clone` failed, the empty target directory would be left lying
around anyway.)

src/librustpkg/api.rs
src/librustpkg/context.rs
src/librustpkg/package_source.rs
src/librustpkg/path_util.rs
src/librustpkg/rustpkg.rs
src/librustpkg/tests.rs
src/librustpkg/util.rs
src/librustpkg/workspace.rs

index 2da66baa412b57914b7d27e535ffac99d36505dd..dfe80674b7fa114bcbb457851c652a0c4d1f0b6f 100644 (file)
 /// Convenience functions intended for calling from pkg.rs
 
 fn default_ctxt(p: @Path) -> Ctx {
-    Ctx { sysroot_opt: Some(p), json: false, dep_cache: @mut HashMap::new() }
+    Ctx {
+        use_rust_path_hack: false,
+        sysroot_opt: Some(p),
+        json: false,
+        dep_cache: @mut HashMap::new()
+    }
 }
 
 pub fn build_lib(sysroot: @Path, root: Path, name: ~str, version: Version,
index 93e0789dcb0c9548d52822e0f8b4e7b07d2cd39d..4087fdd7ca5048820a19a567e89416985a349002 100644 (file)
 use std::os;
 
 pub struct Ctx {
+    // If use_rust_path_hack is true, rustpkg searches for sources
+    // in *package* directories that are in the RUST_PATH (for example,
+    // FOO/src/bar-0.1 instead of FOO). The flag doesn't affect where
+    // rustpkg stores build artifacts.
+    use_rust_path_hack: bool,
     // Sysroot -- if this is None, uses rustc filesearch's
     // idea of the default
     sysroot_opt: Option<@Path>,
index 42f33592aaaead2a5b7cb1f2174943969d398d4f..ae2083f1b22c12a3f3abea19e775e6995fd34e23 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+extern mod extra;
+
 use target::*;
 use package_id::PkgId;
 use std::path::Path;
@@ -16,8 +18,9 @@
 use crate::Crate;
 use messages::*;
 use source_control::{git_clone, git_clone_general};
-use path_util::pkgid_src_in_workspace;
+use path_util::{pkgid_src_in_workspace, find_dir_using_rust_path_hack, default_workspace};
 use util::compile_crate;
+use workspace::is_workspace;
 
 // An enumeration of the unpacked source of a package workspace.
 // This contains a list of files found in the source workspace.
@@ -48,7 +51,7 @@ pub fn new(src_dir: &Path, id: &PkgId) -> PkgSrc {
     }
 
 
-    fn check_dir(&self) -> Path {
+    fn check_dir(&self, cx: &Ctx) -> Path {
         use conditions::nonexistent_package::cond;
 
         debug!("Pushing onto root: %s | %s", self.id.path.to_str(), self.root.to_str());
@@ -59,12 +62,21 @@ fn check_dir(&self) -> Path {
 
         let dir = match path {
             Some(d) => (*d).clone(),
-            None => match self.fetch_git() {
-                Some(d) => d,
-                None => cond.raise((self.id.clone(), ~"supplied path for package dir does not \
-                                      exist, and couldn't interpret it as a URL fragment"))
+            None => {
+                match self.fetch_git() {
+                    Some(d) => d,
+                    None => {
+                        match find_dir_using_rust_path_hack(cx, &self.id) {
+                            Some(d) => d,
+                            None => cond.raise((self.id.clone(),
+                               ~"supplied path for package dir does not \
+                                 exist, and couldn't interpret it as a URL fragment"))
+                        }
+                    }
+                }
             }
         };
+        debug!("For package id %s, returning %s", self.id.to_str(), dir.to_str());
         if !os::path_is_dir(&dir) {
             cond.raise((self.id.clone(), ~"supplied path for package dir is a \
                                         non-directory"));
@@ -79,11 +91,19 @@ fn check_dir(&self) -> Path {
     /// refers to a git repo on the local version, also check it out.
     /// (right now we only support git)
     pub fn fetch_git(&self) -> Option<Path> {
+        use conditions::failed_to_create_temp_dir::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")
+        };
 
         let mut local = self.root.push("src");
         local = local.push(self.id.to_str());
-        // Git can't clone into a non-empty directory
-        os::remove_dir_recursive(&local);
 
         debug!("Checking whether %s exists locally. Cwd = %s, does it? %?",
                self.id.path.to_str(),
@@ -93,15 +113,28 @@ pub fn fetch_git(&self) -> Option<Path> {
         if os::path_exists(&self.id.path) {
             debug!("%s exists locally! Cloning it into %s",
                    self.id.path.to_str(), local.to_str());
+            // Ok to use local here; we know it will succeed
             git_clone(&self.id.path, &local, &self.id.version);
             return Some(local);
         }
 
+        if (self.id.path.clone()).components().len() < 2 {
+            // If a non-URL, don't bother trying to fetch
+            return None;
+        }
+
         let url = fmt!("https://%s", self.id.path.to_str());
         note(fmt!("Fetching package: git clone %s %s [version=%s]",
-                  url, local.to_str(), self.id.version.to_str()));
-        if git_clone_general(url, &local, &self.id.version) {
-            Some(local)
+                  url, clone_target.to_str(), self.id.version.to_str()));
+
+        if git_clone_general(url, &clone_target, &self.id.version) {
+            // since the operation succeeded, move clone_target to local
+            if !os::rename_file(&clone_target, &local) {
+                 None
+            }
+            else {
+                 Some(local)
+            }
         }
         else {
             None
@@ -138,10 +171,10 @@ fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) {
 
     /// Infers crates to build. Called only in the case where there
     /// is no custom build logic
-    pub fn find_crates(&mut self) {
+    pub fn find_crates(&mut self, cx: &Ctx) {
         use conditions::missing_pkg_files::cond;
 
-        let dir = self.check_dir();
+        let dir = self.check_dir(cx);
         debug!("Called check_dir, I'm in %s", dir.to_str());
         let prefix = dir.components.len();
         debug!("Matching against %?", self.id.short_name);
@@ -183,6 +216,7 @@ pub fn find_crates(&mut self) {
     fn build_crates(&self,
                     ctx: &Ctx,
                     src_dir: &Path,
+                    destination_dir: &Path,
                     crates: &[Crate],
                     cfgs: &[~str],
                     what: OutputType) {
@@ -194,8 +228,8 @@ fn build_crates(&self,
             let result = compile_crate(ctx,
                                        &self.id,
                                        path,
-                                       // compile_crate wants the workspace
-                                       &self.root,
+                                       // compile_crate wants the destination workspace
+                                       destination_dir,
                                        crate.flags,
                                        crate.cfgs + cfgs,
                                        false,
@@ -209,15 +243,39 @@ fn build_crates(&self,
         }
     }
 
-    pub fn build(&self, ctx: &Ctx, cfgs: ~[~str]) {
-        let dir = self.check_dir();
-        debug!("Building libs in %s", dir.to_str());
-        self.build_crates(ctx, &dir, self.libs, cfgs, Lib);
+    pub fn build(&self, ctx: &Ctx, cfgs: ~[~str]) -> Path {
+        use conditions::not_a_workspace::cond;
+
+        // Determine the destination workspace (which depends on whether
+        // we're using the rust_path_hack)
+        let destination_workspace = if is_workspace(&self.root) {
+            debug!("%s is indeed a workspace", self.root.to_str());
+            self.root.clone()
+        }
+        else {
+            // It would be nice to have only one place in the code that checks
+            // for the use_rust_path_hack flag...
+            if ctx.use_rust_path_hack {
+                let rs = default_workspace();
+                debug!("Using hack: %s", rs.to_str());
+                rs
+            }
+            else {
+                cond.raise(fmt!("Package root %s is not a workspace; pass in --rust_path_hack \
+                                if you want to treat it as a package source", self.root.to_str()))
+            }
+        };
+
+        let dir = self.check_dir(ctx);
+        debug!("Building libs in %s, destination = %s", dir.to_str(),
+            destination_workspace.to_str());
+        self.build_crates(ctx, &dir, &destination_workspace, self.libs, cfgs, Lib);
         debug!("Building mains");
-        self.build_crates(ctx, &dir, self.mains, cfgs, Main);
+        self.build_crates(ctx, &dir, &destination_workspace, self.mains, cfgs, Main);
         debug!("Building tests");
-        self.build_crates(ctx, &dir, self.tests, cfgs, Test);
+        self.build_crates(ctx, &dir, &destination_workspace, self.tests, cfgs, Test);
         debug!("Building benches");
-        self.build_crates(ctx, &dir, self.benchs, cfgs, Bench);
+        self.build_crates(ctx, &dir, &destination_workspace, self.benchs, cfgs, Bench);
+        destination_workspace
     }
 }
index 467477ca479de6fe97e2aac3f2424a31c62f4ee7..af70a79f93d2be91ffdba46e606c0373175b16c4 100644 (file)
@@ -14,6 +14,7 @@
 pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install};
 pub use version::{Version, NoVersion, split_version_general, try_parsing_version};
 pub use rustc::metadata::filesearch::rust_path;
+use context::Ctx;
 
 use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
 use std::os::mkdir_recursive;
@@ -51,18 +52,23 @@ pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, U_RWX) }
 pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
     debug!("Checking in src dir of %s for %s",
            workspace.to_str(), pkgid.to_str());
+    workspace_contains_package_id_(pkgid, workspace, |p| { p.push("src") }).is_some()
+}
 
-    let src_dir = workspace.push("src");
+pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path,
+// Returns the directory it was actually found in
+             workspace_to_src_dir: &fn(&Path) -> Path) -> Option<Path> {
+    let src_dir = workspace_to_src_dir(workspace);
 
-    let mut found = false;
+    let mut found = None;
     do os::walk_dir(&src_dir) |p| {
         debug!("=> p = %s", p.to_str());
 
-        let was_found = os::path_is_dir(p) && {
+        if os::path_is_dir(p) {
             debug!("p = %s, path = %s [%s]", p.to_str(), pkgid.path.to_str(),
                    src_dir.push_rel(&pkgid.path).to_str());
 
-            *p == src_dir.push_rel(&pkgid.path) || {
+            if *p == src_dir.push_rel(&pkgid.path) || {
                 let pf = p.filename();
                 do pf.iter().any |pf| {
                     let g = pf.to_str();
@@ -76,16 +82,15 @@ pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
                         }
                     }
                 }
+            } {
+                found = Some(p.clone());
             }
-        };
 
-        if was_found {
-            found = true
-        }
+        };
         true
     };
 
-    debug!(if found { fmt!("Found %s in %s", pkgid.to_str(), workspace.to_str()) }
+    debug!(if found.is_some() { fmt!("Found %s in %s", pkgid.to_str(), workspace.to_str()) }
            else     { fmt!("Didn't find %s in %s", pkgid.to_str(), workspace.to_str()) });
     found
 }
@@ -123,8 +128,7 @@ pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<
         Some(result)
     }
     else {
-        // This is not an error, but it's worth logging it
-        error!(fmt!("built_executable_in_workspace: %s does not exist", result.to_str()));
+        debug!("built_executable_in_workspace: %s does not exist", result.to_str());
         None
     }
 }
@@ -164,7 +168,7 @@ pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Pat
 
 /// Does the actual searching stuff
 pub fn installed_library_in_workspace(short_name: &str, workspace: &Path) -> Option<Path> {
-    // NOTE: this could break once we're handling multiple versions better... want a test for it
+    // This could break once we're handling multiple versions better -- I should add a test for it
     library_in_workspace(&Path(short_name), short_name, Install, workspace, "lib", &NoVersion)
 }
 
@@ -246,8 +250,8 @@ pub fn library_in_workspace(path: &Path, short_name: &str, where: Target,
     } // for
 
     if result_filename.is_none() {
-        warn(fmt!("library_in_workspace didn't find a library in %s for %s",
-                  dir_to_search.to_str(), short_name));
+        debug!("warning: library_in_workspace didn't find a library in %s for %s",
+                  dir_to_search.to_str(), short_name);
     }
 
     // Return the filename that matches, which we now know exists
@@ -392,3 +396,25 @@ pub fn uninstall_package_from(workspace: &Path, pkgid: &PkgId) {
     }
 
 }
+
+fn dir_has_file(dir: &Path, file: &str) -> bool {
+    assert!(dir.is_absolute());
+    os::path_exists(&dir.push(file))
+}
+
+pub fn find_dir_using_rust_path_hack(cx: &Ctx, p: &PkgId) -> Option<Path> {
+    if !cx.use_rust_path_hack {
+        return None;
+    }
+    let rp = rust_path();
+    for dir in rp.iter() {
+        debug!("In find_dir_using_rust_path_hack: checking dir %s", dir.to_str());
+        if dir_has_file(dir, "lib.rs") || dir_has_file(dir, "main.rs")
+            || dir_has_file(dir, "test.rs") || dir_has_file(dir, "bench.rs") {
+            debug!("Did find id %s in dir %s", p.to_str(), dir.to_str());
+            return Some(dir.clone());
+        }
+        debug!("Didn't find id %s in dir %s", p.to_str(), dir.to_str())
+    }
+    None
+}
index 79836bcc5554bf2819b7242f89dc6e761065f849..6a6e7569a6518577a4206a8fdde11e23dcccbddd 100644 (file)
 extern mod rustc;
 extern mod syntax;
 
-use std::result;
-use std::io;
-use std::os;
-use std::run;
-use std::str;
-
+use std::{io, os, result, run, str};
 pub use std::path::Path;
 use std::hashmap::HashMap;
 
@@ -173,7 +168,8 @@ fn hash(&self) -> ~str {
 pub trait CtxMethods {
     fn run(&self, cmd: &str, args: ~[~str]);
     fn do_cmd(&self, _cmd: &str, _pkgname: &str);
-    fn build(&self, workspace: &Path, pkgid: &PkgId);
+    /// Returns the destination workspace
+    fn build(&self, workspace: &Path, pkgid: &PkgId) -> Path;
     fn clean(&self, workspace: &Path, id: &PkgId);
     fn info(&self);
     fn install(&self, workspace: &Path, id: &PkgId);
@@ -191,15 +187,19 @@ fn run(&self, cmd: &str, args: ~[~str]) {
             "build" => {
                 if args.len() < 1 {
                     match cwd_to_workspace() {
-                        None => { usage::build(); return }
-                        Some((ws, pkgid)) => self.build(&ws, &pkgid)
+                        None if self.use_rust_path_hack => {
+                            let cwd = os::getcwd();
+                            self.build(&cwd, &PkgId::new(cwd.components[cwd.components.len() - 1]));
+                        }
+                        None => { usage::build(); return; }
+                        Some((ws, pkgid)) => { self.build(&ws, &pkgid); }
                     }
                 }
                 else {
                     // The package id is presumed to be the first command-line
                     // argument
                     let pkgid = PkgId::new(args[0].clone());
-                    do each_pkg_parent_workspace(&pkgid) |workspace| {
+                    do each_pkg_parent_workspace(self, &pkgid) |workspace| {
                         debug!("found pkg %s in workspace %s, trying to build",
                                pkgid.to_str(), workspace.to_str());
                         self.build(workspace, &pkgid);
@@ -238,15 +238,20 @@ fn run(&self, cmd: &str, args: ~[~str]) {
             "install" => {
                 if args.len() < 1 {
                     match cwd_to_workspace() {
-                        None => { usage::install(); return }
-                        Some((ws, pkgid)) => self.install(&ws, &pkgid)
-                    }
+                        None if self.use_rust_path_hack => {
+                            let cwd = os::getcwd();
+                            self.install(&cwd,
+                                &PkgId::new(cwd.components[cwd.components.len() - 1]));
+                        }
+                        None  => { usage::install(); return; }
+                        Some((ws, pkgid))                => self.install(&ws, &pkgid),
+                     }
                 }
                 else {
                     // The package id is presumed to be the first command-line
                     // argument
                     let pkgid = PkgId::new(args[0]);
-                    let workspaces = pkg_parent_workspaces(&pkgid);
+                    let workspaces = pkg_parent_workspaces(self, &pkgid);
                     debug!("package ID = %s, found it in %? workspaces",
                            pkgid.to_str(), workspaces.len());
                     if workspaces.is_empty() {
@@ -257,7 +262,7 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                         self.install(&rp[0], &pkgid);
                     }
                     else {
-                        do each_pkg_parent_workspace(&pkgid) |workspace| {
+                        do each_pkg_parent_workspace(self, &pkgid) |workspace| {
                             self.install(workspace, &pkgid);
                             true
                         };
@@ -294,7 +299,7 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                 else {
                     let rp = rust_path();
                     assert!(!rp.is_empty());
-                    do each_pkg_parent_workspace(&pkgid) |workspace| {
+                    do each_pkg_parent_workspace(self, &pkgid) |workspace| {
                         path_util::uninstall_package_from(workspace, &pkgid);
                         note(fmt!("Uninstalled package %s (was installed in %s)",
                                   pkgid.to_str(), workspace.to_str()));
@@ -318,7 +323,9 @@ fn do_cmd(&self, _cmd: &str, _pkgname: &str)  {
         fail!("`do` not yet implemented");
     }
 
-    fn build(&self, workspace: &Path, pkgid: &PkgId) {
+    /// Returns the destination workspace
+    /// In the case of a custom build, we don't know, so we just return the source workspace
+    fn build(&self, workspace: &Path, pkgid: &PkgId) -> Path {
         debug!("build: workspace = %s (in Rust path? %? is git dir? %? \
                 pkgid = %s", workspace.to_str(),
                in_rust_path(workspace), is_git_dir(&workspace.push_rel(&pkgid.path)),
@@ -374,9 +381,13 @@ fn build(&self, workspace: &Path, pkgid: &PkgId) {
         // the build already. Otherwise...
         if !custom {
             // Find crates inside the workspace
-            src.find_crates();
+            src.find_crates(self);
             // Build it!
-            src.build(self, cfgs);
+            src.build(self, cfgs)
+        }
+        else {
+            // Just return the source workspace
+            workspace.clone()
         }
     }
 
@@ -402,12 +413,15 @@ fn info(&self) {
     }
 
     fn install(&self, workspace: &Path, id: &PkgId)  {
-        // FIXME #7402: Use RUST_PATH to determine target dir
         // Also should use workcache to not build if not necessary.
-        self.build(workspace, id);
-        debug!("install: workspace = %s, id = %s", workspace.to_str(),
-               id.to_str());
-        self.install_no_build(workspace, id);
+        let destination_workspace = self.build(workspace, id);
+        // See #7402: This still isn't quite right yet; we want to
+        // install to the first workspace in the RUST_PATH if there's
+        // a non-default RUST_PATH. This code installs to the same
+        // workspace the package was built in.
+        debug!("install: destination workspace = %s, id = %s",
+               destination_workspace.to_str(), id.to_str());
+        self.install_no_build(&destination_workspace, id);
 
     }
 
@@ -473,7 +487,8 @@ pub fn main_args(args: &[~str]) {
     let opts = ~[getopts::optflag("h"), getopts::optflag("help"),
                  getopts::optflag("j"), getopts::optflag("json"),
                  getopts::optmulti("c"), getopts::optmulti("cfg"),
-                 getopts::optflag("v"), getopts::optflag("version")];
+                 getopts::optflag("v"), getopts::optflag("version"),
+                 getopts::optflag("r"), getopts::optflag("rust-path-hack")];
     let matches = &match getopts::getopts(args, opts) {
         result::Ok(m) => m,
         result::Err(f) => {
@@ -493,6 +508,9 @@ pub fn main_args(args: &[~str]) {
         return;
     }
 
+    let use_rust_path_hack = getopts::opt_present(matches, "r") ||
+                             getopts::opt_present(matches, "rust-path-hack");
+
     let mut args = matches.free.clone();
 
     args.shift();
@@ -501,33 +519,48 @@ pub fn main_args(args: &[~str]) {
         return usage::general();
     }
 
-    let cmd = args.shift();
-
-    if !util::is_cmd(cmd) {
-        return usage::general();
-    } else if help {
-        return match cmd {
-            ~"build" => usage::build(),
-            ~"clean" => usage::clean(),
-            ~"do" => usage::do_cmd(),
-            ~"info" => usage::info(),
-            ~"install" => usage::install(),
-            ~"list"    => usage::list(),
-            ~"prefer" => usage::prefer(),
-            ~"test" => usage::test(),
-            ~"uninstall" => usage::uninstall(),
-            ~"unprefer" => usage::unprefer(),
-            _ => usage::general()
-        };
+    let mut cmd_opt = None;
+    for a in args.iter() {
+        if util::is_cmd(*a) {
+            cmd_opt = Some(a);
+            break;
+        }
     }
+    let cmd = match cmd_opt {
+        None => return usage::general(),
+        Some(cmd) => if help {
+            return match *cmd {
+                ~"build" => usage::build(),
+                ~"clean" => usage::clean(),
+                ~"do" => usage::do_cmd(),
+                ~"info" => usage::info(),
+                ~"install" => usage::install(),
+                ~"list"    => usage::list(),
+                ~"prefer" => usage::prefer(),
+                ~"test" => usage::test(),
+                ~"uninstall" => usage::uninstall(),
+                ~"unprefer" => usage::unprefer(),
+                _ => usage::general()
+            };
+        }
+        else {
+            cmd
+        }
+    };
 
+    // Pop off all flags, plus the command
+    let remaining_args = args.iter().skip_while(|s| !util::is_cmd(**s));
+    // I had to add this type annotation to get the code to typecheck
+    let mut remaining_args: ~[~str] = remaining_args.map(|s| (*s).clone()).collect();
+    remaining_args.shift();
     let sroot = Some(@filesearch::get_or_default_sysroot());
     debug!("Using sysroot: %?", sroot);
     Ctx {
+        use_rust_path_hack: use_rust_path_hack,
         sysroot_opt: sroot, // Currently, only tests override this
         json: json,
         dep_cache: @mut HashMap::new()
-    }.run(cmd, args);
+    }.run(*cmd, remaining_args)
 }
 
 /**
index 98999da41c81633bad276bce6142f104889d20fe..4f8e80f56d5d7a1f89595f093d5c9e4b3d4ea6e1 100644 (file)
@@ -34,6 +34,7 @@ fn datestamp(p: &Path) -> Option<libc::time_t> {
 
 fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx {
     Ctx {
+        use_rust_path_hack: false,
         sysroot_opt: sysroot_opt,
         json: false,
         dep_cache: @mut HashMap::new()
@@ -70,8 +71,8 @@ fn writeFile(file_path: &Path, contents: &str) {
     out.write_line(contents);
 }
 
-fn mk_empty_workspace(short_name: &Path, version: &Version) -> Path {
-    let workspace_dir = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir");
+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);
     workspace_dir
 }
@@ -86,7 +87,7 @@ fn mk_workspace(workspace: &Path, short_name: &Path, version: &Version) -> Path
 
 fn mk_temp_workspace(short_name: &Path, version: &Version) -> Path {
     let package_dir = mk_empty_workspace(short_name,
-                                         version).push("src").push(fmt!("%s-%s",
+                          version, "temp_workspace").push("src").push(fmt!("%s-%s",
                                                             short_name.to_str(),
                                                             version.to_str()));
 
@@ -304,29 +305,54 @@ fn create_local_package_with_custom_build_hook(pkgid: &PkgId,
 
 }
 
-fn assert_lib_exists(repo: &Path, short_name: &str, _v: Version) { // ??? version?
+fn assert_lib_exists(repo: &Path, short_name: &str, v: Version) {
+    assert!(lib_exists(repo, short_name, v));
+}
+
+fn lib_exists(repo: &Path, short_name: &str, _v: Version) -> bool { // ??? version?
     debug!("assert_lib_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
     let lib = installed_library_in_workspace(short_name, repo);
     debug!("assert_lib_exists: checking whether %? exists", lib);
-    assert!(lib.is_some());
-    let libname = lib.get_ref();
-    assert!(os::path_exists(libname));
-    assert!(is_rwx(libname));
+    lib.is_some() && {
+        let libname = lib.get_ref();
+        os::path_exists(libname) && is_rwx(libname)
+    }
 }
 
 fn assert_executable_exists(repo: &Path, short_name: &str) {
+    assert!(executable_exists(repo, short_name));
+}
+
+fn executable_exists(repo: &Path, short_name: &str) -> bool {
     debug!("assert_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
     let exec = target_executable_in_workspace(&PkgId::new(short_name), repo);
-    assert!(os::path_exists(&exec));
-    assert!(is_rwx(&exec));
+    os::path_exists(&exec) && is_rwx(&exec)
 }
 
 fn assert_built_executable_exists(repo: &Path, short_name: &str) {
+    assert!(built_executable_exists(repo, short_name));
+}
+
+fn built_executable_exists(repo: &Path, short_name: &str) -> bool {
     debug!("assert_built_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
-    let exec = built_executable_in_workspace(&PkgId::new(short_name),
-                                             repo).expect("assert_built_executable_exists failed");
-    assert!(os::path_exists(&exec));
-    assert!(is_rwx(&exec));
+    let exec = built_executable_in_workspace(&PkgId::new(short_name), repo);
+    exec.is_some() && {
+       let execname = exec.get_ref();
+       os::path_exists(execname) && is_rwx(execname)
+    }
+}
+
+fn assert_built_library_exists(repo: &Path, short_name: &str) {
+    assert!(built_library_exists(repo, short_name));
+}
+
+fn built_library_exists(repo: &Path, short_name: &str) -> bool {
+    debug!("assert_built_library_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
+    let lib = built_library_in_workspace(&PkgId::new(short_name), repo);
+    lib.is_some() && {
+        let libname = lib.get_ref();
+        os::path_exists(libname) && is_rwx(libname)
+    }
 }
 
 fn command_line_test_output(args: &[~str]) -> ~[~str] {
@@ -452,12 +478,14 @@ fn test_install_valid() {
 fn test_install_invalid() {
     use conditions::nonexistent_package::cond;
     use cond1 = conditions::missing_pkg_files::cond;
+    use cond2 = conditions::not_a_workspace::cond;
 
     let ctxt = fake_ctxt(None);
     let pkgid = fake_pkg();
     let temp_workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir");
     let mut error_occurred = false;
     let mut error1_occurred = false;
+    let mut error2_occurred = false;
     do cond1.trap(|_| {
         error1_occurred = true;
     }).inside {
@@ -465,10 +493,15 @@ fn test_install_invalid() {
             error_occurred = true;
             temp_workspace.clone()
         }).inside {
-            ctxt.install(&temp_workspace, &pkgid);
+            do cond2.trap(|_| {
+               error2_occurred = true;
+               temp_workspace.clone()
+            }).inside {
+                 ctxt.install(&temp_workspace, &pkgid);
+            }
         }
     }
-    assert!(error_occurred && error1_occurred);
+    assert!(error_occurred && error1_occurred && error2_occurred);
 }
 
 // Tests above should (maybe) be converted to shell out to rustpkg, too
@@ -1087,6 +1120,152 @@ fn multiple_workspaces() {
     command_line_test_with_env([~"install", ~"bar"], &c_loc, env);
 }
 
+fn rust_path_hack_test(hack_flag: bool) {
+/*
+      Make a workspace containing a pkg foo [A]
+      Make a second, empty workspace        [B]
+      Set RUST_PATH to B:A
+      rustpkg install foo
+      make sure built files for foo are in B
+      make sure nothing gets built into A or A/../build[lib,bin]
+*/
+   let p_id = PkgId::new("foo");
+   let workspace = create_local_package(&p_id);
+   let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
+   let rust_path = Some(~[(~"RUST_PATH",
+       fmt!("%s:%s", dest_workspace.to_str(), workspace.push_many(["src", "foo-0.1"]).to_str()))]);
+   debug!("declare -x RUST_PATH=%s:%s",
+       dest_workspace.to_str(), workspace.push_many(["src", "foo-0.1"]).to_str());
+   command_line_test_with_env(~[~"install"] + if hack_flag { ~[~"--rust-path-hack"] } else { ~[] } +
+                               ~[~"foo"], &dest_workspace, rust_path);
+   assert_lib_exists(&dest_workspace, "foo", NoVersion);
+   assert_executable_exists(&dest_workspace, "foo");
+   assert_built_library_exists(&dest_workspace, "foo");
+   assert_built_executable_exists(&dest_workspace, "foo");
+   assert!(!lib_exists(&workspace, "foo", NoVersion));
+   assert!(!executable_exists(&workspace, "foo"));
+   assert!(!built_library_exists(&workspace, "foo"));
+   assert!(!built_executable_exists(&workspace, "foo"));
+}
+
+#[test]
+fn test_rust_path_can_contain_package_dirs_with_flag() {
+/*
+   Test that the temporary hack added for bootstrapping Servo builds
+   works. That is: if you add $FOO/src/some_pkg to the RUST_PATH,
+   it will find the sources in some_pkg, build them, and install them
+   into the first entry in the RUST_PATH.
+
+   When the hack is removed, we should change this to a should_fail test.
+*/
+   rust_path_hack_test(true);
+}
+
+#[test]
+#[should_fail]
+fn test_rust_path_can_contain_package_dirs_without_flag() {
+   rust_path_hack_test(false);
+}
+
+#[test]
+fn rust_path_hack_cwd() {
+   // Same as rust_path_hack_test, but the CWD is the dir to build out of
+   let cwd = mkdtemp(&os::tmpdir(), "pkg_files").expect("rust_path_hack_cwd");
+   writeFile(&cwd.push("lib.rs"), "pub fn f() { }");
+
+   let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
+   let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]);
+   debug!("declare -x RUST_PATH=%s", dest_workspace.to_str());
+   command_line_test_with_env([~"install", ~"--rust-path-hack", ~"foo"], &cwd, rust_path);
+   debug!("Checking that foo exists in %s", dest_workspace.to_str());
+   assert_lib_exists(&dest_workspace, "foo", NoVersion);
+   assert_built_library_exists(&dest_workspace, "foo");
+   assert!(!lib_exists(&cwd, "foo", NoVersion));
+   assert!(!built_library_exists(&cwd, "foo"));
+}
+
+#[test]
+fn rust_path_hack_multi_path() {
+   // Same as rust_path_hack_test, but with a more complex package ID
+   let cwd = mkdtemp(&os::tmpdir(), "pkg_files").expect("rust_path_hack_cwd");
+   let subdir = cwd.push_many([~"foo", ~"bar", ~"quux"]);
+   assert!(os::mkdir_recursive(&subdir, U_RWX));
+   writeFile(&subdir.push("lib.rs"), "pub fn f() { }");
+   let name = ~"foo/bar/quux";
+
+   let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
+   let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]);
+   debug!("declare -x RUST_PATH=%s", dest_workspace.to_str());
+   command_line_test_with_env([~"install", ~"--rust-path-hack", name.clone()], &subdir, rust_path);
+   debug!("Checking that %s exists in %s", name, dest_workspace.to_str());
+   assert_lib_exists(&dest_workspace, "quux", NoVersion);
+   assert_built_library_exists(&dest_workspace, name);
+   assert!(!lib_exists(&subdir, "quux", NoVersion));
+   assert!(!built_library_exists(&subdir, name));
+}
+
+#[test]
+fn rust_path_hack_install_no_arg() {
+   // Same as rust_path_hack_cwd, but making rustpkg infer the pkg id
+   let cwd = mkdtemp(&os::tmpdir(), "pkg_files").expect("rust_path_hack_install_no_arg");
+   let source_dir = cwd.push("foo");
+   assert!(make_dir_rwx(&source_dir));
+   writeFile(&source_dir.push("lib.rs"), "pub fn f() { }");
+
+   let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
+   let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]);
+   debug!("declare -x RUST_PATH=%s", dest_workspace.to_str());
+   command_line_test_with_env([~"install", ~"--rust-path-hack"], &source_dir, rust_path);
+   debug!("Checking that foo exists in %s", dest_workspace.to_str());
+   assert_lib_exists(&dest_workspace, "foo", NoVersion);
+   assert_built_library_exists(&dest_workspace, "foo");
+   assert!(!lib_exists(&source_dir, "foo", NoVersion));
+   assert!(!built_library_exists(&cwd, "foo"));
+}
+
+#[test]
+fn rust_path_hack_build_no_arg() {
+   // Same as rust_path_hack_install_no_arg, but building instead of installing
+   let cwd = mkdtemp(&os::tmpdir(), "pkg_files").expect("rust_path_hack_build_no_arg");
+   let source_dir = cwd.push("foo");
+   assert!(make_dir_rwx(&source_dir));
+   writeFile(&source_dir.push("lib.rs"), "pub fn f() { }");
+
+   let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
+   let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]);
+   debug!("declare -x RUST_PATH=%s", dest_workspace.to_str());
+   command_line_test_with_env([~"build", ~"--rust-path-hack"], &source_dir, rust_path);
+   debug!("Checking that foo exists in %s", dest_workspace.to_str());
+   assert_built_library_exists(&dest_workspace, "foo");
+   assert!(!built_library_exists(&source_dir, "foo"));
+}
+
+#[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");
+    let dir = mk_workspace(&dir_for_path, &Path("foo"), &NoVersion);
+    debug!("dir = %s", dir.to_str());
+    writeFile(&dir.push("main.rs"), "fn main() { let _x = (); }");
+    let dir_to_install_to = mkdtemp(&os::tmpdir(),
+        "dest_workspace").expect("rust_path_install_target failed");
+    let dir = dir.pop().pop();
+
+    let rust_path = Some(~[(~"RUST_PATH", fmt!("%s:%s", dir_to_install_to.to_str(),
+                                               dir.to_str()))]);
+    let cwd = os::getcwd();
+
+    debug!("RUST_PATH=%s:%s", dir_to_install_to.to_str(), dir.to_str());
+    command_line_test_with_env([~"install", ~"foo"],
+                               &cwd,
+                               rust_path);
+
+    assert_executable_exists(&dir_to_install_to, "foo");
+
+}
+
+
 /// Returns true if p exists and is executable
 fn is_executable(p: &Path) -> bool {
     use std::libc::consts::os::posix88::{S_IXUSR};
index 4bdb442c1e61920d98287fb985f3de1178e75dc8..958786b7cf4e20a4039e0528828f341ecf25ef9b 100644 (file)
@@ -452,6 +452,13 @@ fn test_is_cmd() {
 
 }
 
+pub fn option_to_vec<T>(x: Option<T>) -> ~[T] {
+    match x {
+       Some(y) => ~[y],
+       None    => ~[]
+    }
+}
+
 // tjc: cheesy
 fn debug_flags() -> ~[~str] { ~[] }
 // static DEBUG_FLAGS: ~[~str] = ~[~"-Z", ~"time-passes"];
index 1afe5d513cc14e2496c84e94518200704cb9e66f..5ad2dfd6d2f4987157b9fa8202d0dbcdac36878a 100644 (file)
 
 use std::{os,util};
 use std::path::Path;
-use path_util::workspace_contains_package_id;
+use context::Ctx;
+use path_util::{workspace_contains_package_id, find_dir_using_rust_path_hack};
+use util::option_to_vec;
 use package_id::PkgId;
 
 use path_util::rust_path;
 
-pub fn each_pkg_parent_workspace(pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool {
+pub fn each_pkg_parent_workspace(cx: &Ctx, pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool {
     // Using the RUST_PATH, find workspaces that contain
     // this package ID
-    let workspaces = pkg_parent_workspaces(pkgid);
+    let workspaces = pkg_parent_workspaces(cx, pkgid);
     if workspaces.is_empty() {
         // tjc: make this a condition
         fail!("Package %s not found in any of \
@@ -36,10 +38,20 @@ pub fn each_pkg_parent_workspace(pkgid: &PkgId, action: &fn(&Path) -> bool) -> b
     return true;
 }
 
-pub fn pkg_parent_workspaces(pkgid: &PkgId) -> ~[Path] {
-    rust_path().move_iter()
+pub fn pkg_parent_workspaces(cx: &Ctx, pkgid: &PkgId) -> ~[Path] {
+    let rs: ~[Path] = rust_path().move_iter()
         .filter(|ws| workspace_contains_package_id(pkgid, ws))
-        .collect()
+        .collect();
+    if cx.use_rust_path_hack {
+        rs + option_to_vec(find_dir_using_rust_path_hack(cx, pkgid))
+    }
+    else {
+        rs
+    }
+}
+
+pub fn is_workspace(p: &Path) -> bool {
+    os::path_is_dir(&p.push("src"))
 }
 
 /// Construct a workspace and package-ID name based on the current directory.