diff --git a/doc/rustpkg.md b/doc/rustpkg.md index 399a7642665..53829e45196 100644 --- a/doc/rustpkg.md +++ b/doc/rustpkg.md @@ -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 diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs index de673972932..390a09d4aa3 100644 --- a/src/librustpkg/api.rs +++ b/src/librustpkg/api.rs @@ -16,6 +16,8 @@ use target::*; 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 { diff --git a/src/librustpkg/conditions.rs b/src/librustpkg/conditions.rs index 2e049d252bc..f40f48e851f 100644 --- a/src/librustpkg/conditions.rs +++ b/src/librustpkg/conditions.rs @@ -50,3 +50,7 @@ condition! { condition! { pub failed_to_create_temp_dir: (~str) -> Path; } + +condition! { + pub git_checkout_failed: (~str, Path) -> (); +} diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs index 68cfa3220f2..c06e197787c 100644 --- a/src/librustpkg/package_id.rs +++ b/src/librustpkg/package_id.rs @@ -102,10 +102,7 @@ impl PkgId { } 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 @@ impl PkgId { } } +pub fn prefixes_iter(p: &Path) -> Prefixes { + Prefixes { + components: p.components().to_owned(), + remaining: ~[] + } +} + struct Prefixes { priv components: ~[~str], priv remaining: ~[~str] diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index 8428c373de6..86566020812 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -17,10 +17,11 @@ use std::os; 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; @@ -30,7 +31,17 @@ 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 @@ condition! { 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 @@ impl 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 @@ impl 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/ part to get the workspace + destination_workspace = source_workspace.pop().pop(); + } + break; + } + None => () + } } match ok_d { Some(d) => d, @@ -138,6 +208,9 @@ impl 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 @@ impl 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 @@ 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(local: &Path, pkgid: &PkgId) -> Option { - 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)); + 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()); - } - - 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()); - - 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()) + match safe_git_clone(&pkgid.path, &pkgid.version, local) { + CheckedOutSources => { + make_read_only(local); + Some(local.clone()) } - else { - None + DirToUse(clone_target) => { + 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 mut failed = false; + + 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 { @@ -292,7 +361,6 @@ impl PkgSrc { fn build_crates(&self, ctx: &BuildContext, - destination_dir: &Path, crates: &[Crate], cfgs: &[~str], what: OutputType) { @@ -311,7 +379,7 @@ impl PkgSrc { 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 @@ impl PkgSrc { // 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 diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index f0e92741445..fbb2255ad1c 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -16,6 +16,7 @@ pub use version::{Version, NoVersion, split_version_general, try_parsing_version 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 + } + } +} + diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 42b8ebe4953..e6ce5ba9d49 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -39,8 +39,9 @@ use path_util::{build_pkg_id_in_workspace, built_test_in_workspace}; use path_util::{U_RWX, in_rust_path}; use path_util::{built_executable_in_workspace, built_library_in_workspace, default_workspace}; use path_util::{target_executable_in_workspace, target_library_in_workspace}; -use source_control::is_git_dir; +use 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 @@ impl CtxMethods for BuildContext { 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 @@ impl CtxMethods for BuildContext { 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 @@ impl CtxMethods for BuildContext { 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 @@ impl CtxMethods for BuildContext { 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 @@ impl CtxMethods for BuildContext { // 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 @@ impl CtxMethods for BuildContext { 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 @@ impl CtxMethods for BuildContext { 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 @@ impl CtxMethods for BuildContext { 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 @@ impl CtxMethods for BuildContext { 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(), diff --git a/src/librustpkg/source_control.rs b/src/librustpkg/source_control.rs index 3d03d89bc20..877247bccad 100644 --- a/src/librustpkg/source_control.rs +++ b/src/librustpkg/source_control.rs @@ -12,62 +12,99 @@ 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 + cond.raise((source.to_owned(), target.clone())) } - else { - true - } - } - _ => true } + _ => () } + } } fn process_output_in_cwd(prog: &str, args: &[~str], cwd: &Path) -> ProcessOutput { diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 6ef0b384a64..fea81abe0b6 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -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; @@ -27,13 +27,15 @@ use path_util::{target_executable_in_workspace, target_test_in_workspace, 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 - } - } -} diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 8bd6b04864b..d66dd847405 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -27,8 +27,8 @@ use context::{in_target, StopBefore, Link, Assemble, BuildContext}; 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 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { 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 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { 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 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { fail2!("Bad kind: {}", *what); } } - // Also, add an additional search path - debug2!("Installed {} into {}", lib_name, target_workspace.to_str()); - (self.save)(target_workspace); } } } diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index 9642d004de3..d5dab15c02a 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -13,12 +13,11 @@ 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() + } +}