rustpkg now makes source files that it checks out automatically read-only, and stores
them under build/.
Also, refactored the `PkgSrc` type to keep track of separate source and destination
workspaces, as well as to have a `build_workspace` method that returns the workspace
to put temporary files in (usually the source, sometimes the destination -- see
comments for more details).
Closes #6480
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
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
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;
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, ~[]);
}
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: ~[],
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 {
condition! {
pub failed_to_create_temp_dir: (~str) -> Path;
}
+
+condition! {
+ pub git_checkout_failed: (~str, Path) -> ();
+}
}
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*
}
}
+pub fn prefixes_iter(p: &Path) -> Prefixes {
+ Prefixes {
+ components: p.components().to_owned(),
+ remaining: ~[]
+ }
+}
+
struct Prefixes {
priv components: ~[~str],
priv remaining: ~[~str]
use context::*;
use crate::Crate;
use messages::*;
-use source_control::{git_clone, git_clone_general};
-use path_util::{find_dir_using_rust_path_hack, default_workspace, make_dir_rwx_recursive};
+use source_control::{safe_git_clone, git_clone_url, DirToUse, CheckedOutSources};
+use source_control::make_read_only;
+use path_util::{find_dir_using_rust_path_hack, make_dir_rwx_recursive};
+use path_util::{target_build_dir, versionize};
use util::compile_crate;
-use workspace::is_workspace;
use workcache_support;
use workcache_support::crate_tag;
use extra::workcache;
#[deriving(Clone)]
pub struct PkgSrc {
/// Root of where the package source code lives
- workspace: Path,
+ source_workspace: Path,
+ /// If build_in_destination is true, temporary results should
+ /// go in the build/ subdirectory of the destination workspace.
+ /// (Otherwise, they go in the build/ subdirectory of the
+ /// source workspace.) This happens if the "RUST_PATH hack" is
+ /// in effect, or if sources were fetched from a remote
+ /// repository.
+ build_in_destination: bool,
+ /// Where to install the results. May or may not be the same
+ /// as source_workspace
+ destination_workspace: Path,
// Directory to start looking in for packages -- normally
// this is workspace/src/id but it may be just workspace
start_dir: Path,
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! {
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 => {
// 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;
+ }
}
};
// Ok, no prefixes work, so try fetching from git
let mut ok_d = None;
- for w in to_try.iter() {
+ for w in output_names.iter() {
debug2!("Calling fetch_git on {}", w.to_str());
- let gf = PkgSrc::fetch_git(w, &id);
- for p in gf.iter() {
+ let target_dir_opt = PkgSrc::fetch_git(w, &id);
+ for p in target_dir_opt.iter() {
ok_d = Some(p.clone());
+ build_in_destination = true;
+ debug2!("2. build_in_destination = {:?}", build_in_destination);
break;
}
- if ok_d.is_some() { break; }
+ match ok_d {
+ Some(ref d) => {
+ if d.is_parent_of(&id.path)
+ || d.is_parent_of(&versionize(&id.path, &id.version)) {
+ // Strip off the package ID
+ source_workspace = d.clone();
+ for _ in id.path.components().iter() {
+ source_workspace = source_workspace.pop();
+ }
+ // Strip off the src/ part
+ source_workspace = source_workspace.pop();
+ // Strip off the build/<target-triple> part to get the workspace
+ destination_workspace = source_workspace.pop().pop();
+ }
+ break;
+ }
+ None => ()
+ }
}
match ok_d {
Some(d) => d,
}
}
};
+ 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) {
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: ~[],
/// refers to a git repo on the local version, also check it out.
/// (right now we only support git)
pub fn fetch_git(local: &Path, pkgid: &PkgId) -> Option<Path> {
- use conditions::failed_to_create_temp_dir::cond;
+ use conditions::git_checkout_failed::cond;
// We use a temporary directory because if the git clone fails,
// it creates the target directory anyway and doesn't delete it
- let scratch_dir = extra::tempfile::mkdtemp(&os::tmpdir(), "rustpkg");
- let clone_target = match scratch_dir {
- Some(d) => d.push("rustpkg_temp"),
- None => cond.raise(~"Failed to create temporary directory for fetching git sources")
- };
-
debug2!("Checking whether {} (path = {}) exists locally. Cwd = {}, does it? {:?}",
- pkgid.to_str(), pkgid.path.to_str(),
- os::getcwd().to_str(),
- os::path_exists(&pkgid.path));
-
- if os::path_exists(&pkgid.path) {
- debug2!("{} exists locally! Cloning it into {}",
- pkgid.path.to_str(), local.to_str());
- // Ok to use local here; we know it will succeed
- git_clone(&pkgid.path, local, &pkgid.version);
- return Some(local.clone());
- }
+ pkgid.to_str(), pkgid.path.to_str(),
+ os::getcwd().to_str(),
+ os::path_exists(&pkgid.path));
+
+ match safe_git_clone(&pkgid.path, &pkgid.version, local) {
+ CheckedOutSources => {
+ make_read_only(local);
+ Some(local.clone())
+ }
+ DirToUse(clone_target) => {
+ if pkgid.path.components().len() < 2 {
+ // If a non-URL, don't bother trying to fetch
+ return None;
+ }
- if pkgid.path.components().len() < 2 {
- // If a non-URL, don't bother trying to fetch
- return None;
- }
+ let url = format!("https://{}", pkgid.path.to_str());
+ debug2!("Fetching package: git clone {} {} [version={}]",
+ url, clone_target.to_str(), pkgid.version.to_str());
- let url = format!("https://{}", pkgid.path.to_str());
- debug2!("Fetching package: git clone {} {} [version={}]",
- url, clone_target.to_str(), pkgid.version.to_str());
+ let mut failed = false;
- if git_clone_general(url, &clone_target, &pkgid.version) {
- // Since the operation succeeded, move clone_target to local.
- // First, create all ancestor directories.
- if make_dir_rwx_recursive(&local.pop())
- && os::rename_file(&clone_target, local) {
- Some(local.clone())
- }
- else {
- None
+ do cond.trap(|_| {
+ failed = true;
+ }).inside {
+ git_clone_url(url, &clone_target, &pkgid.version);
+ };
+
+ if failed {
+ return None;
+ }
+
+ // Move clone_target to local.
+ // First, create all ancestor directories.
+ let moved = make_dir_rwx_recursive(&local.pop())
+ && os::rename_file(&clone_target, local);
+ if moved { Some(local.clone()) }
+ else { None }
}
}
- else {
- None
- }
}
-
// If a file named "pkg.rs" in the start directory exists,
// return the path for it. Otherwise, None
pub fn package_script_option(&self) -> Option<Path> {
fn build_crates(&self,
ctx: &BuildContext,
- destination_dir: &Path,
crates: &[Crate],
cfgs: &[~str],
what: OutputType) {
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,
// 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
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;
}
/// 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
+ }
+ }
+}
+
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};
/// 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,
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]) {
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);
}
}
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);
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? {:?} \
// 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
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);
}
}
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()];
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));
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));
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(),
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 {
if outp.status != 0 {
debug2!("{}", str::from_utf8_owned(outp.output.clone()));
debug2!("{}", str::from_utf8_owned(outp.error));
- false
- }
- else {
- true
+ cond.raise((source.to_owned(), target.clone()))
}
- }
- _ => true
}
+ _ => ()
}
+ }
}
fn process_output_in_cwd(prog: &str, args: &[~str], cwd: &Path) -> ProcessOutput {
// rustpkg unit tests
use context::{BuildContext, Context, RustcFlags};
-use std::{io, libc, os, run, str, task};
+use std::{io, os, run, str, task};
use extra::arc::Arc;
use extra::arc::RWArc;
use extra::tempfile::mkdtemp;
target_bench_in_workspace, make_dir_rwx, U_RWX,
library_in_workspace, installed_library_in_workspace,
built_bench_in_workspace, built_test_in_workspace,
- built_library_in_workspace, built_executable_in_workspace, target_build_dir};
+ built_library_in_workspace, built_executable_in_workspace, target_build_dir,
+ chmod_read_only};
use rustc::back::link::get_cc_prog;
use rustc::metadata::filesearch::rust_path;
use rustc::driver::driver::{build_session, build_session_options, host_triple, optgroups};
use syntax::diagnostic;
use target::*;
use package_source::PkgSrc;
+use source_control::{CheckedOutSources, DirToUse, safe_git_clone};
use util::datestamp;
fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext {
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);
}
}
+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.
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())
}
}
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);
// 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.
// 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());
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")));
}
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);
}
}
#[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"),
}
}
+#[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};
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
- }
- }
-}
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};
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
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
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,
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" {
fail2!("Bad kind: {}", *what);
}
}
- // Also, add an additional search path
- debug2!("Installed {} into {}", lib_name, target_workspace.to_str());
- (self.save)(target_workspace);
}
}
}
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
}
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()
+ }
+}