rustpkg: Use workcache

rustpkg now uses the workcache library to avoid recompilation.
Hooray!
This commit is contained in:
Tim Chevalier 2013-09-06 20:29:16 -07:00
parent bb30f047f6
commit 02a0fb94ee
15 changed files with 680 additions and 509 deletions

View File

@ -493,7 +493,7 @@ impl<'self, T:Send +
#[test]
fn test() {
use std::io::WriterUtil;
use std::run;
use std::{os, run};
let pth = Path("foo.c");
{
@ -501,7 +501,12 @@ fn test() {
r.unwrap().write_str("int main() { return 0; }");
}
let cx = Context::new(RWArc::new(Database::new(Path("db.json"))),
let db_path = os::self_exe_path().expect("workcache::test failed").pop().push("db.json");
if os::path_exists(&db_path) {
os::remove_file(&db_path);
}
let cx = Context::new(RWArc::new(Database::new(db_path)),
RWArc::new(Logger::new()),
Arc::new(TreeMap::new()));

View File

@ -13,49 +13,101 @@ use crate::*;
use package_id::*;
use package_source::*;
use version::Version;
use workcache_support::*;
use extra::arc::{Arc,RWArc};
use extra::workcache;
use extra::workcache::*;
use std::os;
use std::hashmap::*;
use extra::treemap::TreeMap;
/// Convenience functions intended for calling from pkg.rs
/// p is where to put the cache file for dependencies
pub fn default_ctxt(p: Path) -> BuildCtx {
new_default_ctx(new_workcache_cx(&p), p)
}
fn default_ctxt(p: @Path) -> Ctx {
Ctx {
use_rust_path_hack: false,
sysroot_opt: Some(p),
json: false,
dep_cache: @mut HashMap::new()
pub fn new_default_ctx(c: Context, p: Path) -> BuildCtx {
BuildCtx {
cx: Ctx { use_rust_path_hack: false,
sysroot_opt: p },
workcache_cx: c
}
}
pub fn build_lib(sysroot: @Path, root: Path, name: ~str, version: Version,
fn file_is_fresh(path: &str, in_hash: &str) -> bool {
in_hash == digest_file_with_date(&Path(path))
}
fn binary_is_fresh(path: &str, in_hash: &str) -> bool {
in_hash == digest_only_date(&Path(path))
}
pub fn new_workcache_cx(p: &Path) -> Context {
let db_file = p.push("rustpkg_db.json"); // ??? probably wrong
debug!("Workcache database file: %s", db_file.to_str());
let db = RWArc::new(Database::new(db_file));
let lg = RWArc::new(Logger::new());
let cfg = Arc::new(TreeMap::new());
let mut rslt: FreshnessMap = TreeMap::new();
// Set up freshness functions for every type of dependency rustpkg
// knows about
rslt.insert(~"file", file_is_fresh);
rslt.insert(~"binary", binary_is_fresh);
workcache::Context::new_with_freshness(db, lg, cfg, Arc::new(rslt))
}
pub fn build_lib(sysroot: Path, root: Path, name: ~str, version: Version,
lib: Path) {
let pkg_src = PkgSrc {
root: root,
id: PkgId{ version: version, ..PkgId::new(name)},
libs: ~[mk_crate(lib)],
mains: ~[],
tests: ~[],
benchs: ~[]
let cx = default_ctxt(sysroot);
let subroot = root.clone();
let subversion = version.clone();
let sublib = lib.clone();
do cx.workcache_cx.with_prep(name) |prep| {
let pkg_src = PkgSrc {
workspace: subroot.clone(),
start_dir: subroot.push("src").push(name),
id: PkgId{ version: subversion.clone(), ..PkgId::new(name)},
libs: ~[mk_crate(sublib.clone())],
mains: ~[],
tests: ~[],
benchs: ~[]
};
pkg_src.declare_inputs(prep);
let subcx = cx.clone();
let subsrc = pkg_src.clone();
do prep.exec |exec| {
subsrc.clone().build(exec, &subcx.clone(), ~[]);
}
};
pkg_src.build(&default_ctxt(sysroot), ~[]);
}
pub fn build_exe(sysroot: @Path, root: Path, name: ~str, version: Version, main: Path) {
let pkg_src = PkgSrc {
root: root,
id: PkgId{ version: version, ..PkgId::new(name)},
libs: ~[],
mains: ~[mk_crate(main)],
tests: ~[],
benchs: ~[]
};
pkg_src.build(&default_ctxt(sysroot), ~[]);
pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version,
main: Path) {
let cx = default_ctxt(sysroot);
let subroot = root.clone();
let submain = main.clone();
do cx.workcache_cx.with_prep(name) |prep| {
let pkg_src = PkgSrc {
workspace: subroot.clone(),
start_dir: subroot.push("src").push(name),
id: PkgId{ version: version.clone(), ..PkgId::new(name)},
libs: ~[],
mains: ~[mk_crate(submain.clone())],
tests: ~[],
benchs: ~[]
};
pkg_src.declare_inputs(prep);
let subsrc = pkg_src.clone();
let subcx = cx.clone();
do prep.exec |exec| {
subsrc.clone().build(exec, &subcx.clone(), ~[]);
}
}
}
pub fn install_lib(sysroot: @Path,
pub fn install_lib(sysroot: Path,
workspace: Path,
name: ~str,
lib_path: Path,
@ -65,23 +117,33 @@ pub fn install_lib(sysroot: @Path,
debug!("workspace = %s", workspace.to_str());
// make a PkgSrc
let pkg_id = PkgId{ version: version, ..PkgId::new(name)};
let pkg_src = PkgSrc {
root: workspace.clone(),
id: pkg_id.clone(),
libs: ~[mk_crate(lib_path)],
mains: ~[],
tests: ~[],
benchs: ~[]
};
let cx = default_ctxt(sysroot);
pkg_src.build(&cx, ~[]);
let subpath = lib_path.clone();
do cx.workcache_cx.with_prep(pkg_id.to_str()) |prep| {
let pkg_src = PkgSrc {
workspace: workspace.clone(),
start_dir: subpath.push("src").push(name),
id: pkg_id.clone(),
libs: ~[mk_crate(subpath.clone())],
mains: ~[],
tests: ~[],
benchs: ~[]
};
pkg_src.declare_inputs(prep);
let subcx = cx.clone();
let subpkg_src = pkg_src.clone();
do prep.exec |exec| {
subpkg_src.clone().build(exec, &subcx.clone(), ~[]);
}
}
cx.install_no_build(&workspace, &pkg_id);
}
pub fn install_exe(sysroot: @Path, workspace: Path, name: ~str, version: Version) {
default_ctxt(sysroot).install(&workspace, &PkgId{ version: version,
..PkgId::new(name)});
pub fn install_exe(sysroot: Path, workspace: Path, name: ~str, version: Version) {
let cx = default_ctxt(sysroot);
debug!("install_exe calling with_prep");
let pkgid = PkgId{ version: version, ..PkgId::new(name)};
cx.install(PkgSrc::new(workspace, false, pkgid));
}
fn mk_crate(p: Path) -> Crate {

View File

@ -12,11 +12,17 @@
pub use std::path::Path;
pub use package_id::PkgId;
pub use std::libc;
pub use std::libc::stat;
condition! {
pub bad_path: (Path, ~str) -> Path;
}
condition! {
pub bad_stat: (Path, ~str) -> stat;
}
condition! {
pub nonexistent_package: (PkgId, ~str) -> Path;
}

View File

@ -10,56 +10,66 @@
// Context data structure used by rustpkg
use std::hashmap::HashMap;
use std::os;
use extra::workcache;
#[deriving(Clone)]
pub struct Ctx {
// If use_rust_path_hack is true, rustpkg searches for sources
// in *package* directories that are in the RUST_PATH (for example,
// FOO/src/bar-0.1 instead of FOO). The flag doesn't affect where
// rustpkg stores build artifacts.
use_rust_path_hack: bool,
// Sysroot -- if this is None, uses rustc filesearch's
// idea of the default
sysroot_opt: Option<@Path>,
// I'm not sure what this is for
json: bool,
// Cache of hashes of things already installed
// though I'm not sure why the value is a bool
dep_cache: @mut HashMap<~str, bool>,
// The root directory containing the Rust standard libraries
sysroot_opt: Path
}
#[deriving(Clone)]
pub struct BuildCtx {
// Context for workcache
workcache_cx: workcache::Context,
// Everything else
cx: Ctx
}
impl BuildCtx {
pub fn sysroot_opt(&self) -> Path {
self.cx.sysroot_opt.clone()
}
pub fn sysroot_to_use(&self) -> Path {
self.cx.sysroot_to_use()
}
}
impl Ctx {
pub fn sysroot_opt(&self) -> Path {
self.sysroot_opt.clone()
}
}
impl Ctx {
/// Debugging
pub fn sysroot_opt_str(&self) -> ~str {
match self.sysroot_opt {
None => ~"[none]",
Some(p) => p.to_str()
}
self.sysroot_opt.to_str()
}
// Hack so that rustpkg can run either out of a rustc target dir,
// or the host dir
pub fn sysroot_to_use(&self) -> Option<@Path> {
if !in_target(self.sysroot_opt) {
self.sysroot_opt
pub fn sysroot_to_use(&self) -> Path {
if !in_target(&self.sysroot_opt) {
self.sysroot_opt.clone()
}
else {
self.sysroot_opt.map(|p| { @p.pop().pop().pop() })
self.sysroot_opt.pop().pop().pop()
}
}
}
}
/// We assume that if ../../rustc exists, then we're running
/// rustpkg from a Rust target directory. This is part of a
/// kludgy hack used to adjust the sysroot.
pub fn in_target(sysroot_opt: Option<@Path>) -> bool {
match sysroot_opt {
None => false,
Some(p) => {
debug!("Checking whether %s is in target", p.to_str());
os::path_is_dir(&p.pop().pop().push("rustc"))
}
}
pub fn in_target(sysroot_opt: &Path) -> bool {
debug!("Checking whether %s is in target", sysroot_opt.to_str());
os::path_is_dir(&sysroot_opt.pop().pop().push("rustc"))
}

View File

@ -51,12 +51,10 @@ impl PkgId {
// Did the user request a specific version?
let s = match split_version(s) {
Some((path, v)) => {
debug!("s = %s, path = %s, v = %s", s, path, v.to_str());
given_version = Some(v);
path
}
None => {
debug!("%s has no explicit version", s);
s
}
};
@ -81,7 +79,6 @@ impl PkgId {
}
};
debug!("path = %s", path.to_str());
PkgId {
path: path.clone(),
short_name: short_name.to_owned(),

View File

@ -18,14 +18,19 @@ use context::*;
use crate::Crate;
use messages::*;
use source_control::{git_clone, git_clone_general};
use path_util::{pkgid_src_in_workspace, find_dir_using_rust_path_hack, default_workspace};
use path_util::{find_dir_using_rust_path_hack, default_workspace};
use util::compile_crate;
use workspace::is_workspace;
use workcache_support;
use extra::workcache;
// An enumeration of the unpacked source of a package workspace.
// This contains a list of files found in the source workspace.
#[deriving(Clone)]
pub struct PkgSrc {
root: Path, // root of where the package source code lives
workspace: Path, // root of where the package source code lives
start_dir: Path, // dir to start looking in for packages -- normally
// this is workspace/src/id but it may be just workspace
id: PkgId,
libs: ~[Crate],
mains: ~[Crate],
@ -33,17 +38,86 @@ pub struct PkgSrc {
benchs: ~[Crate],
}
impl ToStr for PkgSrc {
fn to_str(&self) -> ~str {
fmt!("Package ID %s in start dir %s [workspace = %s]",
self.id.to_str(),
self.start_dir.to_str(), self.workspace.to_str())
}
}
condition! {
// #6009: should this be pub or not, when #8215 is fixed?
build_err: (~str) -> ();
build_err: (~str) -> ~str;
}
impl PkgSrc {
pub fn new(src_dir: &Path, id: &PkgId) -> PkgSrc {
pub fn new(workspace: Path, use_rust_path_hack: bool, id: PkgId) -> PkgSrc {
use conditions::nonexistent_package::cond;
debug!("Checking package source for package ID %s, \
workspace = %s", id.to_str(), workspace.to_str());
let mut to_try = ~[];
if use_rust_path_hack {
to_try.push(workspace.clone());
}
else {
let result = workspace.push("src").push_rel(&id.path.pop()).push(fmt!("%s-%s",
id.short_name, id.version.to_str()));
to_try.push(result);
to_try.push(workspace.push("src").push_rel(&id.path));
}
debug!("Checking dirs: %?", to_try.map(|s| s.to_str()).connect(":"));
let path = to_try.iter().find(|&d| os::path_exists(d));
let dir: Path = match path {
Some(d) => (*d).clone(),
None => {
let mut ok_d = None;
for w in to_try.iter() {
debug!("Calling fetch_git on %s", w.to_str());
let gf = PkgSrc::fetch_git(w, &id);
for p in gf.iter() {
ok_d = Some(p.clone());
break;
}
if ok_d.is_some() { break; }
}
match ok_d {
Some(d) => d,
None => {
if use_rust_path_hack {
match find_dir_using_rust_path_hack(&id) {
Some(d) => d,
None => cond.raise((id.clone(),
~"supplied path for package dir does not \
exist, and couldn't interpret it as a URL fragment"))
}
}
else {
cond.raise((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", id.to_str(), dir.to_str());
if !os::path_is_dir(&dir) {
cond.raise((id.clone(), ~"supplied path for package dir is a \
non-directory"));
}
PkgSrc {
root: (*src_dir).clone(),
id: (*id).clone(),
workspace: workspace,
start_dir: dir,
id: id,
libs: ~[],
mains: ~[],
tests: ~[],
@ -51,47 +125,12 @@ impl PkgSrc {
}
}
fn check_dir(&self, cx: &Ctx) -> Path {
use conditions::nonexistent_package::cond;
debug!("Pushing onto root: %s | %s", self.id.path.to_str(), self.root.to_str());
let dirs = pkgid_src_in_workspace(&self.id, &self.root);
debug!("Checking dirs: %?", dirs.map(|s| s.to_str()).connect(":"));
let path = dirs.iter().find(|&d| os::path_exists(d));
let dir = match path {
Some(d) => (*d).clone(),
None => {
match self.fetch_git() {
Some(d) => d,
None => {
match find_dir_using_rust_path_hack(cx, &self.id) {
Some(d) => d,
None => cond.raise((self.id.clone(),
~"supplied path for package dir does not \
exist, and couldn't interpret it as a URL fragment"))
}
}
}
}
};
debug!("For package id %s, returning %s", self.id.to_str(), dir.to_str());
if !os::path_is_dir(&dir) {
cond.raise((self.id.clone(), ~"supplied path for package dir is a \
non-directory"));
}
dir
}
/// Try interpreting self's package id as a git repository, and try
/// fetching it and caching it in a local directory. Return the cached directory
/// if this was successful, None otherwise. Similarly, if the package id
/// refers to a git repo on the local version, also check it out.
/// (right now we only support git)
pub fn fetch_git(&self) -> Option<Path> {
pub fn fetch_git(local: &Path, pkgid: &PkgId) -> Option<Path> {
use conditions::failed_to_create_temp_dir::cond;
// We use a temporary directory because if the git clone fails,
@ -103,38 +142,35 @@ impl PkgSrc {
None => cond.raise(~"Failed to create temporary directory for fetching git sources")
};
let mut local = self.root.push("src");
local = local.push(self.id.to_str());
debug!("Checking whether %s exists locally. Cwd = %s, does it? %?",
self.id.path.to_str(),
debug!("Checking whether %s (path = %s) exists locally. Cwd = %s, does it? %?",
pkgid.to_str(), pkgid.path.to_str(),
os::getcwd().to_str(),
os::path_exists(&self.id.path));
os::path_exists(&pkgid.path));
if os::path_exists(&self.id.path) {
if os::path_exists(&pkgid.path) {
debug!("%s exists locally! Cloning it into %s",
self.id.path.to_str(), local.to_str());
pkgid.path.to_str(), local.to_str());
// Ok to use local here; we know it will succeed
git_clone(&self.id.path, &local, &self.id.version);
return Some(local);
git_clone(&pkgid.path, local, &pkgid.version);
return Some(local.clone());
}
if self.id.path.components().len() < 2 {
if pkgid.path.components().len() < 2 {
// If a non-URL, don't bother trying to fetch
return None;
}
let url = fmt!("https://%s", self.id.path.to_str());
note(fmt!("Fetching package: git clone %s %s [version=%s]",
url, clone_target.to_str(), self.id.version.to_str()));
let url = fmt!("https://%s", pkgid.path.to_str());
debug!("Fetching package: git clone %s %s [version=%s]",
url, clone_target.to_str(), pkgid.version.to_str());
if git_clone_general(url, &clone_target, &self.id.version) {
if git_clone_general(url, &clone_target, &pkgid.version) {
// since the operation succeeded, move clone_target to local
if !os::rename_file(&clone_target, &local) {
if !os::rename_file(&clone_target, local) {
None
}
else {
Some(local)
Some(local.clone())
}
}
else {
@ -143,10 +179,11 @@ impl PkgSrc {
}
// If a file named "pkg.rs" in the current directory exists,
// If a file named "pkg.rs" in the start directory exists,
// return the path for it. Otherwise, None
pub fn package_script_option(&self, cwd: &Path) -> Option<Path> {
let maybe_path = cwd.push("pkg.rs");
pub fn package_script_option(&self) -> Option<Path> {
let maybe_path = self.start_dir.push("pkg.rs");
debug!("package_script_option: checking whether %s exists", maybe_path.to_str());
if os::path_exists(&maybe_path) {
Some(maybe_path)
}
@ -166,20 +203,18 @@ impl PkgSrc {
for c in p.components.slice(prefix, p.components.len()).iter() {
sub = sub.push(*c);
}
debug!("found crate %s", sub.to_str());
debug!("Will compile crate %s", sub.to_str());
cs.push(Crate::new(&sub));
}
/// Infers crates to build. Called only in the case where there
/// is no custom build logic
pub fn find_crates(&mut self, cx: &Ctx) {
pub fn find_crates(&mut self) {
use conditions::missing_pkg_files::cond;
let dir = self.check_dir(cx);
debug!("Called check_dir, I'm in %s", dir.to_str());
let prefix = dir.components.len();
debug!("Matching against %?", self.id.short_name);
do os::walk_dir(&dir) |pth| {
let prefix = self.start_dir.components.len();
debug!("Matching against %s", self.id.short_name);
do os::walk_dir(&self.start_dir) |pth| {
let maybe_known_crate_set = match pth.filename() {
Some(filename) => match filename {
"lib.rs" => Some(&mut self.libs),
@ -207,7 +242,8 @@ impl PkgSrc {
cond.raise(self.id.clone());
}
debug!("found %u libs, %u mains, %u tests, %u benchs",
debug!("In %s, found %u libs, %u mains, %u tests, %u benchs",
self.start_dir.to_str(),
self.libs.len(),
self.mains.len(),
self.tests.len(),
@ -215,68 +251,93 @@ impl PkgSrc {
}
fn build_crates(&self,
ctx: &Ctx,
src_dir: &Path,
ctx: &BuildCtx,
exec: &mut workcache::Exec,
destination_dir: &Path,
crates: &[Crate],
cfgs: &[~str],
what: OutputType) {
for crate in crates.iter() {
let path = &src_dir.push_rel(&crate.file).normalize();
note(fmt!("build_crates: compiling %s", path.to_str()));
note(fmt!("build_crates: using as workspace %s", self.root.to_str()));
let path = self.start_dir.push_rel(&crate.file).normalize();
debug!("build_crates: compiling %s", path.to_str());
let path_str = path.to_str();
let cfgs = crate.cfgs + cfgs;
let result = compile_crate(ctx,
&self.id,
path,
// compile_crate wants the destination workspace
destination_dir,
crate.flags,
crate.cfgs + cfgs,
false,
what);
if !result {
build_err::cond.raise(fmt!("build failure on %s",
path.to_str()));
}
debug!("Result of compiling %s was %?",
path.to_str(), result);
let result = {
// compile_crate should return the path of the output artifact
match compile_crate(ctx,
exec,
&self.id,
&path,
destination_dir,
crate.flags,
cfgs,
false,
what).map(|p| p.to_str()) {
Some(p) => p,
None => build_err::cond.raise(fmt!("build failure on %s",
path_str))
}
};
debug!("Result of compiling %s was %s", path_str, result);
}
}
pub fn build(&self, ctx: &Ctx, cfgs: ~[~str]) -> Path {
/// Declare all the crate files in the package source as inputs
pub fn declare_inputs(&self, prep: &mut workcache::Prep) {
let to_do = ~[self.libs.clone(), self.mains.clone(),
self.tests.clone(), self.benchs.clone()];
for cs in to_do.iter() {
for c in cs.iter() {
let path = self.start_dir.push_rel(&c.file).normalize();
debug!("Declaring input: %s", path.to_str());
prep.declare_input("file",
path.to_str(),
workcache_support::digest_file_with_date(&path.clone()));
}
}
}
// It would be better if build returned a Path, but then Path would have to derive
// Encodable.
pub fn build(&self, exec: &mut workcache::Exec, ctx: &BuildCtx, 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.root) {
debug!("%s is indeed a workspace", self.root.to_str());
self.root.clone()
let destination_workspace = if is_workspace(&self.workspace) {
debug!("%s is indeed a workspace", self.workspace.to_str());
self.workspace.clone()
}
else {
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 {
if ctx.cx.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()))
if you want to treat it as a package source",
self.workspace.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);
let libs = self.libs.clone();
let mains = self.mains.clone();
let tests = self.tests.clone();
let benchs = self.benchs.clone();
debug!("Building libs in %s, destination = %s",
destination_workspace.to_str(), destination_workspace.to_str());
self.build_crates(ctx, exec, &destination_workspace, libs, cfgs, Lib);
debug!("Building mains");
self.build_crates(ctx, &dir, &destination_workspace, self.mains, cfgs, Main);
self.build_crates(ctx, exec, &destination_workspace, mains, cfgs, Main);
debug!("Building tests");
self.build_crates(ctx, &dir, &destination_workspace, self.tests, cfgs, Test);
self.build_crates(ctx, exec, &destination_workspace, tests, cfgs, Test);
debug!("Building benches");
self.build_crates(ctx, &dir, &destination_workspace, self.benchs, cfgs, Bench);
destination_workspace
self.build_crates(ctx, exec, &destination_workspace, benchs, cfgs, Bench);
destination_workspace.to_str()
}
}

View File

@ -14,7 +14,6 @@ pub use package_id::PkgId;
pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install};
pub use version::{Version, NoVersion, split_version_general, try_parsing_version};
pub use rustc::metadata::filesearch::rust_path;
use context::Ctx;
use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
use std::os::mkdir_recursive;
@ -62,12 +61,7 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path,
let mut found = None;
do os::walk_dir(&src_dir) |p| {
debug!("=> p = %s", p.to_str());
if os::path_is_dir(p) {
debug!("p = %s, path = %s [%s]", p.to_str(), pkgid.path.to_str(),
src_dir.push_rel(&pkgid.path).to_str());
if *p == src_dir.push_rel(&pkgid.path) || {
let pf = p.filename();
do pf.iter().any |pf| {
@ -75,8 +69,6 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path,
match split_version_general(g, '-') {
None => false,
Some((ref might_match, ref vers)) => {
debug!("might_match = %s, vers = %s", *might_match,
vers.to_str());
*might_match == pkgid.short_name
&& (pkgid.version == *vers || pkgid.version == NoVersion)
}
@ -90,32 +82,15 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path,
true
};
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()) });
if found.is_some() {
debug!("Found %s in %s", pkgid.to_str(), workspace.to_str());
}
else {
debug!("Didn't find %s in %s", pkgid.to_str(), workspace.to_str());
}
found
}
/// Returns a list of possible directories
/// for <pkgid>'s source files in <workspace>.
/// Doesn't check that any of them exist.
/// (for example, try both with and without the version)
pub fn pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> ~[Path] {
let mut results = ~[];
let result = workspace.push("src").push(fmt!("%s-%s",
pkgid.path.to_str(), pkgid.version.to_str()));
results.push(result);
results.push(workspace.push("src").push_rel(&pkgid.path));
results
}
/// Returns a src for pkgid that does exist -- None if none of them do
pub fn first_pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
let rs = pkgid_src_in_workspace(pkgid, workspace);
do rs.iter().find |&p| {
os::path_exists(p)
}.map(|p| (**p).clone())
}
/// Figure out what the executable name for <pkgid> in <workspace>'s build
/// directory is, and if the file exists, return it.
pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
@ -402,10 +377,7 @@ fn dir_has_file(dir: &Path, file: &str) -> bool {
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;
}
pub fn find_dir_using_rust_path_hack(p: &PkgId) -> Option<Path> {
let rp = rust_path();
for dir in rp.iter() {
debug!("In find_dir_using_rust_path_hack: checking dir %s", dir.to_str());

View File

@ -24,8 +24,9 @@ extern mod syntax;
use std::{io, os, result, run, str};
pub use std::path::Path;
use std::hashmap::HashMap;
use extra::workcache;
use extra::arc::RWArc;
use rustc::driver::{driver, session};
use rustc::metadata::filesearch;
use rustc::metadata::filesearch::rust_path;
@ -33,15 +34,16 @@ use extra::{getopts};
use syntax::{ast, diagnostic};
use util::*;
use messages::*;
use path_util::{build_pkg_id_in_workspace, first_pkgid_src_in_workspace};
use path_util::build_pkg_id_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 workspace::{each_pkg_parent_workspace, pkg_parent_workspaces, cwd_to_workspace};
use context::Ctx;
use context::{BuildCtx, Ctx};
use package_id::PkgId;
use package_source::PkgSrc;
use workcache_support::{discover_outputs, digest_only_date};
pub mod api;
mod conditions;
@ -59,6 +61,7 @@ mod target;
mod tests;
mod util;
mod version;
pub mod workcache_support;
mod workspace;
pub mod usage;
@ -69,11 +72,8 @@ pub mod usage;
struct PkgScript<'self> {
/// Uniquely identifies this package
id: &'self PkgId,
// Used to have this field: deps: ~[(~str, Option<~str>)]
// but I think it shouldn't be stored here
/// The contents of the package script: either a file path,
/// or a string containing the text of the input
input: driver::input,
/// File path for the package script
input: Path,
/// The session to use *only* for compiling the custom
/// build script
sess: session::Session,
@ -104,7 +104,7 @@ impl<'self> PkgScript<'self> {
crate_type: session::bin_crate,
.. (*session::basic_options()).clone()
};
let input = driver::file_input(script);
let input = driver::file_input(script.clone());
let sess = driver::build_session(options, diagnostic::emit);
let cfg = driver::build_configuration(sess);
let crate = driver::phase_1_parse_input(sess, cfg.clone(), &input);
@ -115,7 +115,7 @@ impl<'self> PkgScript<'self> {
PkgScript {
id: id,
input: input,
input: script,
sess: sess,
cfg: cfg,
crate: crate,
@ -127,22 +127,24 @@ impl<'self> PkgScript<'self> {
/// is the command to pass to it (e.g., "build", "clean", "install")
/// Returns a pair of an exit code and list of configs (obtained by
/// calling the package script's configs() function if it exists
// FIXME (#4432): Use workcache to only compile the script when changed
fn run_custom(&self, sysroot: @Path) -> (~[~str], ExitCode) {
fn run_custom(&self, exec: &mut workcache::Exec, sysroot: &Path) -> (~[~str], ExitCode) {
let sess = self.sess;
debug!("Working directory = %s", self.build_dir.to_str());
// Collect together any user-defined commands in the package script
let crate = util::ready_crate(sess, self.crate);
debug!("Building output filenames with script name %s",
driver::source_name(&self.input));
driver::source_name(&driver::file_input(self.input.clone())));
let exe = self.build_dir.push(~"pkg" + util::exe_suffix());
util::compile_crate_from_input(&self.input,
exec,
&self.build_dir,
sess,
crate);
debug!("Running program: %s %s %s", exe.to_str(),
sysroot.to_str(), "install");
// Discover the output
exec.discover_output("binary", exe.to_str(), digest_only_date(&exe));
// FIXME #7401 should support commands besides `install`
let status = run::process_status(exe.to_str(), [sysroot.to_str(), ~"install"]);
if status != 0 {
@ -162,47 +164,65 @@ impl<'self> PkgScript<'self> {
fn hash(&self) -> ~str {
self.id.hash()
}
}
pub trait CtxMethods {
fn run(&self, cmd: &str, args: ~[~str]);
fn do_cmd(&self, _cmd: &str, _pkgname: &str);
fn build_from_src(&self, pkg_src: PkgSrc);
/// Returns the destination workspace
fn build(&self, workspace: &Path, pkgid: &PkgId) -> Path;
fn build(&self, exec: &mut workcache::Exec, pkg_src: PkgSrc) -> Path;
fn clean(&self, workspace: &Path, id: &PkgId);
fn info(&self);
fn install(&self, workspace: &Path, id: &PkgId);
fn install_no_build(&self, workspace: &Path, id: &PkgId);
fn install(&self, src: PkgSrc) -> (~[Path], ~[(~str, ~str)]);
/// Returns a list of installed files
fn install_no_build(&self, workspace: &Path, id: &PkgId) -> ~[Path];
fn prefer(&self, _id: &str, _vers: Option<~str>);
fn test(&self);
fn uninstall(&self, _id: &str, _vers: Option<~str>);
fn unprefer(&self, _id: &str, _vers: Option<~str>);
}
impl CtxMethods for Ctx {
impl CtxMethods for BuildCtx {
fn build_from_src(&self, pkg_src: PkgSrc) {
let tag = pkg_src.id.to_str();
debug!("package source = %s", pkg_src.to_str());
do self.workcache_cx.with_prep(tag) |prep| {
let subsrc = pkg_src.clone();
let subself = self.clone();
declare_package_script_dependency(prep, &subsrc);
pkg_src.declare_inputs(prep);
do prep.exec |exec| {
subself.build(exec, subsrc.clone());
}
}
}
fn run(&self, cmd: &str, args: ~[~str]) {
match cmd {
"build" => {
if args.len() < 1 {
match cwd_to_workspace() {
None if self.use_rust_path_hack => {
None if self.cx.use_rust_path_hack => {
let cwd = os::getcwd();
self.build(&cwd, &PkgId::new(cwd.components[cwd.components.len() - 1]));
let pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]);
self.build_from_src(PkgSrc::new(cwd, true, pkgid));
}
None => { usage::build(); return; }
Some((ws, pkgid)) => { self.build(&ws, &pkgid); }
Some((ws, pkgid)) => {
self.build_from_src(PkgSrc::new(ws, false, pkgid));
}
}
}
else {
// The package id is presumed to be the first command-line
// argument
let pkgid = PkgId::new(args[0].clone());
do each_pkg_parent_workspace(self, &pkgid) |workspace| {
do each_pkg_parent_workspace(&self.cx, &pkgid) |workspace| {
debug!("found pkg %s in workspace %s, trying to build",
pkgid.to_str(), workspace.to_str());
self.build(workspace, &pkgid);
let pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone());
self.build_from_src(pkg_src);
true
};
}
@ -236,35 +256,40 @@ impl CtxMethods for Ctx {
self.info();
}
"install" => {
if args.len() < 1 {
if args.len() < 1 {
match cwd_to_workspace() {
None if self.use_rust_path_hack => {
None if self.cx.use_rust_path_hack => {
let cwd = os::getcwd();
self.install(&cwd,
&PkgId::new(cwd.components[cwd.components.len() - 1]));
let inferred_pkgid =
PkgId::new(cwd.components[cwd.components.len() - 1]);
self.install(PkgSrc::new(cwd, true, inferred_pkgid));
}
None => { usage::install(); return; }
Some((ws, pkgid)) => self.install(&ws, &pkgid),
}
Some((ws, pkgid)) => {
let pkg_src = PkgSrc::new(ws, false, pkgid);
self.install(pkg_src);
}
}
}
else {
// The package id is presumed to be the first command-line
// argument
let pkgid = PkgId::new(args[0]);
let workspaces = pkg_parent_workspaces(self, &pkgid);
let workspaces = pkg_parent_workspaces(&self.cx, &pkgid);
debug!("package ID = %s, 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], &pkgid);
src.fetch_git();
self.install(&rp[0], &pkgid);
let src = PkgSrc::new(rp[0].clone(), false, pkgid.clone());
self.install(src);
}
else {
do each_pkg_parent_workspace(self, &pkgid) |workspace| {
self.install(workspace, &pkgid);
true
for workspace in workspaces.iter() {
let src = PkgSrc::new(workspace.clone(),
self.cx.use_rust_path_hack,
pkgid.clone());
self.install(src);
};
}
}
@ -299,7 +324,7 @@ impl CtxMethods for Ctx {
else {
let rp = rust_path();
assert!(!rp.is_empty());
do each_pkg_parent_workspace(self, &pkgid) |workspace| {
do each_pkg_parent_workspace(&self.cx, &pkgid) |workspace| {
path_util::uninstall_package_from(workspace, &pkgid);
note(fmt!("Uninstalled package %s (was installed in %s)",
pkgid.to_str(), workspace.to_str()));
@ -325,44 +350,44 @@ impl CtxMethods for Ctx {
/// 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 {
fn build(&self, exec: &mut workcache::Exec, pkg_src: PkgSrc) -> Path {
let pkg_src = &mut pkg_src.clone(); // :-o
let workspace = pkg_src.workspace.clone();
let pkgid = pkg_src.id.clone();
debug!("build: workspace = %s (in Rust path? %? is git dir? %? \
pkgid = %s", workspace.to_str(),
in_rust_path(workspace), is_git_dir(&workspace.push_rel(&pkgid.path)),
pkgid.to_str());
let src_dir = first_pkgid_src_in_workspace(pkgid, workspace);
pkgid = %s pkgsrc start_dir = %s", workspace.to_str(),
in_rust_path(&workspace), is_git_dir(&workspace.push_rel(&pkgid.path)),
pkgid.to_str(), pkg_src.start_dir.to_str());
// If workspace isn't in the RUST_PATH, and it's a git repo,
// then clone it into the first entry in RUST_PATH, and repeat
debug!("%? %? %s", in_rust_path(workspace),
is_git_dir(&workspace.push_rel(&pkgid.path)),
workspace.to_str());
if !in_rust_path(workspace) && is_git_dir(&workspace.push_rel(&pkgid.path)) {
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 default_ws = default_workspace();
debug!("Calling build recursively with %? and %?", default_ws.to_str(),
pkgid.to_str());
return self.build(&default_ws, pkgid);
return self.build(exec, PkgSrc::new(default_ws, false, pkgid.clone()));
}
// Create the package source
let mut src = PkgSrc::new(workspace, pkgid);
debug!("Package src = %?", src);
// Is there custom build logic? If so, use it
let pkg_src_dir = src_dir;
let mut custom = false;
debug!("Package source directory = %?", pkg_src_dir);
let cfgs = match pkg_src_dir.chain_ref(|p| src.package_script_option(p)) {
debug!("Package source directory = %s", pkg_src.to_str());
let opt = pkg_src.package_script_option();
debug!("Calling pkg_script_option on %?", opt);
let cfgs = match pkg_src.package_script_option() {
Some(package_script_path) => {
let sysroot = self.sysroot_to_use().expect("custom build needs a sysroot");
let pscript = PkgScript::parse(sysroot,
package_script_path,
workspace,
pkgid);
let (cfgs, hook_result) = pscript.run_custom(sysroot);
let sysroot = self.sysroot_to_use();
let (cfgs, hook_result) = {
let pscript = PkgScript::parse(@sysroot.clone(),
package_script_path.clone(),
&workspace.clone(),
&pkgid);
pscript.run_custom(exec, &sysroot)
};
debug!("Command return code = %?", hook_result);
if hook_result != 0 {
fail!("Error running custom build command")
@ -381,9 +406,10 @@ impl CtxMethods for Ctx {
// the build already. Otherwise...
if !custom {
// Find crates inside the workspace
src.find_crates(self);
pkg_src.find_crates();
// Build it!
src.build(self, cfgs)
let rs_path = pkg_src.build(exec, self, cfgs);
Path(rs_path)
}
else {
// Just return the source workspace
@ -412,20 +438,47 @@ impl CtxMethods for Ctx {
fail!("info not yet implemented");
}
fn install(&self, workspace: &Path, id: &PkgId) {
// Also should use workcache to not build if not necessary.
let destination_workspace = self.build(workspace, id);
// See #7402: This still isn't quite right yet; we want to
// install to the first workspace in the RUST_PATH if there's
// a non-default RUST_PATH. This code installs to the same
// workspace the package was built in.
debug!("install: destination workspace = %s, id = %s",
destination_workspace.to_str(), id.to_str());
self.install_no_build(&destination_workspace, id);
/// Returns a pair. First component is a list of installed paths,
/// second is a list of declared and discovered inputs
fn install(&self, pkg_src: PkgSrc) -> (~[Path], ~[(~str, ~str)]) {
let id = &pkg_src.id;
let installed_files = RWArc::new(~[]);
let inputs = RWArc::new(~[]);
// FIXME #7402: Use RUST_PATH to determine target dir
let f: &fn(&mut workcache::Prep) = |prep| {
let sub_inputs = inputs.clone();
let sub_files = installed_files.clone();
let subsrc = pkg_src.clone();
let subself = self.clone();
let id_str = id.to_str();
let sub_id = id.clone();
sub_inputs.write(|r| *r = prep.lookup_declared_inputs().map(|v|
{ (~"file", (*v).clone()) }));
do prep.exec |exec| {
let destination_workspace = subself.build(exec, subsrc.clone()).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.
debug!("install: destination workspace = %s, id = %s",
destination_workspace, id_str);
let result = subself.install_no_build(&Path(destination_workspace), &sub_id);
debug!("install: id = %s, about to call discover_outputs, %?",
id_str, result.to_str());
discover_outputs(exec, result.clone());
sub_files.write(|r| { *r = result.clone(); });
sub_inputs.write(|r| { *r = *r + exec.lookup_discovered_inputs() });
}
};
self.workcache_cx.with_prep(id.to_str(), |p| pkg_src.declare_inputs(p));
self.workcache_cx.with_prep(id.to_str(), f);
(installed_files.unwrap(), inputs.unwrap())
}
fn install_no_build(&self, workspace: &Path, id: &PkgId) {
fn install_no_build(&self, workspace: &Path, id: &PkgId) -> ~[Path] {
use conditions::copy_failed::cond;
// Now copy stuff into the install dirs
@ -439,12 +492,15 @@ impl CtxMethods for Ctx {
target_exec.to_str(), target_lib,
maybe_executable, maybe_library);
let mut outputs = ~[];
for exec in maybe_executable.iter() {
debug!("Copying: %s -> %s", exec.to_str(), target_exec.to_str());
if !(os::mkdir_recursive(&target_exec.dir_path(), U_RWX) &&
os::copy_file(exec, &target_exec)) {
cond.raise(((*exec).clone(), target_exec.clone()));
}
outputs.push(target_exec.clone());
}
for lib in maybe_library.iter() {
let target_lib = target_lib.clone().expect(fmt!("I built %s but apparently \
@ -455,7 +511,9 @@ impl CtxMethods for Ctx {
os::copy_file(lib, &target_lib)) {
cond.raise(((*lib).clone(), target_lib.clone()));
}
outputs.push(target_lib.clone());
}
outputs
}
fn prefer(&self, _id: &str, _vers: Option<~str>) {
@ -476,7 +534,6 @@ impl CtxMethods for Ctx {
}
}
pub fn main() {
io::println("WARNING: The Rust package manager is experimental and may be unstable");
let args = os::args();
@ -485,7 +542,6 @@ pub fn main() {
pub fn main_args(args: &[~str]) {
let opts = ~[getopts::optflag("h"), getopts::optflag("help"),
getopts::optflag("j"), getopts::optflag("json"),
getopts::optmulti("c"), getopts::optmulti("cfg"),
getopts::optflag("v"), getopts::optflag("version"),
getopts::optflag("r"), getopts::optflag("rust-path-hack")];
@ -499,8 +555,6 @@ pub fn main_args(args: &[~str]) {
};
let help = getopts::opt_present(matches, "h") ||
getopts::opt_present(matches, "help");
let json = getopts::opt_present(matches, "j") ||
getopts::opt_present(matches, "json");
if getopts::opt_present(matches, "v") ||
getopts::opt_present(matches, "version") {
@ -512,7 +566,6 @@ pub fn main_args(args: &[~str]) {
getopts::opt_present(matches, "rust-path-hack");
let mut args = matches.free.clone();
args.shift();
if (args.len() < 1) {
@ -553,13 +606,15 @@ pub fn main_args(args: &[~str]) {
// I had to add this type annotation to get the code to typecheck
let mut remaining_args: ~[~str] = remaining_args.map(|s| (*s).clone()).collect();
remaining_args.shift();
let sroot = Some(@filesearch::get_or_default_sysroot());
debug!("Using sysroot: %?", sroot);
Ctx {
use_rust_path_hack: use_rust_path_hack,
sysroot_opt: sroot, // Currently, only tests override this
json: json,
dep_cache: @mut HashMap::new()
let sroot = filesearch::get_or_default_sysroot();
debug!("Using sysroot: %s", sroot.to_str());
debug!("Will store workcache in %s", default_workspace().to_str());
BuildCtx {
cx: Ctx {
use_rust_path_hack: use_rust_path_hack,
sysroot_opt: sroot, // Currently, only tests override this
},
workcache_cx: api::default_ctxt(default_workspace()).workcache_cx // ???
}.run(*cmd, remaining_args)
}
@ -581,3 +636,11 @@ pub fn work_dir() -> Path {
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(),
workcache_support::digest_file_with_date(p)),
None => ()
}
}

View File

@ -11,16 +11,6 @@
use path_util::{installed_library_in_workspace, rust_path};
use version::Version;
/// If a library with path `p` matching pkg_id's name exists under sroot_opt,
/// return Some(p). Return None if there's no such path or if sroot_opt is None.
pub fn find_library_in_search_path(sroot_opt: Option<@Path>, short_name: &str) -> Option<Path> {
do sroot_opt.chain |sroot| {
debug!("Will search for a library with short name %s in \
%s", short_name, (sroot.push("lib")).to_str());
installed_library_in_workspace(short_name, sroot)
}
}
/// If some workspace `p` in the RUST_PATH contains a package matching short_name,
/// return Some(p) (returns the first one of there are multiple matches.) Return
/// None if there's no such path.

View File

@ -10,10 +10,13 @@
// rustpkg unit tests
use context::Ctx;
use std::hashmap::HashMap;
use std::{io, libc, os, run, str};
use context::{BuildCtx, Ctx};
use std::{io, libc, os, run, str, task};
use extra::arc::Arc;
use extra::arc::RWArc;
use extra::tempfile::mkdtemp;
use extra::workcache::{Context, Database, Logger};
use extra::treemap::TreeMap;
use std::run::ProcessOutput;
use installed_packages::list_installed_packages;
use package_id::{PkgId};
@ -26,18 +29,23 @@ use path_util::{target_executable_in_workspace, target_test_in_workspace,
use rustc::metadata::filesearch::rust_path;
use rustc::driver::driver::host_triple;
use target::*;
use package_source::PkgSrc;
/// Returns the last-modified date as an Option
fn datestamp(p: &Path) -> Option<libc::time_t> {
p.stat().map(|stat| stat.st_mtime)
}
fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx {
Ctx {
use_rust_path_hack: false,
sysroot_opt: sysroot_opt,
json: false,
dep_cache: @mut HashMap::new()
fn fake_ctxt(sysroot_opt: Path, workspace: &Path) -> BuildCtx {
let bcx = Context::new(RWArc::new(Database::new(workspace.push("rustpkg_db.json"))),
RWArc::new(Logger::new()),
Arc::new(TreeMap::new()));
BuildCtx {
workcache_cx: bcx,
cx: Ctx {
use_rust_path_hack: false,
sysroot_opt: sysroot_opt
}
}
}
@ -388,7 +396,7 @@ fn lib_output_file_name(workspace: &Path, parent: &str, short_name: &str) -> Pat
}
fn output_file_name(workspace: &Path, short_name: &str) -> Path {
workspace.push(fmt!("%s%s", short_name, os::EXE_SUFFIX))
workspace.push("build").push(short_name).push(fmt!("%s%s", short_name, os::EXE_SUFFIX))
}
fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
@ -401,12 +409,11 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
if run::process_output("touch", [p.to_str()]).status != 0 {
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
}
break;
}
}
}
/// Add a blank line at the end
/// Add a comment at the end
fn frob_source_file(workspace: &Path, pkgid: &PkgId) {
use conditions::bad_path::cond;
let pkg_src_dir = workspace.push("src").push(pkgid.to_str());
@ -423,7 +430,7 @@ fn frob_source_file(workspace: &Path, pkgid: &PkgId) {
let w = io::file_writer(p, &[io::Append]);
match w {
Err(s) => { let _ = cond.raise((p.clone(), fmt!("Bad path: %s", s))); }
Ok(w) => w.write_line("")
Ok(w) => w.write_line("/* hi */")
}
}
None => fail!(fmt!("frob_source_file failed to find a source file in %s",
@ -450,12 +457,13 @@ fn test_install_valid() {
let sysroot = test_sysroot();
debug!("sysroot = %s", sysroot.to_str());
let ctxt = fake_ctxt(Some(@sysroot));
let temp_pkg_id = fake_pkg();
let temp_workspace = mk_temp_workspace(&temp_pkg_id.path, &NoVersion).pop().pop();
let ctxt = fake_ctxt(sysroot, &temp_workspace);
debug!("temp_workspace = %s", temp_workspace.to_str());
// should have test, bench, lib, and main
ctxt.install(&temp_workspace, &temp_pkg_id);
let src = PkgSrc::new(temp_workspace.clone(), false, temp_pkg_id.clone());
ctxt.install(src);
// Check that all files exist
let exec = target_executable_in_workspace(&temp_pkg_id, &temp_workspace);
debug!("exec = %s", exec.to_str());
@ -476,32 +484,19 @@ fn test_install_valid() {
#[test]
fn test_install_invalid() {
use conditions::nonexistent_package::cond;
use cond1 = conditions::missing_pkg_files::cond;
use cond2 = conditions::not_a_workspace::cond;
let ctxt = fake_ctxt(None);
let sysroot = test_sysroot();
let pkgid = fake_pkg();
let temp_workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir");
let mut error_occurred = false;
let mut error1_occurred = false;
let mut error2_occurred = false;
do cond1.trap(|_| {
error1_occurred = true;
}).inside {
do cond.trap(|_| {
error_occurred = true;
temp_workspace.clone()
}).inside {
do cond2.trap(|_| {
error2_occurred = true;
temp_workspace.clone()
}).inside {
ctxt.install(&temp_workspace, &pkgid);
}
}
}
assert!(error_occurred && error1_occurred && error2_occurred);
let ctxt = fake_ctxt(sysroot, &temp_workspace);
// Uses task::try because of #9001
let result = do task::try {
let pkg_src = PkgSrc::new(temp_workspace.clone(), false, pkgid.clone());
ctxt.install(pkg_src);
};
// Not the best test -- doesn't test that we failed in the right way.
// Best we can do for now.
assert!(result == Err(()));
}
// Tests above should (maybe) be converted to shell out to rustpkg, too
@ -898,7 +893,6 @@ fn install_check_duplicates() {
}
#[test]
#[ignore(reason = "Workcache not yet implemented -- see #7075")]
fn no_rebuilding() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
@ -912,24 +906,28 @@ fn no_rebuilding() {
}
#[test]
#[ignore(reason = "Workcache not yet implemented -- see #7075")]
fn no_rebuilding_dep() {
let p_id = PkgId::new("foo");
let dep_id = PkgId::new("bar");
let workspace = create_local_package_with_dep(&p_id, &dep_id);
command_line_test([~"build", ~"foo"], &workspace);
let bar_date = datestamp(&lib_output_file_name(&workspace,
let bar_date_1 = datestamp(&lib_output_file_name(&workspace,
".rust",
"bar"));
let foo_date = datestamp(&output_file_name(&workspace, "foo"));
assert!(bar_date < foo_date);
let foo_date_1 = datestamp(&output_file_name(&workspace, "foo"));
frob_source_file(&workspace, &p_id);
command_line_test([~"build", ~"foo"], &workspace);
let bar_date_2 = datestamp(&lib_output_file_name(&workspace,
".rust",
"bar"));
let foo_date_2 = datestamp(&output_file_name(&workspace, "foo"));
assert_eq!(bar_date_1, bar_date_2);
assert!(foo_date_1 < foo_date_2);
assert!(foo_date_1 > bar_date_1);
}
// n.b. The following two tests are ignored; they worked "accidentally" before,
// when the behavior was "always rebuild libraries" (now it's "never rebuild
// libraries if they already exist"). They can be un-ignored once #7075 is done.
#[test]
#[ignore(reason = "Workcache not yet implemented -- see #7075")]
fn do_rebuild_dep_dates_change() {
let p_id = PkgId::new("foo");
let dep_id = PkgId::new("bar");
@ -946,7 +944,6 @@ fn do_rebuild_dep_dates_change() {
}
#[test]
#[ignore(reason = "Workcache not yet implemented -- see #7075")]
fn do_rebuild_dep_only_contents_change() {
let p_id = PkgId::new("foo");
let dep_id = PkgId::new("bar");

View File

@ -9,6 +9,7 @@
// except according to those terms.
use std::os;
use extra::workcache;
use rustc::driver::{driver, session};
use extra::getopts::groups::getopts;
use syntax::ast_util::*;
@ -18,12 +19,13 @@ use syntax::{ast, attr, codemap, diagnostic, fold};
use syntax::attr::AttrMetaMethods;
use rustc::back::link::output_type_exe;
use rustc::driver::session::{lib_crate, bin_crate};
use context::{Ctx, in_target};
use context::{in_target, BuildCtx};
use package_id::PkgId;
use search::{find_library_in_search_path, find_installed_library_in_rust_path};
use path_util::{target_library_in_workspace, U_RWX};
use package_source::PkgSrc;
use path_util::{installed_library_in_workspace, U_RWX};
pub use target::{OutputType, Main, Lib, Bench, Test};
use version::NoVersion;
use workcache_support::{digest_file_with_date, digest_only_date};
// It would be nice to have the list of commands in just one place -- for example,
// you could update the match in rustpkg.rc but forget to update this list. I think
@ -151,15 +153,15 @@ pub fn ready_crate(sess: session::Session,
@fold.fold_crate(crate)
}
// FIXME (#4432): Use workcache to only compile when needed
pub fn compile_input(ctxt: &Ctx,
pub fn compile_input(ctxt: &BuildCtx,
exec: &mut workcache::Exec,
pkg_id: &PkgId,
in_file: &Path,
workspace: &Path,
flags: &[~str],
cfgs: &[~str],
opt: bool,
what: OutputType) -> bool {
what: OutputType) -> Option<Path> {
assert!(in_file.components.len() > 1);
let input = driver::file_input((*in_file).clone());
@ -173,7 +175,7 @@ pub fn compile_input(ctxt: &Ctx,
debug!("flags: %s", flags.connect(" "));
debug!("cfgs: %s", cfgs.connect(" "));
debug!("out_dir = %s", out_dir.to_str());
debug!("compile_input's sysroot = %s", ctxt.sysroot_opt().to_str());
let crate_type = match what {
Lib => lib_crate,
@ -191,19 +193,19 @@ pub fn compile_input(ctxt: &Ctx,
driver::optgroups()).unwrap();
// Hack so that rustpkg can run either out of a rustc target dir,
// or the host dir
let sysroot_to_use = if !in_target(ctxt.sysroot_opt) {
ctxt.sysroot_opt
let sysroot_to_use = @if !in_target(&ctxt.sysroot_opt()) {
ctxt.sysroot_opt()
}
else {
ctxt.sysroot_opt.map(|p| { @p.pop().pop().pop() })
ctxt.sysroot_opt().pop().pop().pop()
};
debug!("compile_input's sysroot = %?", ctxt.sysroot_opt_str());
debug!("sysroot_to_use = %?", sysroot_to_use);
debug!("compile_input's sysroot = %s", ctxt.sysroot_opt().to_str());
debug!("sysroot_to_use = %s", sysroot_to_use.to_str());
let options = @session::options {
crate_type: crate_type,
optimize: if opt { session::Aggressive } else { session::No },
test: what == Test || what == Bench,
maybe_sysroot: sysroot_to_use,
maybe_sysroot: Some(sysroot_to_use),
addl_lib_search_paths: @mut (~[out_dir.clone()]),
// output_type should be conditional
output_type: output_type_exe, // Use this to get a library? That's weird
@ -228,11 +230,11 @@ pub fn compile_input(ctxt: &Ctx,
// `extern mod` directives.
let cfg = driver::build_configuration(sess);
let mut crate = driver::phase_1_parse_input(sess, cfg.clone(), &input);
crate = driver::phase_2_configure_and_expand(sess, cfg, crate);
crate = driver::phase_2_configure_and_expand(sess, cfg.clone(), crate);
// Not really right. Should search other workspaces too, and the installed
// database (which doesn't exist yet)
find_and_install_dependencies(ctxt, sess, workspace, crate,
find_and_install_dependencies(ctxt, sess, exec, workspace, crate,
|p| {
debug!("a dependency: %s", p.to_str());
// Pass the directory containing a dependency
@ -269,8 +271,7 @@ pub fn compile_input(ctxt: &Ctx,
debug!("calling compile_crate_from_input, workspace = %s,
building_library = %?", out_dir.to_str(), sess.building_library);
compile_crate_from_input(&input, &out_dir, sess, crate);
true
compile_crate_from_input(in_file, exec, &out_dir, sess, crate)
}
// Should use workcache to avoid recompiling when not necessary
@ -278,17 +279,19 @@ pub fn compile_input(ctxt: &Ctx,
// If crate_opt is present, then finish compilation. If it's None, then
// call compile_upto and return the crate
// also, too many arguments
pub fn compile_crate_from_input(input: &driver::input,
pub fn compile_crate_from_input(input: &Path,
exec: &mut workcache::Exec,
// should be of the form <workspace>/build/<pkg id's path>
out_dir: &Path,
sess: session::Session,
crate: @ast::Crate) {
crate: @ast::Crate) -> Option<Path> {
debug!("Calling build_output_filenames with %s, building library? %?",
out_dir.to_str(), sess.building_library);
// bad copy
debug!("out_dir = %s", out_dir.to_str());
let outputs = driver::build_output_filenames(input, &Some(out_dir.clone()), &None,
let outputs = driver::build_output_filenames(&driver::file_input(input.clone()),
&Some(out_dir.clone()), &None,
crate.attrs, sess);
debug!("Outputs are out_filename: %s and obj_filename: %s and output type = %?",
@ -304,8 +307,13 @@ pub fn compile_crate_from_input(input: &driver::input,
&analysis,
outputs);
driver::phase_5_run_llvm_passes(sess, &translation, outputs);
if driver::stop_after_phase_5(sess) { return; }
if driver::stop_after_phase_5(sess) { return Some(outputs.out_filename); }
driver::phase_6_link_output(sess, &translation, outputs);
// Register dependency on the source file
exec.discover_input("file", input.to_str(), digest_file_with_date(input));
Some(outputs.out_filename)
}
#[cfg(windows)]
@ -318,76 +326,90 @@ pub fn exe_suffix() -> ~str { ~".exe" }
pub fn exe_suffix() -> ~str { ~"" }
// Called by build_crates
// FIXME (#4432): Use workcache to only compile when needed
pub fn compile_crate(ctxt: &Ctx, pkg_id: &PkgId,
pub fn compile_crate(ctxt: &BuildCtx,
exec: &mut workcache::Exec,
pkg_id: &PkgId,
crate: &Path, workspace: &Path,
flags: &[~str], cfgs: &[~str], opt: bool,
what: OutputType) -> bool {
what: OutputType) -> Option<Path> {
debug!("compile_crate: crate=%s, workspace=%s", crate.to_str(), workspace.to_str());
debug!("compile_crate: short_name = %s, flags =...", pkg_id.to_str());
for fl in flags.iter() {
debug!("+++ %s", *fl);
}
compile_input(ctxt, pkg_id, crate, workspace, flags, cfgs, opt, what)
compile_input(ctxt, exec, pkg_id, crate, workspace, flags, cfgs, opt, what)
}
/// Collect all `extern mod` directives in `c`, then
/// try to install their targets, failing if any target
/// can't be found.
pub fn find_and_install_dependencies(ctxt: &Ctx,
pub fn find_and_install_dependencies(ctxt: &BuildCtx,
sess: session::Session,
exec: &mut workcache::Exec,
workspace: &Path,
c: &ast::Crate,
save: @fn(Path)
) {
// :-(
debug!("In find_and_install_dependencies...");
let my_workspace = (*workspace).clone();
let my_ctxt = *ctxt;
do c.each_view_item() |vi: &ast::view_item| {
debug!("Finding and installing dependencies...");
do c.each_view_item |vi| {
debug!("A view item!");
match vi.node {
// ignore metadata, I guess
ast::view_item_extern_mod(lib_ident, path_opt, _, _) => {
match my_ctxt.sysroot_opt {
Some(ref x) => debug!("*** sysroot: %s", x.to_str()),
None => debug!("No sysroot given")
};
let lib_name = match path_opt { // ???
let lib_name = match path_opt {
Some(p) => p, None => sess.str_of(lib_ident) };
match find_library_in_search_path(my_ctxt.sysroot_opt, lib_name) {
Some(installed_path) => {
match installed_library_in_workspace(lib_name, &ctxt.sysroot_opt()) {
Some(ref installed_path) => {
debug!("It exists: %s", installed_path.to_str());
// Say that [path for c] has a discovered dependency on
// installed_path
// For binary files, we only hash the datestamp, not the contents.
// I'm not sure what the right thing is.
// Now we know that this crate has a discovered dependency on
// installed_path
exec.discover_input("binary", installed_path.to_str(),
digest_only_date(installed_path));
}
None => {
// FIXME #8711: need to parse version out of path_opt
match find_installed_library_in_rust_path(lib_name, &NoVersion) {
Some(installed_path) => {
debug!("Found library %s, not rebuilding it",
installed_path.to_str());
// Once workcache is implemented, we'll actually check
// whether or not the library at installed_path is fresh
save(installed_path.pop());
debug!("Trying to install library %s, rebuilding it",
lib_name.to_str());
// Try to install it
let pkg_id = PkgId::new(lib_name);
let (outputs_disc, inputs_disc) =
ctxt.install(PkgSrc::new(workspace.clone(), false, pkg_id));
debug!("Installed %s, returned %? dependencies and \
%? transitive dependencies",
lib_name, outputs_disc.len(), inputs_disc.len());
for dep in outputs_disc.iter() {
debug!("Discovering a binary input: %s", dep.to_str());
exec.discover_input("binary", dep.to_str(),
digest_only_date(dep));
}
for &(ref what, ref dep) in inputs_disc.iter() {
if *what == ~"file" {
exec.discover_input(*what, *dep,
digest_file_with_date(&Path(*dep)));
}
None => {
debug!("Trying to install library %s, rebuilding it",
lib_name.to_str());
// Try to install it
let pkg_id = PkgId::new(lib_name);
my_ctxt.install(&my_workspace, &pkg_id);
// Also, add an additional search path
debug!("let installed_path...")
let installed_path = target_library_in_workspace(&pkg_id,
&my_workspace).pop();
debug!("Great, I installed %s, and it's in %s",
lib_name, installed_path.to_str());
save(installed_path);
}
else if *what == ~"binary" {
exec.discover_input(*what, *dep,
digest_only_date(&Path(*dep)));
}
else {
fail!("Bad kind: %s", *what);
}
}
// Also, add an additional search path
let installed_library =
installed_library_in_workspace(lib_name, workspace)
.expect( fmt!("rustpkg failed to install dependency %s",
lib_name));
let install_dir = installed_library.pop();
debug!("Installed %s into %s", lib_name, install_dir.to_str());
save(install_dir);
}
}
}
}
}}
// Ignore `use`s
_ => ()
}

View File

@ -99,12 +99,14 @@ pub fn try_getting_local_version(local_path: &Path) -> Option<Version> {
let rustpath = rust_path();
for rp in rustpath.iter() {
let local_path = rp.push_rel(local_path);
debug!("in try_getting_local_version");
let git_dir = local_path.push(".git");
if !os::path_is_dir(&git_dir) {
loop;
}
let outp = run::process_output("git",
[fmt!("--git-dir=%s", local_path.push(".git").to_str()),
~"tag", ~"-l"]);
[fmt!("--git-dir=%s", git_dir.to_str()), ~"tag", ~"-l"]);
debug!("git --git-dir=%s tag -l ~~~> %?", local_path.push(".git").to_str(), outp.status);
debug!("git --git-dir=%s tag -l ~~~> %?", git_dir.to_str(), outp.status);
if outp.status != 0 {
loop;
@ -129,9 +131,7 @@ pub fn try_getting_local_version(local_path: &Path) -> Option<Version> {
/// and the most recent tag in that repo denotes a version, return it;
/// otherwise, `None`
pub fn try_getting_version(remote_path: &Path) -> Option<Version> {
debug!("try_getting_version: %s", remote_path.to_str());
if is_url_like(remote_path) {
debug!("Trying to fetch its sources..");
let tmp_dir = mkdtemp(&os::tmpdir(),
"test").expect("try_getting_version: couldn't create temp dir");
debug!("(to get version) executing {git clone https://%s %s}",
@ -218,14 +218,11 @@ pub fn split_version<'a>(s: &'a str) -> Option<(&'a str, Version)> {
pub fn split_version_general<'a>(s: &'a str, sep: char) -> Option<(&'a str, Version)> {
match s.rfind(sep) {
Some(i) => {
debug!("in %s, i = %?", s, i);
let path = s.slice(0, i);
debug!("path = %s", path);
// n.b. for now, assuming an exact revision is intended, not a SemVer
Some((path, ExactRevision(s.slice(i + 1, s.len()).to_owned())))
}
None => {
debug!("%s doesn't look like an explicit-version thing", s);
None
}
}

View File

@ -0,0 +1,58 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use extra::sha1::Sha1;
use extra::digest::Digest;
use extra::workcache;
use std::io;
/// Hashes the file contents along with the last-modified time
pub fn digest_file_with_date(path: &Path) -> ~str {
use conditions::bad_path::cond;
use cond1 = conditions::bad_stat::cond;
let mut sha = ~Sha1::new();
let s = io::read_whole_file_str(path);
match s {
Ok(s) => {
(*sha).input_str(s);
let st = match path.stat() {
Some(st) => st,
None => cond1.raise((path.clone(), fmt!("Couldn't get file access time")))
};
(*sha).input_str(st.st_mtime.to_str());
(*sha).result_str()
}
Err(e) => cond.raise((path.clone(), fmt!("Couldn't read file: %s", e))).to_str()
}
}
/// Hashes only the last-modified time
pub fn digest_only_date(path: &Path) -> ~str {
use cond = conditions::bad_stat::cond;
let mut sha = ~Sha1::new();
let st = match path.stat() {
Some(st) => st,
None => cond.raise((path.clone(), fmt!("Couldn't get file access time")))
};
(*sha).input_str(st.st_mtime.to_str());
(*sha).result_str()
}
/// Adds multiple discovered outputs
pub fn discover_outputs(e: &mut workcache::Exec, outputs: ~[Path]) {
debug!("Discovering %? outputs", outputs.len());
for p in outputs.iter() {
debug!("Discovering output! %s", p.to_str());
// For now, assume that all discovered outputs are binaries
e.discover_output("binary", p.to_str(), digest_only_date(p));
}
}

View File

@ -43,7 +43,7 @@ pub fn pkg_parent_workspaces(cx: &Ctx, pkgid: &PkgId) -> ~[Path] {
.filter(|ws| workspace_contains_package_id(pkgid, ws))
.collect();
if cx.use_rust_path_hack {
rs + option_to_vec(find_dir_using_rust_path_hack(cx, pkgid))
rs + option_to_vec(find_dir_using_rust_path_hack(pkgid))
}
else {
rs

View File

@ -14,7 +14,7 @@ use ast_util;
use codemap::{Span, dummy_sp};
use opt_vec;
use parse::token;
use visit::{SimpleVisitor, SimpleVisitorVisitor, Visitor};
use visit::{SimpleVisitor, Visitor};
use visit;
use std::hashmap::HashMap;
@ -684,94 +684,25 @@ pub fn walk_pat(pat: @Pat, it: &fn(@Pat) -> bool) -> bool {
}
pub trait EachViewItem {
fn each_view_item(&self, f: @fn(&ast::view_item) -> bool) -> bool;
fn each_view_item(&self, f: &fn(&ast::view_item) -> bool) -> bool;
}
struct EachViewItemData {
callback: @fn(&ast::view_item) -> bool,
struct EachViewItemData<'self> {
callback: &'self fn(&ast::view_item) -> bool,
}
impl SimpleVisitor for EachViewItemData {
fn visit_mod(&mut self, _: &_mod, _: Span, _: NodeId) {
// XXX: Default method.
}
fn visit_view_item(&mut self, view_item: &view_item) {
impl<'self> Visitor<()> for EachViewItemData<'self> {
fn visit_view_item(&mut self, view_item: &ast::view_item, _: ()) {
let _ = (self.callback)(view_item);
}
fn visit_foreign_item(&mut self, _: @foreign_item) {
// XXX: Default method.
}
fn visit_item(&mut self, _: @item) {
// XXX: Default method.
}
fn visit_local(&mut self, _: @Local) {
// XXX: Default method.
}
fn visit_block(&mut self, _: &Block) {
// XXX: Default method.
}
fn visit_stmt(&mut self, _: @Stmt) {
// XXX: Default method.
}
fn visit_arm(&mut self, _: &Arm) {
// XXX: Default method.
}
fn visit_pat(&mut self, _: @Pat) {
// XXX: Default method.
}
fn visit_decl(&mut self, _: @Decl) {
// XXX: Default method.
}
fn visit_expr(&mut self, _: @Expr) {
// XXX: Default method.
}
fn visit_expr_post(&mut self, _: @Expr) {
// XXX: Default method.
}
fn visit_ty(&mut self, _: &Ty) {
// XXX: Default method.
}
fn visit_generics(&mut self, _: &Generics) {
// XXX: Default method.
}
fn visit_fn(&mut self,
_: &visit::fn_kind,
_: &fn_decl,
_: &Block,
_: Span,
_: NodeId) {
// XXX: Default method.
}
fn visit_ty_method(&mut self, _: &TypeMethod) {
// XXX: Default method.
}
fn visit_trait_method(&mut self, _: &trait_method) {
// XXX: Default method.
}
fn visit_struct_def(&mut self,
_: @struct_def,
_: Ident,
_: &Generics,
_: NodeId) {
// XXX: Default method.
}
fn visit_struct_field(&mut self, _: @struct_field) {
// XXX: Default method.
}
fn visit_struct_method(&mut self, _: @method) {
// XXX: Default method.
}
}
impl EachViewItem for ast::Crate {
fn each_view_item(&self, f: @fn(&ast::view_item) -> bool) -> bool {
let data = @mut EachViewItemData {
fn each_view_item(&self, f: &fn(&ast::view_item) -> bool) -> bool {
let mut visit = EachViewItemData {
callback: f,
};
let visitor = @mut SimpleVisitorVisitor {
simple_visitor: data as @mut SimpleVisitor,
};
visit::walk_crate(visitor, self, ());
visit::walk_crate(&mut visit, self, ());
true
}
}