rustpkg: Allow package directories to appear in the RUST_PATH

This commit adds a rustpkg flag, --rust-path-hack, that allows
rustpkg to *search* inside package directories if they appear in
the RUST_PATH, while *building* libraries and executables into a
different target directory.

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

This commit also includes a fix for how rustpkg fetches sources
from git repositories -- it uses a temporary directory as the target
when invoking `git clone`, then moves that directory into the workspace
if the clone was successful. (The old behavior was that when the
`git clone` failed, the empty target directory would be left lying
around anyway.)
This commit is contained in:
Tim Chevalier 2013-08-28 14:52:37 -07:00
parent 7cbdee1663
commit 98e470ad73
8 changed files with 428 additions and 103 deletions

View File

@ -20,7 +20,12 @@
/// Convenience functions intended for calling from pkg.rs /// Convenience functions intended for calling from pkg.rs
fn default_ctxt(p: @Path) -> Ctx { fn default_ctxt(p: @Path) -> Ctx {
Ctx { sysroot_opt: Some(p), json: false, dep_cache: @mut HashMap::new() } Ctx {
use_rust_path_hack: false,
sysroot_opt: Some(p),
json: false,
dep_cache: @mut HashMap::new()
}
} }
pub fn build_lib(sysroot: @Path, root: Path, name: ~str, version: Version, pub fn build_lib(sysroot: @Path, root: Path, name: ~str, version: Version,

View File

@ -15,6 +15,11 @@
use std::os; use std::os;
pub struct Ctx { pub struct Ctx {
// If use_rust_path_hack is true, rustpkg searches for sources
// in *package* directories that are in the RUST_PATH (for example,
// FOO/src/bar-0.1 instead of FOO). The flag doesn't affect where
// rustpkg stores build artifacts.
use_rust_path_hack: bool,
// Sysroot -- if this is None, uses rustc filesearch's // Sysroot -- if this is None, uses rustc filesearch's
// idea of the default // idea of the default
sysroot_opt: Option<@Path>, sysroot_opt: Option<@Path>,

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
extern mod extra;
use target::*; use target::*;
use package_id::PkgId; use package_id::PkgId;
use std::path::Path; use std::path::Path;
@ -16,8 +18,9 @@
use crate::Crate; use crate::Crate;
use messages::*; use messages::*;
use source_control::{git_clone, git_clone_general}; use source_control::{git_clone, git_clone_general};
use path_util::pkgid_src_in_workspace; use path_util::{pkgid_src_in_workspace, find_dir_using_rust_path_hack, default_workspace};
use util::compile_crate; use util::compile_crate;
use workspace::is_workspace;
// An enumeration of the unpacked source of a package workspace. // An enumeration of the unpacked source of a package workspace.
// This contains a list of files found in the source workspace. // This contains a list of files found in the source workspace.
@ -48,7 +51,7 @@ pub fn new(src_dir: &Path, id: &PkgId) -> PkgSrc {
} }
fn check_dir(&self) -> Path { fn check_dir(&self, cx: &Ctx) -> Path {
use conditions::nonexistent_package::cond; use conditions::nonexistent_package::cond;
debug!("Pushing onto root: %s | %s", self.id.path.to_str(), self.root.to_str()); debug!("Pushing onto root: %s | %s", self.id.path.to_str(), self.root.to_str());
@ -59,12 +62,21 @@ fn check_dir(&self) -> Path {
let dir = match path { let dir = match path {
Some(d) => (*d).clone(), Some(d) => (*d).clone(),
None => match self.fetch_git() { None => {
Some(d) => d, match self.fetch_git() {
None => cond.raise((self.id.clone(), ~"supplied path for package dir does not \ Some(d) => d,
exist, and couldn't interpret it as a URL fragment")) None => {
match find_dir_using_rust_path_hack(cx, &self.id) {
Some(d) => d,
None => cond.raise((self.id.clone(),
~"supplied path for package dir does not \
exist, and couldn't interpret it as a URL fragment"))
}
}
}
} }
}; };
debug!("For package id %s, returning %s", self.id.to_str(), dir.to_str());
if !os::path_is_dir(&dir) { if !os::path_is_dir(&dir) {
cond.raise((self.id.clone(), ~"supplied path for package dir is a \ cond.raise((self.id.clone(), ~"supplied path for package dir is a \
non-directory")); non-directory"));
@ -79,11 +91,19 @@ fn check_dir(&self) -> Path {
/// refers to a git repo on the local version, also check it out. /// refers to a git repo on the local version, also check it out.
/// (right now we only support git) /// (right now we only support git)
pub fn fetch_git(&self) -> Option<Path> { pub fn fetch_git(&self) -> Option<Path> {
use conditions::failed_to_create_temp_dir::cond;
// We use a temporary directory because if the git clone fails,
// it creates the target directory anyway and doesn't delete it
let scratch_dir = extra::tempfile::mkdtemp(&os::tmpdir(), "rustpkg");
let clone_target = match scratch_dir {
Some(d) => d.push("rustpkg_temp"),
None => cond.raise(~"Failed to create temporary directory for fetching git sources")
};
let mut local = self.root.push("src"); let mut local = self.root.push("src");
local = local.push(self.id.to_str()); local = local.push(self.id.to_str());
// Git can't clone into a non-empty directory
os::remove_dir_recursive(&local);
debug!("Checking whether %s exists locally. Cwd = %s, does it? %?", debug!("Checking whether %s exists locally. Cwd = %s, does it? %?",
self.id.path.to_str(), self.id.path.to_str(),
@ -93,15 +113,28 @@ pub fn fetch_git(&self) -> Option<Path> {
if os::path_exists(&self.id.path) { if os::path_exists(&self.id.path) {
debug!("%s exists locally! Cloning it into %s", debug!("%s exists locally! Cloning it into %s",
self.id.path.to_str(), local.to_str()); self.id.path.to_str(), local.to_str());
// Ok to use local here; we know it will succeed
git_clone(&self.id.path, &local, &self.id.version); git_clone(&self.id.path, &local, &self.id.version);
return Some(local); return Some(local);
} }
if (self.id.path.clone()).components().len() < 2 {
// If a non-URL, don't bother trying to fetch
return None;
}
let url = fmt!("https://%s", self.id.path.to_str()); let url = fmt!("https://%s", self.id.path.to_str());
note(fmt!("Fetching package: git clone %s %s [version=%s]", note(fmt!("Fetching package: git clone %s %s [version=%s]",
url, local.to_str(), self.id.version.to_str())); url, clone_target.to_str(), self.id.version.to_str()));
if git_clone_general(url, &local, &self.id.version) {
Some(local) if git_clone_general(url, &clone_target, &self.id.version) {
// since the operation succeeded, move clone_target to local
if !os::rename_file(&clone_target, &local) {
None
}
else {
Some(local)
}
} }
else { else {
None None
@ -138,10 +171,10 @@ fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) {
/// Infers crates to build. Called only in the case where there /// Infers crates to build. Called only in the case where there
/// is no custom build logic /// is no custom build logic
pub fn find_crates(&mut self) { pub fn find_crates(&mut self, cx: &Ctx) {
use conditions::missing_pkg_files::cond; use conditions::missing_pkg_files::cond;
let dir = self.check_dir(); let dir = self.check_dir(cx);
debug!("Called check_dir, I'm in %s", dir.to_str()); debug!("Called check_dir, I'm in %s", dir.to_str());
let prefix = dir.components.len(); let prefix = dir.components.len();
debug!("Matching against %?", self.id.short_name); debug!("Matching against %?", self.id.short_name);
@ -183,6 +216,7 @@ pub fn find_crates(&mut self) {
fn build_crates(&self, fn build_crates(&self,
ctx: &Ctx, ctx: &Ctx,
src_dir: &Path, src_dir: &Path,
destination_dir: &Path,
crates: &[Crate], crates: &[Crate],
cfgs: &[~str], cfgs: &[~str],
what: OutputType) { what: OutputType) {
@ -194,8 +228,8 @@ fn build_crates(&self,
let result = compile_crate(ctx, let result = compile_crate(ctx,
&self.id, &self.id,
path, path,
// compile_crate wants the workspace // compile_crate wants the destination workspace
&self.root, destination_dir,
crate.flags, crate.flags,
crate.cfgs + cfgs, crate.cfgs + cfgs,
false, false,
@ -209,15 +243,39 @@ fn build_crates(&self,
} }
} }
pub fn build(&self, ctx: &Ctx, cfgs: ~[~str]) { pub fn build(&self, ctx: &Ctx, cfgs: ~[~str]) -> Path {
let dir = self.check_dir(); use conditions::not_a_workspace::cond;
debug!("Building libs in %s", dir.to_str());
self.build_crates(ctx, &dir, self.libs, cfgs, Lib); // Determine the destination workspace (which depends on whether
// we're using the rust_path_hack)
let destination_workspace = if is_workspace(&self.root) {
debug!("%s is indeed a workspace", self.root.to_str());
self.root.clone()
}
else {
// It would be nice to have only one place in the code that checks
// for the use_rust_path_hack flag...
if ctx.use_rust_path_hack {
let rs = default_workspace();
debug!("Using hack: %s", rs.to_str());
rs
}
else {
cond.raise(fmt!("Package root %s is not a workspace; pass in --rust_path_hack \
if you want to treat it as a package source", self.root.to_str()))
}
};
let dir = self.check_dir(ctx);
debug!("Building libs in %s, destination = %s", dir.to_str(),
destination_workspace.to_str());
self.build_crates(ctx, &dir, &destination_workspace, self.libs, cfgs, Lib);
debug!("Building mains"); debug!("Building mains");
self.build_crates(ctx, &dir, self.mains, cfgs, Main); self.build_crates(ctx, &dir, &destination_workspace, self.mains, cfgs, Main);
debug!("Building tests"); debug!("Building tests");
self.build_crates(ctx, &dir, self.tests, cfgs, Test); self.build_crates(ctx, &dir, &destination_workspace, self.tests, cfgs, Test);
debug!("Building benches"); debug!("Building benches");
self.build_crates(ctx, &dir, self.benchs, cfgs, Bench); self.build_crates(ctx, &dir, &destination_workspace, self.benchs, cfgs, Bench);
destination_workspace
} }
} }

View File

@ -14,6 +14,7 @@
pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install}; pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install};
pub use version::{Version, NoVersion, split_version_general, try_parsing_version}; pub use version::{Version, NoVersion, split_version_general, try_parsing_version};
pub use rustc::metadata::filesearch::rust_path; pub use rustc::metadata::filesearch::rust_path;
use context::Ctx;
use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
use std::os::mkdir_recursive; use std::os::mkdir_recursive;
@ -51,18 +52,23 @@ pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, U_RWX) }
pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool { pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
debug!("Checking in src dir of %s for %s", debug!("Checking in src dir of %s for %s",
workspace.to_str(), pkgid.to_str()); workspace.to_str(), pkgid.to_str());
workspace_contains_package_id_(pkgid, workspace, |p| { p.push("src") }).is_some()
}
let src_dir = workspace.push("src"); pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path,
// Returns the directory it was actually found in
workspace_to_src_dir: &fn(&Path) -> Path) -> Option<Path> {
let src_dir = workspace_to_src_dir(workspace);
let mut found = false; let mut found = None;
do os::walk_dir(&src_dir) |p| { do os::walk_dir(&src_dir) |p| {
debug!("=> p = %s", p.to_str()); debug!("=> p = %s", p.to_str());
let was_found = os::path_is_dir(p) && { if os::path_is_dir(p) {
debug!("p = %s, path = %s [%s]", p.to_str(), pkgid.path.to_str(), debug!("p = %s, path = %s [%s]", p.to_str(), pkgid.path.to_str(),
src_dir.push_rel(&pkgid.path).to_str()); src_dir.push_rel(&pkgid.path).to_str());
*p == src_dir.push_rel(&pkgid.path) || { if *p == src_dir.push_rel(&pkgid.path) || {
let pf = p.filename(); let pf = p.filename();
do pf.iter().any |pf| { do pf.iter().any |pf| {
let g = pf.to_str(); let g = pf.to_str();
@ -76,16 +82,15 @@ pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
} }
} }
} }
} {
found = Some(p.clone());
} }
};
if was_found { };
found = true
}
true true
}; };
debug!(if found { fmt!("Found %s in %s", pkgid.to_str(), workspace.to_str()) } debug!(if found.is_some() { fmt!("Found %s in %s", pkgid.to_str(), workspace.to_str()) }
else { fmt!("Didn't find %s in %s", pkgid.to_str(), workspace.to_str()) }); else { fmt!("Didn't find %s in %s", pkgid.to_str(), workspace.to_str()) });
found found
} }
@ -123,8 +128,7 @@ pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<
Some(result) Some(result)
} }
else { else {
// This is not an error, but it's worth logging it debug!("built_executable_in_workspace: %s does not exist", result.to_str());
error!(fmt!("built_executable_in_workspace: %s does not exist", result.to_str()));
None None
} }
} }
@ -164,7 +168,7 @@ pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Pat
/// Does the actual searching stuff /// Does the actual searching stuff
pub fn installed_library_in_workspace(short_name: &str, workspace: &Path) -> Option<Path> { pub fn installed_library_in_workspace(short_name: &str, workspace: &Path) -> Option<Path> {
// NOTE: this could break once we're handling multiple versions better... want a test for it // This could break once we're handling multiple versions better -- I should add a test for it
library_in_workspace(&Path(short_name), short_name, Install, workspace, "lib", &NoVersion) library_in_workspace(&Path(short_name), short_name, Install, workspace, "lib", &NoVersion)
} }
@ -246,8 +250,8 @@ pub fn library_in_workspace(path: &Path, short_name: &str, where: Target,
} // for } // for
if result_filename.is_none() { if result_filename.is_none() {
warn(fmt!("library_in_workspace didn't find a library in %s for %s", debug!("warning: library_in_workspace didn't find a library in %s for %s",
dir_to_search.to_str(), short_name)); dir_to_search.to_str(), short_name);
} }
// Return the filename that matches, which we now know exists // Return the filename that matches, which we now know exists
@ -392,3 +396,25 @@ pub fn uninstall_package_from(workspace: &Path, pkgid: &PkgId) {
} }
} }
fn dir_has_file(dir: &Path, file: &str) -> bool {
assert!(dir.is_absolute());
os::path_exists(&dir.push(file))
}
pub fn find_dir_using_rust_path_hack(cx: &Ctx, p: &PkgId) -> Option<Path> {
if !cx.use_rust_path_hack {
return None;
}
let rp = rust_path();
for dir in rp.iter() {
debug!("In find_dir_using_rust_path_hack: checking dir %s", dir.to_str());
if dir_has_file(dir, "lib.rs") || dir_has_file(dir, "main.rs")
|| dir_has_file(dir, "test.rs") || dir_has_file(dir, "bench.rs") {
debug!("Did find id %s in dir %s", p.to_str(), dir.to_str());
return Some(dir.clone());
}
debug!("Didn't find id %s in dir %s", p.to_str(), dir.to_str())
}
None
}

View File

@ -22,12 +22,7 @@
extern mod rustc; extern mod rustc;
extern mod syntax; extern mod syntax;
use std::result; use std::{io, os, result, run, str};
use std::io;
use std::os;
use std::run;
use std::str;
pub use std::path::Path; pub use std::path::Path;
use std::hashmap::HashMap; use std::hashmap::HashMap;
@ -173,7 +168,8 @@ fn hash(&self) -> ~str {
pub trait CtxMethods { pub trait CtxMethods {
fn run(&self, cmd: &str, args: ~[~str]); fn run(&self, cmd: &str, args: ~[~str]);
fn do_cmd(&self, _cmd: &str, _pkgname: &str); fn do_cmd(&self, _cmd: &str, _pkgname: &str);
fn build(&self, workspace: &Path, pkgid: &PkgId); /// Returns the destination workspace
fn build(&self, workspace: &Path, pkgid: &PkgId) -> Path;
fn clean(&self, workspace: &Path, id: &PkgId); fn clean(&self, workspace: &Path, id: &PkgId);
fn info(&self); fn info(&self);
fn install(&self, workspace: &Path, id: &PkgId); fn install(&self, workspace: &Path, id: &PkgId);
@ -191,15 +187,19 @@ fn run(&self, cmd: &str, args: ~[~str]) {
"build" => { "build" => {
if args.len() < 1 { if args.len() < 1 {
match cwd_to_workspace() { match cwd_to_workspace() {
None => { usage::build(); return } None if self.use_rust_path_hack => {
Some((ws, pkgid)) => self.build(&ws, &pkgid) let cwd = os::getcwd();
self.build(&cwd, &PkgId::new(cwd.components[cwd.components.len() - 1]));
}
None => { usage::build(); return; }
Some((ws, pkgid)) => { self.build(&ws, &pkgid); }
} }
} }
else { else {
// The package id is presumed to be the first command-line // The package id is presumed to be the first command-line
// argument // argument
let pkgid = PkgId::new(args[0].clone()); let pkgid = PkgId::new(args[0].clone());
do each_pkg_parent_workspace(&pkgid) |workspace| { do each_pkg_parent_workspace(self, &pkgid) |workspace| {
debug!("found pkg %s in workspace %s, trying to build", debug!("found pkg %s in workspace %s, trying to build",
pkgid.to_str(), workspace.to_str()); pkgid.to_str(), workspace.to_str());
self.build(workspace, &pkgid); self.build(workspace, &pkgid);
@ -238,15 +238,20 @@ fn run(&self, cmd: &str, args: ~[~str]) {
"install" => { "install" => {
if args.len() < 1 { if args.len() < 1 {
match cwd_to_workspace() { match cwd_to_workspace() {
None => { usage::install(); return } None if self.use_rust_path_hack => {
Some((ws, pkgid)) => self.install(&ws, &pkgid) let cwd = os::getcwd();
} self.install(&cwd,
&PkgId::new(cwd.components[cwd.components.len() - 1]));
}
None => { usage::install(); return; }
Some((ws, pkgid)) => self.install(&ws, &pkgid),
}
} }
else { else {
// The package id is presumed to be the first command-line // The package id is presumed to be the first command-line
// argument // argument
let pkgid = PkgId::new(args[0]); let pkgid = PkgId::new(args[0]);
let workspaces = pkg_parent_workspaces(&pkgid); let workspaces = pkg_parent_workspaces(self, &pkgid);
debug!("package ID = %s, found it in %? workspaces", debug!("package ID = %s, found it in %? workspaces",
pkgid.to_str(), workspaces.len()); pkgid.to_str(), workspaces.len());
if workspaces.is_empty() { if workspaces.is_empty() {
@ -257,7 +262,7 @@ fn run(&self, cmd: &str, args: ~[~str]) {
self.install(&rp[0], &pkgid); self.install(&rp[0], &pkgid);
} }
else { else {
do each_pkg_parent_workspace(&pkgid) |workspace| { do each_pkg_parent_workspace(self, &pkgid) |workspace| {
self.install(workspace, &pkgid); self.install(workspace, &pkgid);
true true
}; };
@ -294,7 +299,7 @@ fn run(&self, cmd: &str, args: ~[~str]) {
else { else {
let rp = rust_path(); let rp = rust_path();
assert!(!rp.is_empty()); assert!(!rp.is_empty());
do each_pkg_parent_workspace(&pkgid) |workspace| { do each_pkg_parent_workspace(self, &pkgid) |workspace| {
path_util::uninstall_package_from(workspace, &pkgid); path_util::uninstall_package_from(workspace, &pkgid);
note(fmt!("Uninstalled package %s (was installed in %s)", note(fmt!("Uninstalled package %s (was installed in %s)",
pkgid.to_str(), workspace.to_str())); pkgid.to_str(), workspace.to_str()));
@ -318,7 +323,9 @@ fn do_cmd(&self, _cmd: &str, _pkgname: &str) {
fail!("`do` not yet implemented"); fail!("`do` not yet implemented");
} }
fn build(&self, workspace: &Path, pkgid: &PkgId) { /// Returns the destination workspace
/// In the case of a custom build, we don't know, so we just return the source workspace
fn build(&self, workspace: &Path, pkgid: &PkgId) -> Path {
debug!("build: workspace = %s (in Rust path? %? is git dir? %? \ debug!("build: workspace = %s (in Rust path? %? is git dir? %? \
pkgid = %s", workspace.to_str(), pkgid = %s", workspace.to_str(),
in_rust_path(workspace), is_git_dir(&workspace.push_rel(&pkgid.path)), in_rust_path(workspace), is_git_dir(&workspace.push_rel(&pkgid.path)),
@ -374,9 +381,13 @@ fn build(&self, workspace: &Path, pkgid: &PkgId) {
// the build already. Otherwise... // the build already. Otherwise...
if !custom { if !custom {
// Find crates inside the workspace // Find crates inside the workspace
src.find_crates(); src.find_crates(self);
// Build it! // Build it!
src.build(self, cfgs); src.build(self, cfgs)
}
else {
// Just return the source workspace
workspace.clone()
} }
} }
@ -402,12 +413,15 @@ fn info(&self) {
} }
fn install(&self, workspace: &Path, id: &PkgId) { fn install(&self, workspace: &Path, id: &PkgId) {
// FIXME #7402: Use RUST_PATH to determine target dir
// Also should use workcache to not build if not necessary. // Also should use workcache to not build if not necessary.
self.build(workspace, id); let destination_workspace = self.build(workspace, id);
debug!("install: workspace = %s, id = %s", workspace.to_str(), // See #7402: This still isn't quite right yet; we want to
id.to_str()); // install to the first workspace in the RUST_PATH if there's
self.install_no_build(workspace, id); // a non-default RUST_PATH. This code installs to the same
// workspace the package was built in.
debug!("install: destination workspace = %s, id = %s",
destination_workspace.to_str(), id.to_str());
self.install_no_build(&destination_workspace, id);
} }
@ -473,7 +487,8 @@ pub fn main_args(args: &[~str]) {
let opts = ~[getopts::optflag("h"), getopts::optflag("help"), let opts = ~[getopts::optflag("h"), getopts::optflag("help"),
getopts::optflag("j"), getopts::optflag("json"), getopts::optflag("j"), getopts::optflag("json"),
getopts::optmulti("c"), getopts::optmulti("cfg"), getopts::optmulti("c"), getopts::optmulti("cfg"),
getopts::optflag("v"), getopts::optflag("version")]; getopts::optflag("v"), getopts::optflag("version"),
getopts::optflag("r"), getopts::optflag("rust-path-hack")];
let matches = &match getopts::getopts(args, opts) { let matches = &match getopts::getopts(args, opts) {
result::Ok(m) => m, result::Ok(m) => m,
result::Err(f) => { result::Err(f) => {
@ -493,6 +508,9 @@ pub fn main_args(args: &[~str]) {
return; return;
} }
let use_rust_path_hack = getopts::opt_present(matches, "r") ||
getopts::opt_present(matches, "rust-path-hack");
let mut args = matches.free.clone(); let mut args = matches.free.clone();
args.shift(); args.shift();
@ -501,33 +519,48 @@ pub fn main_args(args: &[~str]) {
return usage::general(); return usage::general();
} }
let cmd = args.shift(); let mut cmd_opt = None;
for a in args.iter() {
if !util::is_cmd(cmd) { if util::is_cmd(*a) {
return usage::general(); cmd_opt = Some(a);
} else if help { break;
return match cmd { }
~"build" => usage::build(),
~"clean" => usage::clean(),
~"do" => usage::do_cmd(),
~"info" => usage::info(),
~"install" => usage::install(),
~"list" => usage::list(),
~"prefer" => usage::prefer(),
~"test" => usage::test(),
~"uninstall" => usage::uninstall(),
~"unprefer" => usage::unprefer(),
_ => usage::general()
};
} }
let cmd = match cmd_opt {
None => return usage::general(),
Some(cmd) => if help {
return match *cmd {
~"build" => usage::build(),
~"clean" => usage::clean(),
~"do" => usage::do_cmd(),
~"info" => usage::info(),
~"install" => usage::install(),
~"list" => usage::list(),
~"prefer" => usage::prefer(),
~"test" => usage::test(),
~"uninstall" => usage::uninstall(),
~"unprefer" => usage::unprefer(),
_ => usage::general()
};
}
else {
cmd
}
};
// Pop off all flags, plus the command
let remaining_args = args.iter().skip_while(|s| !util::is_cmd(**s));
// I had to add this type annotation to get the code to typecheck
let mut remaining_args: ~[~str] = remaining_args.map(|s| (*s).clone()).collect();
remaining_args.shift();
let sroot = Some(@filesearch::get_or_default_sysroot()); let sroot = Some(@filesearch::get_or_default_sysroot());
debug!("Using sysroot: %?", sroot); debug!("Using sysroot: %?", sroot);
Ctx { Ctx {
use_rust_path_hack: use_rust_path_hack,
sysroot_opt: sroot, // Currently, only tests override this sysroot_opt: sroot, // Currently, only tests override this
json: json, json: json,
dep_cache: @mut HashMap::new() dep_cache: @mut HashMap::new()
}.run(cmd, args); }.run(*cmd, remaining_args)
} }
/** /**

View File

@ -34,6 +34,7 @@ fn datestamp(p: &Path) -> Option<libc::time_t> {
fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx { fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx {
Ctx { Ctx {
use_rust_path_hack: false,
sysroot_opt: sysroot_opt, sysroot_opt: sysroot_opt,
json: false, json: false,
dep_cache: @mut HashMap::new() dep_cache: @mut HashMap::new()
@ -70,8 +71,8 @@ fn writeFile(file_path: &Path, contents: &str) {
out.write_line(contents); out.write_line(contents);
} }
fn mk_empty_workspace(short_name: &Path, version: &Version) -> Path { fn mk_empty_workspace(short_name: &Path, version: &Version, tag: &str) -> Path {
let workspace_dir = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir"); let workspace_dir = mkdtemp(&os::tmpdir(), tag).expect("couldn't create temp dir");
mk_workspace(&workspace_dir, short_name, version); mk_workspace(&workspace_dir, short_name, version);
workspace_dir workspace_dir
} }
@ -86,7 +87,7 @@ fn mk_workspace(workspace: &Path, short_name: &Path, version: &Version) -> Path
fn mk_temp_workspace(short_name: &Path, version: &Version) -> Path { fn mk_temp_workspace(short_name: &Path, version: &Version) -> Path {
let package_dir = mk_empty_workspace(short_name, let package_dir = mk_empty_workspace(short_name,
version).push("src").push(fmt!("%s-%s", version, "temp_workspace").push("src").push(fmt!("%s-%s",
short_name.to_str(), short_name.to_str(),
version.to_str())); version.to_str()));
@ -304,29 +305,54 @@ fn create_local_package_with_custom_build_hook(pkgid: &PkgId,
} }
fn assert_lib_exists(repo: &Path, short_name: &str, _v: Version) { // ??? version? fn assert_lib_exists(repo: &Path, short_name: &str, v: Version) {
assert!(lib_exists(repo, short_name, v));
}
fn lib_exists(repo: &Path, short_name: &str, _v: Version) -> bool { // ??? version?
debug!("assert_lib_exists: repo = %s, short_name = %s", repo.to_str(), short_name); debug!("assert_lib_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
let lib = installed_library_in_workspace(short_name, repo); let lib = installed_library_in_workspace(short_name, repo);
debug!("assert_lib_exists: checking whether %? exists", lib); debug!("assert_lib_exists: checking whether %? exists", lib);
assert!(lib.is_some()); lib.is_some() && {
let libname = lib.get_ref(); let libname = lib.get_ref();
assert!(os::path_exists(libname)); os::path_exists(libname) && is_rwx(libname)
assert!(is_rwx(libname)); }
} }
fn assert_executable_exists(repo: &Path, short_name: &str) { fn assert_executable_exists(repo: &Path, short_name: &str) {
assert!(executable_exists(repo, short_name));
}
fn executable_exists(repo: &Path, short_name: &str) -> bool {
debug!("assert_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name); debug!("assert_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
let exec = target_executable_in_workspace(&PkgId::new(short_name), repo); let exec = target_executable_in_workspace(&PkgId::new(short_name), repo);
assert!(os::path_exists(&exec)); os::path_exists(&exec) && is_rwx(&exec)
assert!(is_rwx(&exec));
} }
fn assert_built_executable_exists(repo: &Path, short_name: &str) { fn assert_built_executable_exists(repo: &Path, short_name: &str) {
assert!(built_executable_exists(repo, short_name));
}
fn built_executable_exists(repo: &Path, short_name: &str) -> bool {
debug!("assert_built_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name); debug!("assert_built_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
let exec = built_executable_in_workspace(&PkgId::new(short_name), let exec = built_executable_in_workspace(&PkgId::new(short_name), repo);
repo).expect("assert_built_executable_exists failed"); exec.is_some() && {
assert!(os::path_exists(&exec)); let execname = exec.get_ref();
assert!(is_rwx(&exec)); os::path_exists(execname) && is_rwx(execname)
}
}
fn assert_built_library_exists(repo: &Path, short_name: &str) {
assert!(built_library_exists(repo, short_name));
}
fn built_library_exists(repo: &Path, short_name: &str) -> bool {
debug!("assert_built_library_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
let lib = built_library_in_workspace(&PkgId::new(short_name), repo);
lib.is_some() && {
let libname = lib.get_ref();
os::path_exists(libname) && is_rwx(libname)
}
} }
fn command_line_test_output(args: &[~str]) -> ~[~str] { fn command_line_test_output(args: &[~str]) -> ~[~str] {
@ -452,12 +478,14 @@ fn test_install_valid() {
fn test_install_invalid() { fn test_install_invalid() {
use conditions::nonexistent_package::cond; use conditions::nonexistent_package::cond;
use cond1 = conditions::missing_pkg_files::cond; use cond1 = conditions::missing_pkg_files::cond;
use cond2 = conditions::not_a_workspace::cond;
let ctxt = fake_ctxt(None); let ctxt = fake_ctxt(None);
let pkgid = fake_pkg(); let pkgid = fake_pkg();
let temp_workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir"); let temp_workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir");
let mut error_occurred = false; let mut error_occurred = false;
let mut error1_occurred = false; let mut error1_occurred = false;
let mut error2_occurred = false;
do cond1.trap(|_| { do cond1.trap(|_| {
error1_occurred = true; error1_occurred = true;
}).inside { }).inside {
@ -465,10 +493,15 @@ fn test_install_invalid() {
error_occurred = true; error_occurred = true;
temp_workspace.clone() temp_workspace.clone()
}).inside { }).inside {
ctxt.install(&temp_workspace, &pkgid); do cond2.trap(|_| {
error2_occurred = true;
temp_workspace.clone()
}).inside {
ctxt.install(&temp_workspace, &pkgid);
}
} }
} }
assert!(error_occurred && error1_occurred); assert!(error_occurred && error1_occurred && error2_occurred);
} }
// Tests above should (maybe) be converted to shell out to rustpkg, too // Tests above should (maybe) be converted to shell out to rustpkg, too
@ -1087,6 +1120,152 @@ fn multiple_workspaces() {
command_line_test_with_env([~"install", ~"bar"], &c_loc, env); command_line_test_with_env([~"install", ~"bar"], &c_loc, env);
} }
fn rust_path_hack_test(hack_flag: bool) {
/*
Make a workspace containing a pkg foo [A]
Make a second, empty workspace [B]
Set RUST_PATH to B:A
rustpkg install foo
make sure built files for foo are in B
make sure nothing gets built into A or A/../build[lib,bin]
*/
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
let rust_path = Some(~[(~"RUST_PATH",
fmt!("%s:%s", dest_workspace.to_str(), workspace.push_many(["src", "foo-0.1"]).to_str()))]);
debug!("declare -x RUST_PATH=%s:%s",
dest_workspace.to_str(), workspace.push_many(["src", "foo-0.1"]).to_str());
command_line_test_with_env(~[~"install"] + if hack_flag { ~[~"--rust-path-hack"] } else { ~[] } +
~[~"foo"], &dest_workspace, rust_path);
assert_lib_exists(&dest_workspace, "foo", NoVersion);
assert_executable_exists(&dest_workspace, "foo");
assert_built_library_exists(&dest_workspace, "foo");
assert_built_executable_exists(&dest_workspace, "foo");
assert!(!lib_exists(&workspace, "foo", NoVersion));
assert!(!executable_exists(&workspace, "foo"));
assert!(!built_library_exists(&workspace, "foo"));
assert!(!built_executable_exists(&workspace, "foo"));
}
#[test]
fn test_rust_path_can_contain_package_dirs_with_flag() {
/*
Test that the temporary hack added for bootstrapping Servo builds
works. That is: if you add $FOO/src/some_pkg to the RUST_PATH,
it will find the sources in some_pkg, build them, and install them
into the first entry in the RUST_PATH.
When the hack is removed, we should change this to a should_fail test.
*/
rust_path_hack_test(true);
}
#[test]
#[should_fail]
fn test_rust_path_can_contain_package_dirs_without_flag() {
rust_path_hack_test(false);
}
#[test]
fn rust_path_hack_cwd() {
// Same as rust_path_hack_test, but the CWD is the dir to build out of
let cwd = mkdtemp(&os::tmpdir(), "pkg_files").expect("rust_path_hack_cwd");
writeFile(&cwd.push("lib.rs"), "pub fn f() { }");
let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]);
debug!("declare -x RUST_PATH=%s", dest_workspace.to_str());
command_line_test_with_env([~"install", ~"--rust-path-hack", ~"foo"], &cwd, rust_path);
debug!("Checking that foo exists in %s", dest_workspace.to_str());
assert_lib_exists(&dest_workspace, "foo", NoVersion);
assert_built_library_exists(&dest_workspace, "foo");
assert!(!lib_exists(&cwd, "foo", NoVersion));
assert!(!built_library_exists(&cwd, "foo"));
}
#[test]
fn rust_path_hack_multi_path() {
// Same as rust_path_hack_test, but with a more complex package ID
let cwd = mkdtemp(&os::tmpdir(), "pkg_files").expect("rust_path_hack_cwd");
let subdir = cwd.push_many([~"foo", ~"bar", ~"quux"]);
assert!(os::mkdir_recursive(&subdir, U_RWX));
writeFile(&subdir.push("lib.rs"), "pub fn f() { }");
let name = ~"foo/bar/quux";
let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]);
debug!("declare -x RUST_PATH=%s", dest_workspace.to_str());
command_line_test_with_env([~"install", ~"--rust-path-hack", name.clone()], &subdir, rust_path);
debug!("Checking that %s exists in %s", name, dest_workspace.to_str());
assert_lib_exists(&dest_workspace, "quux", NoVersion);
assert_built_library_exists(&dest_workspace, name);
assert!(!lib_exists(&subdir, "quux", NoVersion));
assert!(!built_library_exists(&subdir, name));
}
#[test]
fn rust_path_hack_install_no_arg() {
// Same as rust_path_hack_cwd, but making rustpkg infer the pkg id
let cwd = mkdtemp(&os::tmpdir(), "pkg_files").expect("rust_path_hack_install_no_arg");
let source_dir = cwd.push("foo");
assert!(make_dir_rwx(&source_dir));
writeFile(&source_dir.push("lib.rs"), "pub fn f() { }");
let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]);
debug!("declare -x RUST_PATH=%s", dest_workspace.to_str());
command_line_test_with_env([~"install", ~"--rust-path-hack"], &source_dir, rust_path);
debug!("Checking that foo exists in %s", dest_workspace.to_str());
assert_lib_exists(&dest_workspace, "foo", NoVersion);
assert_built_library_exists(&dest_workspace, "foo");
assert!(!lib_exists(&source_dir, "foo", NoVersion));
assert!(!built_library_exists(&cwd, "foo"));
}
#[test]
fn rust_path_hack_build_no_arg() {
// Same as rust_path_hack_install_no_arg, but building instead of installing
let cwd = mkdtemp(&os::tmpdir(), "pkg_files").expect("rust_path_hack_build_no_arg");
let source_dir = cwd.push("foo");
assert!(make_dir_rwx(&source_dir));
writeFile(&source_dir.push("lib.rs"), "pub fn f() { }");
let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]);
debug!("declare -x RUST_PATH=%s", dest_workspace.to_str());
command_line_test_with_env([~"build", ~"--rust-path-hack"], &source_dir, rust_path);
debug!("Checking that foo exists in %s", dest_workspace.to_str());
assert_built_library_exists(&dest_workspace, "foo");
assert!(!built_library_exists(&source_dir, "foo"));
}
#[test]
#[ignore (reason = "#7402 not yet implemented")]
fn rust_path_install_target() {
let dir_for_path = mkdtemp(&os::tmpdir(),
"source_workspace").expect("rust_path_install_target failed");
let dir = mk_workspace(&dir_for_path, &Path("foo"), &NoVersion);
debug!("dir = %s", dir.to_str());
writeFile(&dir.push("main.rs"), "fn main() { let _x = (); }");
let dir_to_install_to = mkdtemp(&os::tmpdir(),
"dest_workspace").expect("rust_path_install_target failed");
let dir = dir.pop().pop();
let rust_path = Some(~[(~"RUST_PATH", fmt!("%s:%s", dir_to_install_to.to_str(),
dir.to_str()))]);
let cwd = os::getcwd();
debug!("RUST_PATH=%s:%s", dir_to_install_to.to_str(), dir.to_str());
command_line_test_with_env([~"install", ~"foo"],
&cwd,
rust_path);
assert_executable_exists(&dir_to_install_to, "foo");
}
/// Returns true if p exists and is executable /// Returns true if p exists and is executable
fn is_executable(p: &Path) -> bool { fn is_executable(p: &Path) -> bool {
use std::libc::consts::os::posix88::{S_IXUSR}; use std::libc::consts::os::posix88::{S_IXUSR};

View File

@ -452,6 +452,13 @@ fn test_is_cmd() {
} }
pub fn option_to_vec<T>(x: Option<T>) -> ~[T] {
match x {
Some(y) => ~[y],
None => ~[]
}
}
// tjc: cheesy // tjc: cheesy
fn debug_flags() -> ~[~str] { ~[] } fn debug_flags() -> ~[~str] { ~[] }
// static DEBUG_FLAGS: ~[~str] = ~[~"-Z", ~"time-passes"]; // static DEBUG_FLAGS: ~[~str] = ~[~"-Z", ~"time-passes"];

View File

@ -12,15 +12,17 @@
use std::{os,util}; use std::{os,util};
use std::path::Path; use std::path::Path;
use path_util::workspace_contains_package_id; use context::Ctx;
use path_util::{workspace_contains_package_id, find_dir_using_rust_path_hack};
use util::option_to_vec;
use package_id::PkgId; use package_id::PkgId;
use path_util::rust_path; use path_util::rust_path;
pub fn each_pkg_parent_workspace(pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool { pub fn each_pkg_parent_workspace(cx: &Ctx, pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool {
// Using the RUST_PATH, find workspaces that contain // Using the RUST_PATH, find workspaces that contain
// this package ID // this package ID
let workspaces = pkg_parent_workspaces(pkgid); let workspaces = pkg_parent_workspaces(cx, pkgid);
if workspaces.is_empty() { if workspaces.is_empty() {
// tjc: make this a condition // tjc: make this a condition
fail!("Package %s not found in any of \ fail!("Package %s not found in any of \
@ -36,10 +38,20 @@ pub fn each_pkg_parent_workspace(pkgid: &PkgId, action: &fn(&Path) -> bool) -> b
return true; return true;
} }
pub fn pkg_parent_workspaces(pkgid: &PkgId) -> ~[Path] { pub fn pkg_parent_workspaces(cx: &Ctx, pkgid: &PkgId) -> ~[Path] {
rust_path().move_iter() let rs: ~[Path] = rust_path().move_iter()
.filter(|ws| workspace_contains_package_id(pkgid, ws)) .filter(|ws| workspace_contains_package_id(pkgid, ws))
.collect() .collect();
if cx.use_rust_path_hack {
rs + option_to_vec(find_dir_using_rust_path_hack(cx, pkgid))
}
else {
rs
}
}
pub fn is_workspace(p: &Path) -> bool {
os::path_is_dir(&p.push("src"))
} }
/// Construct a workspace and package-ID name based on the current directory. /// Construct a workspace and package-ID name based on the current directory.