diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs index 727bbcb30b4..e1092458ffa 100644 --- a/src/librustpkg/api.rs +++ b/src/librustpkg/api.rs @@ -12,6 +12,7 @@ use context::*; use crate::*; use package_id::*; use package_source::*; +use target::*; use version::Version; use workcache_support::*; @@ -63,56 +64,40 @@ pub fn new_workcache_context(p: &Path) -> workcache::Context { pub fn build_lib(sysroot: Path, root: Path, name: ~str, version: Version, lib: Path) { let cx = default_context(sysroot); - let subroot = root.clone(); - let subversion = version.clone(); - let sublib = lib.clone(); - do cx.workcache_context.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())], + let pkg_src = PkgSrc { + workspace: root.clone(), + start_dir: root.push("src").push(name), + id: PkgId{ version: version, ..PkgId::new(name)}, + // n.b. This assumes the package only has one crate + libs: ~[mk_crate(lib)], mains: ~[], tests: ~[], benchs: ~[] }; - pkg_src.declare_inputs(prep); - let subcx = cx.clone(); - let subsrc = pkg_src.clone(); - do prep.exec |exec| { - subsrc.build(exec, &subcx.clone(), ~[]); - } - }; + pkg_src.build(&cx, ~[]); } pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version, main: Path) { let cx = default_context(sysroot); - let subroot = root.clone(); - let submain = main.clone(); - do cx.workcache_context.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(), ~[]); - } - } + let pkg_src = PkgSrc { + workspace: root.clone(), + start_dir: root.push("src").push(name), + id: PkgId{ version: version, ..PkgId::new(name)}, + libs: ~[], + // n.b. This assumes the package only has one crate + mains: ~[mk_crate(main)], + tests: ~[], + benchs: ~[] + }; + + pkg_src.build(&cx, ~[]); } pub fn install_pkg(sysroot: Path, workspace: Path, name: ~str, version: Version) { let cx = default_context(sysroot); let pkgid = PkgId{ version: version, ..PkgId::new(name)}; - cx.install(PkgSrc::new(workspace, false, pkgid)); + cx.install(PkgSrc::new(workspace, false, pkgid), &Everything); } fn mk_crate(p: Path) -> Crate { diff --git a/src/librustpkg/exit_codes.rs b/src/librustpkg/exit_codes.rs new file mode 100644 index 00000000000..484f6bdcaec --- /dev/null +++ b/src/librustpkg/exit_codes.rs @@ -0,0 +1,11 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub static copy_failed_code: int = 65; diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs index bc2fcdd7fe9..52b986cb6e7 100644 --- a/src/librustpkg/package_id.rs +++ b/src/librustpkg/package_id.rs @@ -108,6 +108,12 @@ impl PkgId { } } + // This is the workcache function name for the *installed* + // binaries for this package (as opposed to the built ones, + // which are per-crate). + pub fn install_tag(&self) -> ~str { + fmt!("install(%s)", self.to_str()) + } } struct Prefixes { diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index b5ded6f3faf..4bf647b011d 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -22,6 +22,7 @@ use path_util::{find_dir_using_rust_path_hack, default_workspace, make_dir_rwx_r use util::compile_crate; use workspace::is_workspace; use workcache_support; +use workcache_support::crate_tag; use extra::workcache; // An enumeration of the unpacked source of a package workspace. @@ -231,7 +232,7 @@ impl PkgSrc { p.filestem().map_default(false, |p| { p == &self.id.short_name.as_slice() }) } - fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { + pub fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { assert!(p.components.len() > prefix); let mut sub = Path(""); for c in p.components.slice(prefix, p.components.len()).iter() { @@ -286,7 +287,6 @@ impl PkgSrc { fn build_crates(&self, ctx: &BuildContext, - exec: &mut workcache::Exec, destination_dir: &Path, crates: &[Crate], cfgs: &[~str], @@ -297,25 +297,40 @@ impl PkgSrc { let path_str = path.to_str(); let cfgs = crate.cfgs + cfgs; - let result = - // compile_crate should return the path of the output artifact - compile_crate(ctx, - exec, - &self.id, - &path, - destination_dir, - crate.flags, - cfgs, - false, - what).to_str(); - debug!("Result of compiling %s was %s", path_str, result); + do ctx.workcache_context.with_prep(crate_tag(&path)) |prep| { + debug!("Building crate %s, declaring it as an input", path.to_str()); + prep.declare_input("file", path.to_str(), + workcache_support::digest_file_with_date(&path)); + let subpath = path.clone(); + let subcfgs = cfgs.clone(); + let subpath_str = path_str.clone(); + let subcx = ctx.clone(); + let id = self.id.clone(); + let sub_dir = destination_dir.clone(); + let sub_flags = crate.flags.clone(); + do prep.exec |exec| { + let result = compile_crate(&subcx, + exec, + &id, + &subpath, + &sub_dir, + sub_flags, + subcfgs, + false, + what).to_str(); + debug!("Result of compiling %s was %s", subpath_str, result); + result + } + }; } } /// Declare all the crate files in the package source as inputs + /// (to the package) pub fn declare_inputs(&self, prep: &mut workcache::Prep) { let to_do = ~[self.libs.clone(), self.mains.clone(), self.tests.clone(), self.benchs.clone()]; + debug!("In declare inputs, self = %s", self.to_str()); for cs in to_do.iter() { for c in cs.iter() { let path = self.start_dir.push_rel(&c.file).normalize(); @@ -330,7 +345,6 @@ impl PkgSrc { // 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, build_context: &BuildContext, cfgs: ~[~str]) -> ~str { use conditions::not_a_workspace::cond; @@ -360,13 +374,23 @@ impl PkgSrc { let benchs = self.benchs.clone(); debug!("Building libs in %s, destination = %s", destination_workspace.to_str(), destination_workspace.to_str()); - self.build_crates(build_context, exec, &destination_workspace, libs, cfgs, Lib); + self.build_crates(build_context, &destination_workspace, libs, cfgs, Lib); debug!("Building mains"); - self.build_crates(build_context, exec, &destination_workspace, mains, cfgs, Main); + self.build_crates(build_context, &destination_workspace, mains, cfgs, Main); debug!("Building tests"); - self.build_crates(build_context, exec, &destination_workspace, tests, cfgs, Test); + self.build_crates(build_context, &destination_workspace, tests, cfgs, Test); debug!("Building benches"); - self.build_crates(build_context, exec, &destination_workspace, benchs, cfgs, Bench); + self.build_crates(build_context, &destination_workspace, benchs, cfgs, Bench); destination_workspace.to_str() } + + /// Debugging + pub fn dump_crates(&self) { + let crate_sets = [&self.libs, &self.mains, &self.tests, &self.benchs]; + for crate_set in crate_sets.iter() { + for c in crate_set.iter() { + debug!("Built crate: %s", c.file.to_str()) + } + } + } } diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 2a0cf5fea34..7cd30c7af9e 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -22,11 +22,10 @@ extern mod extra; extern mod rustc; extern mod syntax; -use std::{io, os, result, run, str}; +use std::{io, os, result, run, str, task}; pub use std::path::Path; use extra::workcache; -use extra::arc::RWArc; use rustc::driver::{driver, session}; use rustc::metadata::filesearch; use rustc::metadata::filesearch::rust_path; @@ -45,12 +44,16 @@ use context::{Context, BuildContext, LLVMAssemble, LLVMCompileBitcode}; use package_id::PkgId; use package_source::PkgSrc; -use workcache_support::{discover_outputs, digest_only_date}; +use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench}; +// use workcache_support::{discover_outputs, digest_only_date}; +use workcache_support::digest_only_date; +use exit_codes::copy_failed_code; pub mod api; mod conditions; mod context; mod crate; +mod exit_codes; mod installed_packages; mod messages; mod package_id; @@ -172,19 +175,18 @@ impl<'self> PkgScript<'self> { 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, exec: &mut workcache::Exec, pkg_src: PkgSrc) -> Path; + fn build(&self, pkg_src: &mut PkgSrc, what: &WhatToBuild) -> Path; fn clean(&self, workspace: &Path, id: &PkgId); fn info(&self); /// Returns a pair. First component is a list of installed paths, /// second is a list of declared and discovered inputs - fn install(&self, src: PkgSrc) -> (~[Path], ~[(~str, ~str)]); + fn install(&self, src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]); /// Returns a list of installed files fn install_no_build(&self, source_workspace: &Path, target_workspace: &Path, - id: &PkgId) -> ~[Path]; + id: &PkgId) -> ~[~str]; fn prefer(&self, _id: &str, _vers: Option<~str>); fn test(&self); fn uninstall(&self, _id: &str, _vers: Option<~str>); @@ -193,20 +195,6 @@ pub trait CtxMethods { } impl CtxMethods for BuildContext { - 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_context.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" => { @@ -215,11 +203,13 @@ impl CtxMethods for BuildContext { None if self.context.use_rust_path_hack => { let cwd = os::getcwd(); let pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]); - self.build_from_src(PkgSrc::new(cwd, true, pkgid)); + let mut pkg_src = PkgSrc::new(cwd, true, pkgid); + self.build(&mut pkg_src, &Everything); } None => { usage::build(); return; } Some((ws, pkgid)) => { - self.build_from_src(PkgSrc::new(ws, false, pkgid)); + let mut pkg_src = PkgSrc::new(ws, false, pkgid); + self.build(&mut pkg_src, &Everything); } } } @@ -230,8 +220,8 @@ impl CtxMethods for BuildContext { do each_pkg_parent_workspace(&self.context, &pkgid) |workspace| { debug!("found pkg %s in workspace %s, trying to build", pkgid.to_str(), workspace.to_str()); - let pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone()); - self.build_from_src(pkg_src); + let mut pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone()); + self.build(&mut pkg_src, &Everything); true }; } @@ -271,12 +261,12 @@ impl CtxMethods for BuildContext { let cwd = os::getcwd(); let inferred_pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]); - self.install(PkgSrc::new(cwd, true, inferred_pkgid)); + self.install(PkgSrc::new(cwd, true, inferred_pkgid), &Everything); } None => { usage::install(); return; } Some((ws, pkgid)) => { let pkg_src = PkgSrc::new(ws, false, pkgid); - self.install(pkg_src); + self.install(pkg_src, &Everything); } } } @@ -291,14 +281,14 @@ impl CtxMethods for BuildContext { let rp = rust_path(); assert!(!rp.is_empty()); let src = PkgSrc::new(rp[0].clone(), false, pkgid.clone()); - self.install(src); + self.install(src, &Everything); } else { for workspace in workspaces.iter() { let src = PkgSrc::new(workspace.clone(), self.context.use_rust_path_hack, pkgid.clone()); - self.install(src); + self.install(src, &Everything); }; } } @@ -366,7 +356,9 @@ impl CtxMethods for BuildContext { /// 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, exec: &mut workcache::Exec, mut pkg_src: PkgSrc) -> Path { + /// what_to_build says: "Just build the lib.rs file in one subdirectory, + /// don't walk anything recursively." Or else, everything. + fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) -> Path { let workspace = pkg_src.workspace.clone(); let pkgid = pkg_src.id.clone(); @@ -384,7 +376,7 @@ impl CtxMethods for BuildContext { let default_ws = default_workspace(); debug!("Calling build recursively with %? and %?", default_ws.to_str(), pkgid.to_str()); - return self.build(exec, PkgSrc::new(default_ws, false, pkgid.clone())); + return self.build(&mut PkgSrc::new(default_ws, false, pkgid.clone()), what_to_build); } // Is there custom build logic? If so, use it @@ -395,12 +387,21 @@ impl CtxMethods for BuildContext { let cfgs = match pkg_src.package_script_option() { Some(package_script_path) => { 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) + let (cfgs, hook_result) = + do self.workcache_context.with_prep(package_script_path.to_str()) |prep| { + let sub_sysroot = sysroot.clone(); + let package_script_path_clone = package_script_path.clone(); + let sub_ws = workspace.clone(); + let sub_id = pkgid.clone(); + declare_package_script_dependency(prep, &*pkg_src); + do prep.exec |exec| { + let pscript = PkgScript::parse(@sub_sysroot.clone(), + package_script_path_clone.clone(), + &sub_ws, + &sub_id); + + pscript.run_custom(exec, &sub_sysroot) + } }; debug!("Command return code = %?", hook_result); if hook_result != 0 { @@ -419,10 +420,31 @@ impl CtxMethods for BuildContext { // If there was a package script, it should have finished // the build already. Otherwise... if !custom { - // Find crates inside the workspace - pkg_src.find_crates(); + match what_to_build { + // Find crates inside the workspace + &Everything => pkg_src.find_crates(), + // Don't infer any crates -- just build the one that was requested + &JustOne(ref p) => { + // We expect that p is relative to the package source's start directory, + // so check that assumption + debug!("JustOne: p = %s", p.to_str()); + assert!(os::path_exists(&pkg_src.start_dir.push_rel(p))); + if is_lib(p) { + PkgSrc::push_crate(&mut pkg_src.libs, 0, p); + } else if is_main(p) { + PkgSrc::push_crate(&mut pkg_src.mains, 0, p); + } else if is_test(p) { + PkgSrc::push_crate(&mut pkg_src.tests, 0, p); + } else if is_bench(p) { + PkgSrc::push_crate(&mut pkg_src.benchs, 0, p); + } else { + warn(fmt!("Not building any crates for dependency %s", p.to_str())); + return workspace.clone(); + } + } + } // Build it! - let rs_path = pkg_src.build(exec, self, cfgs); + let rs_path = pkg_src.build(self, cfgs); Path(rs_path) } else { @@ -452,56 +474,54 @@ impl CtxMethods for BuildContext { fail!("info not yet implemented"); } - fn install(&self, pkg_src: PkgSrc) -> (~[Path], ~[(~str, ~str)]) { + fn install(&self, mut pkg_src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]) { - let id = &pkg_src.id; + let id = pkg_src.id.clone(); - let installed_files = RWArc::new(~[]); - let inputs = RWArc::new(~[]); - // FIXME #7402: Use RUST_PATH to determine target dir - self.workcache_context.with_prep(id.to_str(), |p| pkg_src.declare_inputs(p)); - do self.workcache_context.with_prep(id.to_str()) |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. - let actual_workspace = if path_util::user_set_rust_path() { - default_workspace() - } - else { - Path(destination_workspace) - }; - debug!("install: destination workspace = %s, id = %s, installing to %s", - destination_workspace, id_str, actual_workspace.to_str()); - let result = subself.install_no_build(&Path(destination_workspace), - &actual_workspace, - &sub_id); - debug!("install: id = %s, about to call discover_outputs, %?", - id_str, result.to_str()); + let mut installed_files = ~[]; + let inputs = ~[]; - discover_outputs(exec, result.clone()); - sub_files.write(|r| { *r = result.clone(); }); - sub_inputs.write(|r| { *r = *r + exec.lookup_discovered_inputs() }); - note(fmt!("Installed package %s to %s", id_str, actual_workspace.to_str())); + // workcache only knows about *crates*. Building a package + // just means inferring all the crates in it, then building each one. + let destination_workspace = self.build(&mut pkg_src, what).to_str(); + + let to_do = ~[pkg_src.libs.clone(), pkg_src.mains.clone(), + pkg_src.tests.clone(), pkg_src.benchs.clone()]; + debug!("In declare inputs for %s", id.to_str()); + for cs in to_do.iter() { + for c in cs.iter() { + let path = pkg_src.start_dir.push_rel(&c.file).normalize(); + debug!("Recording input: %s", path.to_str()); + installed_files.push(path); } + } + // See #7402: This still isn't quite right yet; we want to + // install to the first workspace in the RUST_PATH if there's + // a non-default RUST_PATH. This code installs to the same + // workspace the package was built in. + let actual_workspace = if path_util::user_set_rust_path() { + default_workspace() + } + else { + Path(destination_workspace) }; - (installed_files.unwrap(), inputs.unwrap()) + debug!("install: destination workspace = %s, id = %s, installing to %s", + destination_workspace, id.to_str(), actual_workspace.to_str()); + let result = self.install_no_build(&Path(destination_workspace), + &actual_workspace, + &id).map(|s| Path(*s)); + debug!("install: id = %s, about to call discover_outputs, %?", + id.to_str(), result.to_str()); + installed_files = installed_files + result; + note(fmt!("Installed package %s to %s", id.to_str(), actual_workspace.to_str())); + (installed_files, inputs) } + // again, working around lack of Encodable for Path fn install_no_build(&self, source_workspace: &Path, target_workspace: &Path, - id: &PkgId) -> ~[Path] { + id: &PkgId) -> ~[~str] { use conditions::copy_failed::cond; // Now copy stuff into the install dirs @@ -511,32 +531,59 @@ impl CtxMethods for BuildContext { let target_lib = maybe_library.map(|_p| target_library_in_workspace(id, target_workspace)); debug!("target_exec = %s target_lib = %? \ - maybe_executable = %? maybe_library = %?", + maybe_executable = %? maybe_library = %?", target_exec.to_str(), target_lib, maybe_executable, maybe_library); - let mut outputs = ~[]; + do self.workcache_context.with_prep(id.install_tag()) |prep| { + for ee in maybe_executable.iter() { + prep.declare_input("binary", + ee.to_str(), + workcache_support::digest_only_date(ee)); + } + for ll in maybe_library.iter() { + prep.declare_input("binary", + ll.to_str(), + workcache_support::digest_only_date(ll)); + } + let subex = maybe_executable.clone(); + let sublib = maybe_library.clone(); + let sub_target_ex = target_exec.clone(); + let sub_target_lib = target_lib.clone(); - 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())); + do prep.exec |exe_thing| { + let mut outputs = ~[]; + + for exec in subex.iter() { + debug!("Copying: %s -> %s", exec.to_str(), sub_target_ex.to_str()); + if !(os::mkdir_recursive(&sub_target_ex.dir_path(), U_RWX) && + os::copy_file(exec, &sub_target_ex)) { + cond.raise(((*exec).clone(), sub_target_ex.clone())); + } + exe_thing.discover_output("binary", + sub_target_ex.to_str(), + workcache_support::digest_only_date(&sub_target_ex)); + outputs.push(sub_target_ex.to_str()); + } + for lib in sublib.iter() { + let target_lib = sub_target_lib + .clone().expect(fmt!("I built %s but apparently \ + didn't install it!", lib.to_str())); + let target_lib = target_lib + .pop().push(lib.filename().expect("weird target lib")); + debug!("Copying: %s -> %s", lib.to_str(), sub_target_lib.to_str()); + if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) && + os::copy_file(lib, &target_lib)) { + cond.raise(((*lib).clone(), target_lib.clone())); + } + exe_thing.discover_output("binary", + target_lib.to_str(), + workcache_support::digest_only_date(&target_lib)); + outputs.push(target_lib.to_str()); + } + outputs } - outputs.push(target_exec.clone()); } - for lib in maybe_library.iter() { - let target_lib = target_lib.clone().expect(fmt!("I built %s but apparently \ - didn't install it!", lib.to_str())); - let target_lib = target_lib.pop().push(lib.filename().expect("weird target lib")); - debug!("Copying: %s -> %s", lib.to_str(), target_lib.to_str()); - if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) && - os::copy_file(lib, &target_lib)) { - cond.raise(((*lib).clone(), target_lib.clone())); - } - outputs.push(target_lib.clone()); - } - outputs } fn prefer(&self, _id: &str, _vers: Option<~str>) { @@ -726,15 +773,27 @@ pub fn main_args(args: &[~str]) { debug!("Using sysroot: %s", sroot.to_str()); debug!("Will store workcache in %s", default_workspace().to_str()); - BuildContext { - context: Context { - cfgs: cfgs, - rustc_flags: rustc_flags, - use_rust_path_hack: use_rust_path_hack, - sysroot: sroot, // Currently, only tests override this - }, - workcache_context: api::default_context(default_workspace()).workcache_context - }.run(*cmd, remaining_args) + + let rm_args = remaining_args.clone(); + let sub_cmd = cmd.clone(); + // Wrap the rest in task::try in case of a condition failure in a task + let result = do task::try { + BuildContext { + context: Context { + cfgs: cfgs.clone(), + rustc_flags: rustc_flags.clone(), + use_rust_path_hack: use_rust_path_hack, + sysroot: sroot.clone(), // Currently, only tests override this + }, + workcache_context: api::default_context(default_workspace()).workcache_context + }.run(sub_cmd, rm_args.clone()) + }; + // FIXME #9262: This is using the same error code for all errors, + // and at least one test case succeeds if rustpkg returns copy_failed_code, + // when actually, it might set the exit code for that even if a different + // unhandled condition got raised. + if result.is_err() { os::set_exit_status(copy_failed_code); } + } /** diff --git a/src/librustpkg/target.rs b/src/librustpkg/target.rs index 03c2f5a4fe4..9d3ad1f39a7 100644 --- a/src/librustpkg/target.rs +++ b/src/librustpkg/target.rs @@ -16,8 +16,45 @@ pub enum OutputType { Main, Lib, Bench, Test } #[deriving(Eq)] pub enum Target { - // In-place build + /// In-place build Build, - // Install to bin/ or lib/ dir + /// Install to bin/ or lib/ dir Install } + +#[deriving(Eq, Clone)] +pub enum WhatToBuild { + /// Build just one lib.rs file in `path`, which is relative to the active workspace's src/ dir + JustOne(Path), + /// Build everything + Everything +} + +pub fn is_lib(p: &Path) -> bool { + file_is(p, "lib") +} + +pub fn is_main(p: &Path) -> bool { + file_is(p, "main") +} + +pub fn is_test(p: &Path) -> bool { + file_is(p, "test") +} + +pub fn is_bench(p: &Path) -> bool { + file_is(p, "bench") +} + +fn file_is(p: &Path, stem: &str) -> bool { + match p.filestem() { + Some(s) if s == stem => true, + _ => false + } +} + +pub fn lib_name_of(p: &Path) -> Path { + p.push("lib.rs") +} + +pub static lib_crate_filename: &'static str = "lib.rs"; diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 952931fa97a..918cc366799 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -34,11 +34,7 @@ use rustc::driver::driver::{build_session, build_session_options, host_triple, o use syntax::diagnostic; use target::*; use package_source::PkgSrc; - -/// Returns the last-modified date as an Option -fn datestamp(p: &Path) -> Option { - p.stat().map(|stat| stat.st_mtime) -} +use util::datestamp; fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext { let context = workcache::Context::new( @@ -224,18 +220,26 @@ fn rustpkg_exec() -> Path { } fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput { - command_line_test_with_env(args, cwd, None).expect("Command line test failed") + match command_line_test_with_env(args, cwd, None) { + Success(r) => r, + _ => fail!("Command line test failed") + } } -fn command_line_test_partial(args: &[~str], cwd: &Path) -> Option { +fn command_line_test_partial(args: &[~str], cwd: &Path) -> ProcessResult { command_line_test_with_env(args, cwd, None) } +enum ProcessResult { + Success(ProcessOutput), + Fail(int) // exit code +} + /// Runs `rustpkg` (based on the directory that this executable was /// invoked from) with the given arguments, in the given working directory. /// Returns the process's output. fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>) - -> Option { + -> ProcessResult { let cmd = rustpkg_exec().to_str(); let env_str = match env { Some(ref pairs) => pairs.map(|&(ref k, ref v)| { fmt!("%s=%s", *k, *v) }).connect(","), @@ -266,10 +270,10 @@ to make sure the command succeeded debug!("Command %s %? failed with exit code %?; its output was {{{ %s }}}", cmd, args, output.status, str::from_utf8(output.output) + str::from_utf8(output.error)); - None + Fail(output.status) } else { - Some(output) + Success(output) } } @@ -410,8 +414,11 @@ fn command_line_test_output(args: &[~str]) -> ~[~str] { fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~str] { let mut result = ~[]; - let p_output = command_line_test_with_env(args, - &os::getcwd(), Some(env)).expect("Command-line test failed"); + let p_output = match command_line_test_with_env(args, + &os::getcwd(), Some(env)) { + Fail(_) => fail!("Command-line test failed"), + Success(r) => r + }; let test_output = str::from_utf8(p_output.output); for s in test_output.split_iter('\n') { result.push(s.to_owned()); @@ -420,9 +427,9 @@ fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~ } // assumes short_name and path are one and the same -- I should fix -fn lib_output_file_name(workspace: &Path, parent: &str, short_name: &str) -> Path { - debug!("lib_output_file_name: given %s and parent %s and short name %s", - workspace.to_str(), parent, short_name); +fn lib_output_file_name(workspace: &Path, short_name: &str) -> Path { + debug!("lib_output_file_name: given %s and short name %s", + workspace.to_str(), short_name); library_in_workspace(&Path(short_name), short_name, Build, @@ -450,19 +457,18 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) { } /// Add a comment at the end -fn frob_source_file(workspace: &Path, pkgid: &PkgId) { +fn frob_source_file(workspace: &Path, pkgid: &PkgId, filename: &str) { use conditions::bad_path::cond; let pkg_src_dir = workspace.push_many([~"src", pkgid.to_str()]); - let contents = os::list_dir_path(&pkg_src_dir); let mut maybe_p = None; - for p in contents.iter() { - if p.filetype() == Some(".rs") { - maybe_p = Some(p); - break; - } + let maybe_file = pkg_src_dir.push(filename); + debug!("Trying to frob %s -- %s", pkg_src_dir.to_str(), filename); + if os::path_exists(&maybe_file) { + maybe_p = Some(maybe_file); } + debug!("Frobbed? %?", maybe_p); match maybe_p { - Some(p) => { + Some(ref p) => { let w = io::file_writer(p, &[io::Append]); match w { Err(s) => { let _ = cond.raise((p.clone(), fmt!("Bad path: %s", s))); } @@ -499,7 +505,7 @@ fn test_install_valid() { debug!("temp_workspace = %s", temp_workspace.to_str()); // should have test, bench, lib, and main let src = PkgSrc::new(temp_workspace.clone(), false, temp_pkg_id.clone()); - ctxt.install(src); + ctxt.install(src, &Everything); // Check that all files exist let exec = target_executable_in_workspace(&temp_pkg_id, &temp_workspace); debug!("exec = %s", exec.to_str()); @@ -528,7 +534,7 @@ fn test_install_invalid() { // Uses task::try because of #9001 let result = do task::try { let pkg_src = PkgSrc::new(temp_workspace.clone(), false, pkgid.clone()); - ctxt.install(pkg_src); + ctxt.install(pkg_src, &Everything); }; // Not the best test -- doesn't test that we failed in the right way. // Best we can do for now. @@ -939,26 +945,28 @@ fn no_rebuilding() { } #[test] -#[ignore] 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_1 = datestamp(&lib_output_file_name(&workspace, - ".rust", - "bar")); - let foo_date_1 = datestamp(&output_file_name(&workspace, ~"foo")); + let bar_lib = lib_output_file_name(&workspace, "bar"); + let bar_date_1 = datestamp(&bar_lib); + + frob_source_file(&workspace, &p_id, "main.rs"); + + // Now make `bar` read-only so that subsequent rebuilds of it will fail + assert!(chmod_read_only(&bar_lib)); + + match command_line_test_partial([~"build", ~"foo"], &workspace) { + Success(*) => (), // ok + Fail(status) if status == 65 => fail!("no_rebuilding_dep failed: it tried to rebuild bar"), + Fail(_) => fail!("no_rebuilding_dep failed for some other reason") + } - 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")); + "bar")); assert_eq!(bar_date_1, bar_date_2); - assert!(foo_date_1 < foo_date_2); - assert!(foo_date_1 > bar_date_1); } #[test] @@ -967,7 +975,7 @@ fn do_rebuild_dep_dates_change() { 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_lib_name = lib_output_file_name(&workspace, "build", "bar"); + let bar_lib_name = lib_output_file_name(&workspace, "bar"); let bar_date = datestamp(&bar_lib_name); debug!("Datestamp on %s is %?", bar_lib_name.to_str(), bar_date); touch_source_file(&workspace, &dep_id); @@ -983,11 +991,11 @@ fn do_rebuild_dep_only_contents_change() { 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, "build", "bar")); - frob_source_file(&workspace, &dep_id); + let bar_date = datestamp(&lib_output_file_name(&workspace, "bar")); + frob_source_file(&workspace, &dep_id, "lib.rs"); // should adjust the datestamp command_line_test([~"build", ~"foo"], &workspace); - let new_bar_date = datestamp(&lib_output_file_name(&workspace, "build", "bar")); + let new_bar_date = datestamp(&lib_output_file_name(&workspace, "bar")); assert!(new_bar_date > bar_date); } @@ -1309,7 +1317,6 @@ fn rust_path_hack_build_no_arg() { } #[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"); @@ -1464,10 +1471,13 @@ fn test_cfg_fail() { let workspace = create_local_package(&p_id); writeFile(&workspace.push_many(["src", "foo-0.1", "main.rs"]), "#[cfg(quux)] fn main() {}"); - assert!(command_line_test_partial([test_sysroot().to_str(), + match command_line_test_partial([test_sysroot().to_str(), ~"build", ~"foo"], - &workspace).is_none()); + &workspace) { + Success(*) => fail!("test_cfg_fail failed"), + _ => () + } } @@ -1680,6 +1690,21 @@ fn test_target_specific_install_dir() { assert_executable_exists(&workspace, "foo"); } +#[test] +fn test_dependencies_terminate() { + // let a_id = PkgId::new("a"); + let b_id = PkgId::new("b"); +// let workspace = create_local_package_with_dep(&b_id, &a_id); + let workspace = create_local_package(&b_id); + let b_dir = workspace.push_many([~"src", ~"b-0.1"]); + // writeFile(&b_dir.push("lib.rs"), "extern mod a; pub fn f() {}"); + let b_subdir = b_dir.push("test"); + assert!(os::mkdir_recursive(&b_subdir, U_RWX)); + writeFile(&b_subdir.push("test.rs"), + "extern mod b; use b::f; #[test] fn g() { f() }"); + command_line_test([~"install", ~"b"], &workspace); +} + /// Returns true if p exists and is executable fn is_executable(p: &Path) -> bool { use std::libc::consts::os::posix88::{S_IXUSR}; @@ -1689,3 +1714,25 @@ fn is_executable(p: &Path) -> bool { Some(mode) => mode & S_IXUSR as uint == S_IXUSR as uint } } + +#[cfg(target_os = "win32")] +fn chmod_read_only(p: &Path) -> bool { + #[fixed_stack_segment]; + unsafe { + do p.to_str().with_c_str |src_buf| { + libc::chmod(src_buf, libc::consts::os::posix88::S_IRUSR as c_int) == 0 as libc::c_int + } + } +} + +#[cfg(not(target_os = "win32"))] +fn chmod_read_only(p: &Path) -> bool { + #[fixed_stack_segment]; + unsafe { + do p.to_str().with_c_str |src_buf| { + libc::chmod(src_buf, + libc::consts::os::posix88::S_IRUSR as libc::mode_t) == 0 + as libc::c_int + } + } +} diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index ab883b50f8c..64f76dcdc60 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::libc; use std::os; use extra::workcache; use rustc::driver::{driver, session}; @@ -26,7 +27,7 @@ use workspace::pkg_parent_workspaces; use path_util::{installed_library_in_workspace, U_RWX, rust_path, system_library, target_build_dir}; use messages::error; -pub use target::{OutputType, Main, Lib, Bench, Test}; +pub use target::{OutputType, Main, Lib, Bench, Test, JustOne, lib_name_of, lib_crate_filename}; use workcache_support::{digest_file_with_date, digest_only_date}; // It would be nice to have the list of commands in just one place -- for example, @@ -171,6 +172,8 @@ pub fn compile_input(context: &BuildContext, // not sure if we should support anything else let out_dir = target_build_dir(workspace).push_rel(&pkg_id.path); + // Make the output directory if it doesn't exist already + assert!(os::mkdir_recursive(&out_dir, U_RWX)); let binary = os::args()[0].to_managed(); @@ -220,7 +223,7 @@ pub fn compile_input(context: &BuildContext, optimize: if opt { session::Aggressive } else { session::No }, test: what == Test || what == Bench, maybe_sysroot: Some(sysroot_to_use), - addl_lib_search_paths: @mut (~[out_dir.clone()]), + addl_lib_search_paths: @mut (~[]), output_type: output_type, .. (*driver::build_session_options(binary, &matches, diagnostic::emit)).clone() }; @@ -329,6 +332,9 @@ pub fn compile_crate_from_input(input: &Path, // Register dependency on the source file exec.discover_input("file", input.to_str(), digest_file_with_date(input)); + debug!("Built %s, date = %?", outputs.out_filename.to_str(), + datestamp(&outputs.out_filename)); + Some(outputs.out_filename) } @@ -409,7 +415,8 @@ pub fn find_and_install_dependencies(context: &BuildContext, workspaces[0] }; let (outputs_disc, inputs_disc) = - context.install(PkgSrc::new(dep_workspace.clone(), false, pkg_id)); + context.install(PkgSrc::new(dep_workspace.clone(), + false, pkg_id), &JustOne(Path(lib_crate_filename))); debug!("Installed %s, returned %? dependencies and \ %? transitive dependencies", lib_name, outputs_disc.len(), inputs_disc.len()); @@ -435,10 +442,11 @@ pub fn find_and_install_dependencies(context: &BuildContext, debug!("Adding additional search path: %s", lib_name); let installed_library = installed_library_in_workspace(&Path(lib_name), &dep_workspace) - .expect( fmt!("rustpkg failed to install dependency %s", + .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()); + debug!("Installed %s into %s [%?]", lib_name, install_dir.to_str(), + datestamp(&installed_library)); save(install_dir); } }} @@ -449,37 +457,6 @@ pub fn find_and_install_dependencies(context: &BuildContext, }; } -#[cfg(windows)] -pub fn link_exe(_src: &Path, _dest: &Path) -> bool { - #[fixed_stack_segment]; #[inline(never)]; - - /* FIXME (#1768): Investigate how to do this on win32 - Node wraps symlinks by having a .bat, - but that won't work with minGW. */ - - false -} - -#[cfg(target_os = "linux")] -#[cfg(target_os = "android")] -#[cfg(target_os = "freebsd")] -#[cfg(target_os = "macos")] -pub fn link_exe(src: &Path, dest: &Path) -> bool { - #[fixed_stack_segment]; #[inline(never)]; - - use std::c_str::ToCStr; - use std::libc; - - unsafe { - do src.with_c_str |src_buf| { - do dest.with_c_str |dest_buf| { - libc::link(src_buf, dest_buf) == 0 as libc::c_int && - libc::chmod(dest_buf, 755) == 0 as libc::c_int - } - } - } -} - pub fn mk_string_lit(s: @str) -> ast::lit { Spanned { node: ast::lit_str(s), @@ -516,3 +493,12 @@ pub fn option_to_vec(x: Option) -> ~[T] { // tjc: cheesy fn debug_flags() -> ~[~str] { ~[] } // static DEBUG_FLAGS: ~[~str] = ~[~"-Z", ~"time-passes"]; + + +/// Returns the last-modified date as an Option +pub fn datestamp(p: &Path) -> Option { + debug!("Scrutinizing datestamp for %s - does it exist? %?", p.to_str(), os::path_exists(p)); + let out = p.stat().map(|stat| stat.st_mtime); + debug!("Date = %?", out); + out.map(|t| { *t as libc::time_t }) +} diff --git a/src/librustpkg/workcache_support.rs b/src/librustpkg/workcache_support.rs index e2416782d98..daf35c988c8 100644 --- a/src/librustpkg/workcache_support.rs +++ b/src/librustpkg/workcache_support.rs @@ -56,3 +56,8 @@ pub fn discover_outputs(e: &mut workcache::Exec, outputs: ~[Path]) { e.discover_output("binary", p.to_str(), digest_only_date(p)); } } + +/// Returns the function name for building a crate +pub fn crate_tag(p: &Path) -> ~str { + p.to_str() // implicitly, it's "build(p)"... +}