diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index edea267d22686..2daa2bafaacec 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -129,7 +129,7 @@ fn check_impl_of_trait(cx: Context, it: @item, trait_ref: &trait_ref, self_type: // If this trait has builtin-kind supertraits, meet them. let self_ty: ty::t = ty::node_id_to_type(cx.tcx, it.id); - error!("checking impl with self type %?", ty::get(self_ty).sty); + debug!("checking impl with self type %?", ty::get(self_ty).sty); do check_builtin_bounds(cx, self_ty, trait_def.bounds) |missing| { cx.tcx.sess.span_err(self_type.span, fmt!("the type `%s', which does not fulfill `%s`, cannot implement this \ diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs index 2da66baa412b5..dfe80674b7fa1 100644 --- a/src/librustpkg/api.rs +++ b/src/librustpkg/api.rs @@ -20,7 +20,12 @@ use std::hashmap::*; /// 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, diff --git a/src/librustpkg/conditions.rs b/src/librustpkg/conditions.rs index 4f192fd1d92c1..785c635e08561 100644 --- a/src/librustpkg/conditions.rs +++ b/src/librustpkg/conditions.rs @@ -36,3 +36,11 @@ condition! { condition! { no_rust_path: (~str) -> super::Path; } + +condition! { + not_a_workspace: (~str) -> super::Path; +} + +condition! { + failed_to_create_temp_dir: (~str) -> super::Path; +} diff --git a/src/librustpkg/context.rs b/src/librustpkg/context.rs index 93e0789dcb0c9..4087fdd7ca504 100644 --- a/src/librustpkg/context.rs +++ b/src/librustpkg/context.rs @@ -15,6 +15,11 @@ use std::hashmap::HashMap; 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>, diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index 42f33592aaaea..ae2083f1b22c1 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -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 context::*; 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 @@ impl 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 @@ impl PkgSrc { 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 @@ impl PkgSrc { /// 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 { + 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 @@ impl PkgSrc { 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 @@ impl PkgSrc { /// 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 @@ impl PkgSrc { fn build_crates(&self, ctx: &Ctx, src_dir: &Path, + destination_dir: &Path, crates: &[Crate], cfgs: &[~str], what: OutputType) { @@ -194,8 +228,8 @@ impl PkgSrc { 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 @@ impl PkgSrc { } } - 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 } } diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index 467477ca479de..af70a79f93d2b 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -14,6 +14,7 @@ pub use package_id::PkgId; 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 { + 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 Option { - // 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 { + 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 +} diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 79836bcc5554b..6a6e7569a6518 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -22,12 +22,7 @@ extern mod extra; 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 @@ impl<'self> PkgScript<'self> { 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 @@ impl CtxMethods for Ctx { "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 @@ impl CtxMethods for Ctx { "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 @@ impl CtxMethods for Ctx { 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 @@ impl CtxMethods for Ctx { 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 @@ impl CtxMethods for Ctx { 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 @@ impl CtxMethods for Ctx { // 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 @@ impl CtxMethods for Ctx { } 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) } /** diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 98999da41c816..4f8e80f56d5d7 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -34,6 +34,7 @@ fn datestamp(p: &Path) -> Option { 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}; diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 4bdb442c1e619..958786b7cf4e2 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -452,6 +452,13 @@ mod test { } +pub fn option_to_vec(x: Option) -> ~[T] { + match x { + Some(y) => ~[y], + None => ~[] + } +} + // tjc: cheesy fn debug_flags() -> ~[~str] { ~[] } // static DEBUG_FLAGS: ~[~str] = ~[~"-Z", ~"time-passes"]; diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index 1afe5d513cc14..5ad2dfd6d2f49 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -12,15 +12,17 @@ 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. diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 07e0b0857a181..9140816278807 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -1002,6 +1002,18 @@ pub fn remove_file(p: &Path) -> bool { } } +/// Renames an existing file or directory +pub fn rename_file(old: &Path, new: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + do old.with_c_str |old_buf| { + do new.with_c_str |new_buf| { + libc::rename(old_buf, new_buf) == (0 as c_int) + } + } + } +} + #[cfg(unix)] /// Returns the platform-specific value of errno pub fn errno() -> int { diff --git a/src/test/run-pass/rename-directory.rs b/src/test/run-pass/rename-directory.rs new file mode 100644 index 0000000000000..6079168838585 --- /dev/null +++ b/src/test/run-pass/rename-directory.rs @@ -0,0 +1,57 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test can't be a unit test in std, +// because it needs mkdtemp, which is in extra + +// xfail-fast +extern mod extra; + +use extra::tempfile::mkdtemp; +use std::os; +use std::libc; +use std::libc::*; + +fn rename_directory() { + #[fixed_stack_segment]; + unsafe { + static U_RWX: i32 = (S_IRUSR | S_IWUSR | S_IXUSR) as i32; + + let tmpdir = mkdtemp(&os::tmpdir(), "rename_directory").expect("rename_directory failed"); + let old_path = tmpdir.push_many(["foo", "bar", "baz"]); + assert!(os::mkdir_recursive(&old_path, U_RWX)); + let test_file = &old_path.push("temp.txt"); + + /* Write the temp input file */ + let ostream = do test_file.to_str().with_c_str |fromp| { + do "w+b".with_c_str |modebuf| { + libc::fopen(fromp, modebuf) + } + }; + assert!((ostream as uint != 0u)); + let s = ~"hello"; + do "hello".with_c_str |buf| { + let write_len = libc::fwrite(buf as *c_void, + 1u as size_t, + (s.len() + 1u) as size_t, + ostream); + assert_eq!(write_len, (s.len() + 1) as size_t) + } + assert_eq!(libc::fclose(ostream), (0u as c_int)); + + let new_path = tmpdir.push_many(["quux", "blat"]); + assert!(os::mkdir_recursive(&new_path, U_RWX)); + assert!(os::rename_file(&old_path, &new_path.push("newdir"))); + assert!(os::path_is_dir(&new_path.push("newdir"))); + assert!(os::path_exists(&new_path.push_many(["newdir", "temp.txt"]))); + } +} + +fn main() { rename_directory() }