From c97957588b7e26d32e7800072df4c31da56703ef Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Mon, 30 Sep 2013 22:39:50 -0700 Subject: [PATCH] rustpkg: Support arbitrary dependencies in the install API api::install_pkg now accepts an argument that's a list of (kind, path) dependency pairs. This allows custom package scripts to declare C dependencies, as is demonstrated in rustpkg::tests::test_c_dependency_ok. Closes #6403 --- mk/tests.mk | 1 + src/librustpkg/api.rs | 96 ++++++++++++-- src/librustpkg/conditions.rs | 6 + src/librustpkg/context.rs | 18 +++ src/librustpkg/package_source.rs | 52 ++++++-- src/librustpkg/path_util.rs | 4 +- src/librustpkg/rustpkg.rs | 113 +++++++++------- src/librustpkg/target.rs | 26 +++- src/librustpkg/tests.rs | 121 ++++++++++++++++-- .../testsuite/pass/src/c-dependencies/bar.rs | 13 ++ .../testsuite/pass/src/c-dependencies/foo.rs | 12 ++ .../testsuite/pass/src/c-dependencies/lib.rs | 14 ++ .../testsuite/pass/src/c-dependencies/pkg.rs | 83 ++++++++++++ .../testsuite/pass/src/fancy-lib/lib.rs | 2 +- .../testsuite/pass/src/fancy-lib/pkg.rs | 31 ++--- src/librustpkg/util.rs | 26 +++- 16 files changed, 514 insertions(+), 104 deletions(-) create mode 100644 src/librustpkg/testsuite/pass/src/c-dependencies/bar.rs create mode 100644 src/librustpkg/testsuite/pass/src/c-dependencies/foo.rs create mode 100644 src/librustpkg/testsuite/pass/src/c-dependencies/lib.rs create mode 100644 src/librustpkg/testsuite/pass/src/c-dependencies/pkg.rs diff --git a/mk/tests.mk b/mk/tests.mk index c06c234e951..d6fc5ecb8e5 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -370,6 +370,7 @@ $(3)/stage$(1)/test/rustpkgtest-$(2)$$(X_$(2)): \ $$(SREQ$(1)_T_$(2)_H_$(3)) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBSYNTAX_$(2)) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC_$(2)) \ + $$(HBIN$(1)_H_$(3))/rustpkg$$(X_$(2)) \ $$(TBIN$(1)_T_$(2)_H_$(3))/rustpkg$$(X_$(2)) \ $$(TBIN$(1)_T_$(2)_H_$(3))/rustc$$(X_$(2)) @$$(call E, compile_and_link: $$@) diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs index 1f5802927a6..c67b6f52c7e 100644 --- a/src/librustpkg/api.rs +++ b/src/librustpkg/api.rs @@ -12,22 +12,35 @@ use context::*; use crate::*; use package_id::*; use package_source::*; +use path_util::{platform_library_name, target_build_dir}; use target::*; use version::Version; +use workspace::pkg_parent_workspaces; use workcache_support::*; +pub use path_util::default_workspace; pub use source_control::{safe_git_clone, git_clone_url}; -use std::os; +use std::{os, run}; use extra::arc::{Arc,RWArc}; use extra::workcache; use extra::workcache::{Database, Logger, FreshnessMap}; use extra::treemap::TreeMap; +// A little sad -- duplicated from rustc::back::* +#[cfg(target_arch = "arm")] +fn cc_args() -> ~[~str] { ~[~"-marm"] } +#[cfg(target_arch = "mips")] +fn cc_args() -> ~[~str] { ~[] } +#[cfg(target_arch = "x86")] +fn cc_args() -> ~[~str] { ~[~"-m32"] } +#[cfg(target_arch = "x86_64")] +fn cc_args() -> ~[~str] { ~[~"-m64"] } + /// Convenience functions intended for calling from pkg.rs /// p is where to put the cache file for dependencies -pub fn default_context(p: Path) -> BuildContext { - new_default_context(new_workcache_context(&p), p) +pub fn default_context(sysroot: Path, p: Path) -> BuildContext { + new_default_context(new_workcache_context(&p), sysroot) } pub fn new_default_context(c: workcache::Context, p: Path) -> BuildContext { @@ -68,7 +81,7 @@ 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 cx = default_context(sysroot, root.clone()); let pkg_src = PkgSrc { source_workspace: root.clone(), build_in_destination: false, @@ -81,12 +94,12 @@ pub fn build_lib(sysroot: Path, root: Path, name: ~str, version: Version, tests: ~[], benchs: ~[] }; - pkg_src.build(&cx, ~[]); + pkg_src.build(&cx, ~[], []); } pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version, main: Path) { - let cx = default_context(sysroot); + let cx = default_context(sysroot, root.clone()); let pkg_src = PkgSrc { source_workspace: root.clone(), build_in_destination: false, @@ -100,13 +113,76 @@ pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version, benchs: ~[] }; - pkg_src.build(&cx, ~[]); + pkg_src.build(&cx, ~[], []); } -pub fn install_pkg(sysroot: Path, workspace: Path, name: ~str, version: Version) { - let cx = default_context(sysroot); +pub fn install_pkg(cx: &BuildContext, + workspace: Path, + name: ~str, + version: Version, + // For now, these inputs are assumed to be inputs to each of the crates + more_inputs: ~[(~str, Path)]) { // pairs of Kind and Path let pkgid = PkgId{ version: version, ..PkgId::new(name)}; - cx.install(PkgSrc::new(workspace.clone(), workspace, false, pkgid), &Everything); + cx.install(PkgSrc::new(workspace.clone(), workspace, false, pkgid), + &WhatToBuild{ build_type: Inferred, + inputs_to_discover: more_inputs, + sources: Everything }); +} + +/// Builds an arbitrary library whose short name is `output`, +/// by invoking `tool` with arguments `args` plus "-o %s", where %s +/// is the platform-specific library name for `output`. +/// Returns that platform-specific name. +pub fn build_library_in_workspace(exec: &mut workcache::Exec, + context: &mut Context, + package_name: &str, + tool: &str, + flags: &[~str], + paths: &[~str], + output: &str) -> ~str { + use command_failed = conditions::command_failed::cond; + + let workspace = my_workspace(context, package_name); + let workspace_build_dir = target_build_dir(&workspace); + let out_name = workspace_build_dir.join_many([package_name.to_str(), + platform_library_name(output)]); + // make paths absolute + let pkgid = PkgId::new(package_name); + let absolute_paths = paths.map(|s| { + let whatever = workspace.join_many([~"src", + pkgid.to_str(), + s.to_owned()]); + whatever.as_str().unwrap().to_owned() + }); + + let cc_args = cc_args(); + + let all_args = flags + absolute_paths + cc_args + + ~[~"-o", out_name.as_str().unwrap().to_owned()]; + let exit_code = run::process_status(tool, all_args); + if exit_code != 0 { + command_failed.raise((tool.to_owned(), all_args, exit_code)) + } + else { + let out_name_str = out_name.as_str().unwrap().to_owned(); + exec.discover_output("binary", + out_name_str, + digest_only_date(&out_name)); + context.add_library_path(out_name.dir_path()); + out_name_str + } +} + +pub fn my_workspace(context: &Context, package_name: &str) -> Path { + use bad_pkg_id = conditions::bad_pkg_id::cond; + + // (this assumes no particular version is requested) + let pkgid = PkgId::new(package_name); + let workspaces = pkg_parent_workspaces(context, &pkgid); + if workspaces.is_empty() { + bad_pkg_id.raise((Path::new(package_name), package_name.to_owned())); + } + workspaces[0] } fn mk_crate(p: Path) -> Crate { diff --git a/src/librustpkg/conditions.rs b/src/librustpkg/conditions.rs index 19b310b4506..046ba405dbb 100644 --- a/src/librustpkg/conditions.rs +++ b/src/librustpkg/conditions.rs @@ -54,3 +54,9 @@ condition! { condition! { pub git_checkout_failed: (~str, Path) -> (); } + +condition! { + // str is output of applying the command (first component) + // to the args (second component) + pub command_failed: (~str, ~[~str], int) -> ~str; +} diff --git a/src/librustpkg/context.rs b/src/librustpkg/context.rs index 3f1f2a1f59d..72197219fc5 100644 --- a/src/librustpkg/context.rs +++ b/src/librustpkg/context.rs @@ -54,6 +54,15 @@ impl BuildContext { pub fn compile_upto(&self) -> StopBefore { self.context.compile_upto() } + + pub fn add_library_path(&mut self, p: Path) { + debug!("Adding library path: {}", p.display()); + self.context.add_library_path(p); + } + + pub fn additional_library_paths(&self) -> ~[Path] { + self.context.rustc_flags.additional_library_paths.clone() + } } /* @@ -85,6 +94,9 @@ pub struct RustcFlags { target: Option<~str>, // Target CPU (defaults to rustc's default target CPU) target_cpu: Option<~str>, + // Additional library directories, which get passed with the -L flag + // This can't be set with a rustpkg flag, only from package scripts + additional_library_paths: ~[Path], // Any -Z features experimental_features: Option<~[~str]> } @@ -99,6 +111,7 @@ impl Clone for RustcFlags { save_temps: self.save_temps, target: self.target.clone(), target_cpu: self.target_cpu.clone(), + additional_library_paths: self.additional_library_paths.clone(), experimental_features: self.experimental_features.clone() } } @@ -148,6 +161,10 @@ impl Context { pub fn compile_upto(&self) -> StopBefore { self.rustc_flags.compile_upto } + + pub fn add_library_path(&mut self, p: Path) { + self.rustc_flags.additional_library_paths.push(p); + } } /// We assume that if ../../rustc exists, then we're running @@ -210,6 +227,7 @@ impl RustcFlags { save_temps: false, target: None, target_cpu: None, + additional_library_paths: ~[], experimental_features: None } } diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index 68d2d9662e3..17ba79862e0 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -23,7 +23,7 @@ use path_util::{find_dir_using_rust_path_hack, make_dir_rwx_recursive, default_w use path_util::{target_build_dir, versionize, dir_has_crate_file}; use util::{compile_crate, DepMap}; use workcache_support; -use workcache_support::crate_tag; +use workcache_support::{digest_only_date, digest_file_with_date, crate_tag}; use extra::workcache; use extra::treemap::TreeMap; @@ -390,7 +390,8 @@ impl PkgSrc { deps: &mut DepMap, crates: &[Crate], cfgs: &[~str], - what: OutputType) { + what: OutputType, + inputs_to_discover: &[(~str, Path)]) { for crate in crates.iter() { let path = self.start_dir.join(&crate.file); debug!("build_crates: compiling {}", path.display()); @@ -408,7 +409,19 @@ impl PkgSrc { let sub_dir = self.build_workspace().clone(); let sub_flags = crate.flags.clone(); let sub_deps = deps.clone(); + let inputs = inputs_to_discover.map(|&(ref k, ref p)| + (k.clone(), p.as_str().unwrap().to_owned())); do prep.exec |exec| { + for &(ref kind, ref p) in inputs.iter() { + let pth = Path::new(p.clone()); + exec.discover_input(*kind, *p, if *kind == ~"file" { + digest_file_with_date(&pth) + } else if *kind == ~"binary" { + digest_only_date(&Path::new(p.clone())) + } else { + fail!("Bad kind in build_crates") + }); + } let result = compile_crate(&subcx, exec, &id, @@ -452,22 +465,43 @@ impl PkgSrc { build_context: &BuildContext, // DepMap is a map from str (crate name) to (kind, name) -- // it tracks discovered dependencies per-crate - cfgs: ~[~str]) -> DepMap { + cfgs: ~[~str], + inputs_to_discover: &[(~str, Path)]) -> DepMap { let mut deps = TreeMap::new(); - let libs = self.libs.clone(); let mains = self.mains.clone(); let tests = self.tests.clone(); let benchs = self.benchs.clone(); debug!("Building libs in {}, destination = {}", - self.source_workspace.display(), self.build_workspace().display()); - self.build_crates(build_context, &mut deps, libs, cfgs, Lib); + self.destination_workspace.display(), + self.destination_workspace.display()); + self.build_crates(build_context, + &mut deps, + libs, + cfgs, + Lib, + inputs_to_discover); debug!("Building mains"); - self.build_crates(build_context, &mut deps, mains, cfgs, Main); + self.build_crates(build_context, + &mut deps, + mains, + cfgs, + Main, + inputs_to_discover); debug!("Building tests"); - self.build_crates(build_context, &mut deps, tests, cfgs, Test); + self.build_crates(build_context, + &mut deps, + tests, + cfgs, + Test, + inputs_to_discover); debug!("Building benches"); - self.build_crates(build_context, &mut deps, benchs, cfgs, Bench); + self.build_crates(build_context, + &mut deps, + benchs, + cfgs, + Bench, + inputs_to_discover); deps } diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index c47c89d777b..a48ef23115c 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -461,7 +461,6 @@ pub fn versionize(p: &Path, v: &Version) -> Path { p.with_filename(q) } - #[cfg(target_os = "win32")] pub fn chmod_read_only(p: &Path) -> bool { #[fixed_stack_segment]; @@ -483,3 +482,6 @@ pub fn chmod_read_only(p: &Path) -> bool { } } +pub fn platform_library_name(s: &str) -> ~str { + format!("{}{}{}", os::consts::DLL_PREFIX, s, os::consts::DLL_SUFFIX) +} diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 6c55f7af0c0..37a5a2ea711 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -33,7 +33,6 @@ use rustc::metadata::filesearch; use rustc::metadata::filesearch::rust_path; use extra::{getopts}; use syntax::{ast, diagnostic}; -use util::*; use messages::{error, warn, note}; use path_util::{build_pkg_id_in_workspace, built_test_in_workspace}; use path_util::{U_RWX, in_rust_path}; @@ -47,15 +46,16 @@ use context::{Context, BuildContext, LLVMAssemble, LLVMCompileBitcode}; use package_id::PkgId; use package_source::PkgSrc; -use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench, Tests}; +use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench}; +use target::{Tests, MaybeCustom, Inferred, JustOne}; use workcache_support::digest_only_date; use exit_codes::{COPY_FAILED_CODE, BAD_FLAG_CODE}; pub mod api; mod conditions; -mod context; +pub mod context; mod crate; -mod exit_codes; +pub mod exit_codes; mod installed_packages; mod messages; mod package_id; @@ -67,7 +67,7 @@ mod target; #[cfg(test)] mod tests; mod util; -mod version; +pub mod version; pub mod workcache_support; mod workspace; @@ -96,7 +96,7 @@ impl<'self> PkgScript<'self> { /// Given the path name for a package script /// and a package ID, parse the package script into /// a PkgScript that we can then execute - fn parse<'a>(sysroot: @Path, + fn parse<'a>(sysroot: Path, script: Path, workspace: &Path, id: &'a PkgId) -> PkgScript<'a> { @@ -107,7 +107,7 @@ impl<'self> PkgScript<'self> { debug!("pkgscript parse: {}", sysroot.display()); let options = @session::options { binary: binary, - maybe_sysroot: Some(sysroot), + maybe_sysroot: Some(@sysroot), crate_type: session::bin_crate, .. (*session::basic_options()).clone() }; @@ -132,12 +132,7 @@ impl<'self> PkgScript<'self> { } } - /// Run the contents of this package script, where - /// 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 - fn run_custom(&mut self, exec: &mut workcache::Exec, - sysroot: &Path) -> (~[~str], ExitCode) { + fn build_custom(&mut self, exec: &mut workcache::Exec) -> ~str { let sess = self.sess; debug!("Working directory = {}", self.build_dir.display()); @@ -152,17 +147,28 @@ impl<'self> PkgScript<'self> { &self.build_dir, sess, crate); - debug!("Running program: {} {} {}", exe.display(), - sysroot.display(), "install"); // Discover the output // FIXME (#9639): This needs to handle non-utf8 paths - exec.discover_output("binary", exe.as_str().unwrap(), digest_only_date(&exe)); + // Discover the output + exec.discover_output("binary", exe.as_str().unwrap().to_owned(), digest_only_date(&exe)); + exe.as_str().unwrap().to_owned() + } + + + /// Run the contents of this package script, where + /// 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 + fn run_custom(exe: &Path, sysroot: &Path) -> (~[~str], int) { + debug!("Running program: {} {} {}", exe.as_str().unwrap().to_owned(), + sysroot.display(), "install"); // FIXME #7401 should support commands besides `install` // FIXME (#9639): This needs to handle non-utf8 paths let status = run::process_status(exe.as_str().unwrap(), [sysroot.as_str().unwrap().to_owned(), ~"install"]); if status != 0 { - return (~[], status); + debug!("run_custom: first pkg command failed with {:?}", status); + (~[], status) } else { debug!("Running program (configs): {} {} {}", @@ -170,6 +176,7 @@ impl<'self> PkgScript<'self> { // FIXME (#9639): This needs to handle non-utf8 paths let output = run::process_output(exe.as_str().unwrap(), [sysroot.as_str().unwrap().to_owned(), ~"configs"]); + debug!("run_custom: second pkg command did {:?}", output.status); // Run the configs() function to get the configs let cfgs = str::from_utf8_slice(output.output).word_iter() .map(|w| w.to_owned()).collect(); @@ -263,7 +270,7 @@ impl CtxMethods for BuildContext { let cwd = os::getcwd(); match cmd { "build" => { - self.build_args(args, &Everything); + self.build_args(args, &WhatToBuild::new(MaybeCustom, Everything)); } "clean" => { if args.len() < 1 { @@ -301,12 +308,14 @@ impl CtxMethods for BuildContext { let inferred_pkgid = PkgId::new(cwd.filename_str().unwrap()); self.install(PkgSrc::new(cwd, default_workspace(), - true, inferred_pkgid), &Everything); + true, inferred_pkgid), + &WhatToBuild::new(MaybeCustom, Everything)); } None => { usage::install(); return; } Some((ws, pkgid)) => { let pkg_src = PkgSrc::new(ws.clone(), ws.clone(), false, pkgid); - self.install(pkg_src, &Everything); + self.install(pkg_src, &WhatToBuild::new(MaybeCustom, + Everything)); } } } @@ -320,7 +329,7 @@ impl CtxMethods for BuildContext { if workspaces.is_empty() { let d = default_workspace(); let src = PkgSrc::new(d.clone(), d, false, pkgid.clone()); - self.install(src, &Everything); + self.install(src, &WhatToBuild::new(MaybeCustom, Everything)); } else { for workspace in workspaces.iter() { @@ -331,7 +340,7 @@ impl CtxMethods for BuildContext { dest, self.context.use_rust_path_hack, pkgid.clone()); - self.install(src, &Everything); + self.install(src, &WhatToBuild::new(MaybeCustom, Everything)); }; } } @@ -354,7 +363,8 @@ impl CtxMethods for BuildContext { } "test" => { // Build the test executable - let maybe_id_and_workspace = self.build_args(args, &Tests); + let maybe_id_and_workspace = self.build_args(args, + &WhatToBuild::new(MaybeCustom, Tests)); match maybe_id_and_workspace { Some((pkg_id, workspace)) => { // Assuming it's built, run the tests @@ -420,6 +430,7 @@ impl CtxMethods for BuildContext { pkgid = {} pkgsrc start_dir = {}", workspace.display(), in_rust_path(&workspace), is_git_dir(&workspace.join(&pkgid.path)), pkgid.to_str(), pkg_src.start_dir.display()); + debug!("build: what to build = {:?}", what_to_build); // 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 @@ -448,27 +459,27 @@ impl CtxMethods for BuildContext { debug!("Package source directory = {}", 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 cfgs = match (pkg_src.package_script_option(), what_to_build.build_type) { + (Some(package_script_path), MaybeCustom) => { let sysroot = self.sysroot_to_use(); - // FIXME (#9639): This needs to handle non-utf8 paths - let pkg_script_path_str = package_script_path.as_str().unwrap(); - let (cfgs, hook_result) = - do self.workcache_context.with_prep(pkg_script_path_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); + // Build the package script if needed + let script_build = format!("build_package_script({})", + package_script_path.display()); + let pkg_exe = do self.workcache_context.with_prep(script_build) |prep| { + let subsysroot = sysroot.clone(); + let psp = package_script_path.clone(); + let ws = workspace.clone(); + let pid = pkgid.clone(); do prep.exec |exec| { - let mut pscript = PkgScript::parse(@sub_sysroot.clone(), - package_script_path_clone.clone(), - &sub_ws, - &sub_id); - - pscript.run_custom(exec, &sub_sysroot) + let mut pscript = PkgScript::parse(subsysroot.clone(), + psp.clone(), + &ws, + &pid); + pscript.build_custom(exec) } }; + // We always *run* the package script + let (cfgs, hook_result) = PkgScript::run_custom(&Path::new(pkg_exe), &sysroot); debug!("Command return code = {:?}", hook_result); if hook_result != 0 { fail!("Error running custom build command") @@ -477,7 +488,11 @@ impl CtxMethods for BuildContext { // otherwise, the package script succeeded cfgs } - None => { + (Some(_), Inferred) => { + debug!("There is a package script, but we're ignoring it"); + ~[] + } + (None, _) => { debug!("No package script, continuing"); ~[] } @@ -486,13 +501,13 @@ impl CtxMethods for BuildContext { // If there was a package script, it should have finished // the build already. Otherwise... if !custom { - match what_to_build { + match what_to_build.sources { // Find crates inside the workspace - &Everything => pkg_src.find_crates(), + Everything => pkg_src.find_crates(), // Find only tests - &Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path::new(s)) }), + Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path::new(s)) }), // Don't infer any crates -- just build the one that was requested - &JustOne(ref p) => { + JustOne(ref p) => { // We expect that p is relative to the package source's start directory, // so check that assumption debug!("JustOne: p = {}", p.display()); @@ -512,7 +527,7 @@ impl CtxMethods for BuildContext { } } // Build it! - pkg_src.build(self, cfgs); + pkg_src.build(self, cfgs, []); } } @@ -551,6 +566,8 @@ impl CtxMethods for BuildContext { // just means inferring all the crates in it, then building each one. self.build(&mut pkg_src, what); + debug!("Done building package source {}", pkg_src.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 {}", id.to_str()); @@ -823,6 +840,7 @@ pub fn main_args(args: &[~str]) -> int { save_temps: save_temps, target: target, target_cpu: target_cpu, + additional_library_paths: ~[], // No way to set this from the rustpkg command line experimental_features: experimental_features }; @@ -895,7 +913,8 @@ pub fn main_args(args: &[~str]) -> int { 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 + workcache_context: api::default_context(sroot.clone(), + default_workspace()).workcache_context }.run(sub_cmd, rm_args.clone()) }; // FIXME #9262: This is using the same error code for all errors, diff --git a/src/librustpkg/target.rs b/src/librustpkg/target.rs index b21641a5e53..9863fd0a89e 100644 --- a/src/librustpkg/target.rs +++ b/src/librustpkg/target.rs @@ -23,7 +23,31 @@ pub enum Target { } #[deriving(Eq, Clone)] -pub enum WhatToBuild { +pub struct WhatToBuild { + build_type: BuildType, // Whether or not to ignore the pkg.rs file + sources: SourceType, // Which crates to build + inputs_to_discover: ~[(~str, Path)] // Inputs to these crates to be discovered + // (For now all of these inputs will be taken as discovered inputs + // for all of the crates) + // (Paired with their kinds) +} + +impl WhatToBuild { + pub fn new(build_type: BuildType, sources: SourceType) -> WhatToBuild { + WhatToBuild { build_type: build_type, + sources: sources, + inputs_to_discover: ~[] } + } +} + +#[deriving(Eq, Clone)] +pub enum BuildType { + Inferred, // Ignore the pkg.rs file even if one exists + MaybeCustom // Use the pkg.rs file if it exists +} + +#[deriving(Eq, Clone)] +pub enum SourceType { /// Build just one lib.rs file in `path`, which is relative to the active workspace's src/ dir JustOne(Path), /// Build any test.rs files that can be recursively found in the active workspace diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 58c6b4ff81f..16e13f70092 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -28,7 +28,7 @@ use path_util::{target_executable_in_workspace, target_test_in_workspace, library_in_workspace, installed_library_in_workspace, built_bench_in_workspace, built_test_in_workspace, built_library_in_workspace, built_executable_in_workspace, target_build_dir, - chmod_read_only}; + chmod_read_only, platform_library_name}; use rustc::back::link::get_cc_prog; use rustc::metadata::filesearch::rust_path; use rustc::driver::driver::{build_session, build_session_options, host_triple, optgroups}; @@ -299,12 +299,6 @@ fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~s cmd, args, str::from_utf8(output.output), str::from_utf8(output.error), output.status); -/* -By the way, rustpkg *won't* return a nonzero exit code if it fails -- -see #4547 -So tests that use this need to check the existence of a file -to make sure the command succeeded -*/ if output.status != 0 { debug!("Command {} {:?} failed with exit code {:?}; its output was --- {} ---", cmd, args, output.status, @@ -600,7 +594,7 @@ fn test_install_valid() { temp_workspace.clone(), false, temp_pkg_id.clone()); - ctxt.install(src, &Everything); + ctxt.install(src, &WhatToBuild::new(MaybeCustom, Everything)); // Check that all files exist let exec = target_executable_in_workspace(&temp_pkg_id, temp_workspace); debug!("exec = {}", exec.display()); @@ -639,7 +633,7 @@ fn test_install_invalid() { temp_workspace.clone(), false, pkgid.clone()); - ctxt.install(pkg_src, &Everything); + ctxt.install(pkg_src, &WhatToBuild::new(MaybeCustom, Everything)); }; // Not the best test -- doesn't test that we failed in the right way. // Best we can do for now. @@ -897,14 +891,14 @@ fn rustpkg_local_pkg() { } #[test] -#[ignore (reason = "test makes bogus assumptions about build directory layout: issue #8690")] fn package_script_with_default_build() { let dir = create_local_package(&PkgId::new("fancy-lib")); let dir = dir.path(); debug!("dir = {}", dir.display()); let mut source = test_sysroot().dir_path(); source.pop(); source.pop(); - source.push_many(["src", "librustpkg", "testsuite", "pass", "src", "fancy-lib", "pkg.rs"]); + let source = Path::new(file!()).dir_path().join_many( + [~"testsuite", ~"pass", ~"src", ~"fancy-lib", ~"pkg.rs"]); debug!("package_script_with_default_build: {}", source.display()); if !os::copy_file(&source, &dir.join_many(["src", "fancy-lib-0.1", "pkg.rs"])) { @@ -912,7 +906,10 @@ fn package_script_with_default_build() { } command_line_test([~"install", ~"fancy-lib"], dir); assert_lib_exists(dir, &Path::new("fancy-lib"), NoVersion); - assert!(os::path_exists(&target_build_dir(dir).join_many(["fancy-lib", "generated.rs"]))); + assert!(os::path_exists(&target_build_dir(dir).join_many([~"fancy-lib", ~"generated.rs"]))); + let generated_path = target_build_dir(dir).join_many([~"fancy-lib", ~"generated.rs"]); + debug!("generated path = {}", generated_path.display()); + assert!(os::path_exists(&generated_path)); } #[test] @@ -2251,6 +2248,106 @@ fn find_sources_in_cwd() { assert_executable_exists(&source_dir.join(".rust"), "foo"); } +#[test] +fn test_c_dependency_ok() { + // Pkg has a custom build script that adds a single C file as a dependency, and + // registers a hook to build it if it's not fresh + // After running `build`, test that the C library built + + let dir = create_local_package(&PkgId::new("cdep")); + let dir = dir.path(); + writeFile(&dir.join_many(["src", "cdep-0.1", "main.rs"]), + "#[link_args = \"-lfoo\"]\nextern { fn f(); } \ + \n#[fixed_stack_segment]\nfn main() { unsafe { f(); } }"); + writeFile(&dir.join_many(["src", "cdep-0.1", "foo.c"]), "void f() {}"); + + debug!("dir = {}", dir.display()); + let source = Path::new(file!()).dir_path().join_many( + [~"testsuite", ~"pass", ~"src", ~"c-dependencies", ~"pkg.rs"]); + if !os::copy_file(&source, + &dir.join_many([~"src", ~"cdep-0.1", ~"pkg.rs"])) { + fail!("Couldn't copy file"); + } + command_line_test([~"build", ~"cdep"], dir); + assert_executable_exists(dir, "cdep"); + let out_dir = target_build_dir(dir).join("cdep"); + let c_library_path = out_dir.join(platform_library_name("foo")); + debug!("c library path: {}", c_library_path.display()); + assert!(os::path_exists(&c_library_path)); +} + +#[test] +fn test_c_dependency_no_rebuilding() { + let dir = create_local_package(&PkgId::new("cdep")); + let dir = dir.path(); + writeFile(&dir.join_many(["src", "cdep-0.1", "main.rs"]), + "#[link_args = \"-lfoo\"]\nextern { fn f(); } \ + \n#[fixed_stack_segment]\nfn main() { unsafe { f(); } }"); + writeFile(&dir.join_many(["src", "cdep-0.1", "foo.c"]), "void f() {}"); + + debug!("dir = {}", dir.display()); + let source = Path::new(file!()).dir_path().join_many( + [~"testsuite", ~"pass", ~"src", ~"c-dependencies", ~"pkg.rs"]); + if !os::copy_file(&source, + &dir.join_many([~"src", ~"cdep-0.1", ~"pkg.rs"])) { + fail!("Couldn't copy file"); + } + command_line_test([~"build", ~"cdep"], dir); + assert_executable_exists(dir, "cdep"); + let out_dir = target_build_dir(dir).join("cdep"); + let c_library_path = out_dir.join(platform_library_name("foo")); + debug!("c library path: {}", c_library_path.display()); + assert!(os::path_exists(&c_library_path)); + + // Now, make it read-only so rebuilding will fail + assert!(chmod_read_only(&c_library_path)); + + match command_line_test_partial([~"build", ~"cdep"], dir) { + Success(*) => (), // ok + Fail(status) if status == 65 => fail!("test_c_dependency_no_rebuilding failed: \ + it tried to rebuild foo.c"), + Fail(_) => fail!("test_c_dependency_no_rebuilding failed for some other reason") + } +} + +#[test] +fn test_c_dependency_yes_rebuilding() { + let dir = create_local_package(&PkgId::new("cdep")); + let dir = dir.path(); + writeFile(&dir.join_many(["src", "cdep-0.1", "main.rs"]), + "#[link_args = \"-lfoo\"]\nextern { fn f(); } \ + \n#[fixed_stack_segment]\nfn main() { unsafe { f(); } }"); + let c_file_name = dir.join_many(["src", "cdep-0.1", "foo.c"]); + writeFile(&c_file_name, "void f() {}"); + + let source = Path::new(file!()).dir_path().join_many( + [~"testsuite", ~"pass", ~"src", ~"c-dependencies", ~"pkg.rs"]); + let target = dir.join_many([~"src", ~"cdep-0.1", ~"pkg.rs"]); + debug!("Copying {} -> {}", source.display(), target.display()); + if !os::copy_file(&source, &target) { + fail!("Couldn't copy file"); + } + command_line_test([~"build", ~"cdep"], dir); + assert_executable_exists(dir, "cdep"); + let out_dir = target_build_dir(dir).join("cdep"); + let c_library_path = out_dir.join(platform_library_name("foo")); + debug!("c library path: {}", c_library_path.display()); + assert!(os::path_exists(&c_library_path)); + + // Now, make the Rust library read-only so rebuilding will fail + match built_library_in_workspace(&PkgId::new("cdep"), dir) { + Some(ref pth) => assert!(chmod_read_only(pth)), + None => assert_built_library_exists(dir, "cdep") + } + + match command_line_test_partial([~"build", ~"cdep"], dir) { + Success(*) => fail!("test_c_dependency_yes_rebuilding failed: \ + it didn't rebuild and should have"), + Fail(status) if status == 65 => (), + Fail(_) => fail!("test_c_dependency_yes_rebuilding failed for some other reason") + } +} + /// Returns true if p exists and is executable fn is_executable(p: &Path) -> bool { use std::libc::consts::os::posix88::{S_IXUSR}; diff --git a/src/librustpkg/testsuite/pass/src/c-dependencies/bar.rs b/src/librustpkg/testsuite/pass/src/c-dependencies/bar.rs new file mode 100644 index 00000000000..ffbc6e2a7f9 --- /dev/null +++ b/src/librustpkg/testsuite/pass/src/c-dependencies/bar.rs @@ -0,0 +1,13 @@ +// 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 fn assert_true() { + assert!(true); +} diff --git a/src/librustpkg/testsuite/pass/src/c-dependencies/foo.rs b/src/librustpkg/testsuite/pass/src/c-dependencies/foo.rs new file mode 100644 index 00000000000..542a6af402d --- /dev/null +++ b/src/librustpkg/testsuite/pass/src/c-dependencies/foo.rs @@ -0,0 +1,12 @@ +// 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 fn do_nothing() { +} \ No newline at end of file diff --git a/src/librustpkg/testsuite/pass/src/c-dependencies/lib.rs b/src/librustpkg/testsuite/pass/src/c-dependencies/lib.rs new file mode 100644 index 00000000000..bd1cb240a34 --- /dev/null +++ b/src/librustpkg/testsuite/pass/src/c-dependencies/lib.rs @@ -0,0 +1,14 @@ +// 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. + +extern mod std; + +pub mod foo; +pub mod bar; diff --git a/src/librustpkg/testsuite/pass/src/c-dependencies/pkg.rs b/src/librustpkg/testsuite/pass/src/c-dependencies/pkg.rs new file mode 100644 index 00000000000..b667dc0a576 --- /dev/null +++ b/src/librustpkg/testsuite/pass/src/c-dependencies/pkg.rs @@ -0,0 +1,83 @@ +// 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. + +extern mod rustpkg; +extern mod rustc; + +use std::{io, os, task}; +use rustpkg::api; +use rustpkg::version::NoVersion; +use rustpkg::workcache_support::digest_file_with_date; +use rustpkg::exit_codes::COPY_FAILED_CODE; + +pub fn main() { + let args = os::args(); + +// by convention, first arg is sysroot + if args.len() < 2 { + fail!("Package script requires a directory where rustc libraries live as the first \ + argument"); + } + + let path_for_db = api::default_workspace(); + debug!("path_for_db = {}", path_for_db.display()); + + let sysroot_arg = args[1].clone(); + let sysroot = Path::new(sysroot_arg); + if !os::path_exists(&sysroot) { + fail!("Package script requires a sysroot that exists; {} doesn't", sysroot.display()); + } + + if args[2] != ~"install" { + io::println(format!("Warning: I don't know how to {}", args[2])); + return; + } + + let mut context = api::default_context(sysroot, path_for_db); + let my_workspace = api::my_workspace(&context.context, "cdep"); + let foo_c_name = my_workspace.join_many(["src", "cdep-0.1", "foo.c"]); + + let out_lib_path = do context.workcache_context.with_prep("foo.c") |prep| { + let sub_cx = context.context.clone(); + debug!("foo_c_name = {}", foo_c_name.display()); + prep.declare_input("file", + foo_c_name.as_str().unwrap().to_owned(), + digest_file_with_date(&foo_c_name)); + let out_path = do prep.exec |exec| { + let out_path = api::build_library_in_workspace(exec, + &mut sub_cx.clone(), + "cdep", + "gcc", + [~"-c"], + [~"foo.c"], + "foo"); + let out_p = Path::new(out_path); + out_p.as_str().unwrap().to_owned() + }; + out_path + }; + let out_lib_path = Path::new(out_lib_path); + debug!("out_lib_path = {}", out_lib_path.display()); + context.add_library_path(out_lib_path.dir_path()); + + let context_clone = context.clone(); + let task_res = do task::try { + let mut cc = context_clone.clone(); + api::install_pkg(&mut cc, + os::getcwd(), + ~"cdep", + NoVersion, + ~[(~"binary", out_lib_path.clone()), (~"file", foo_c_name.clone())]); + }; + + if task_res.is_err() { + os::set_exit_status(COPY_FAILED_CODE); + } +} diff --git a/src/librustpkg/testsuite/pass/src/fancy-lib/lib.rs b/src/librustpkg/testsuite/pass/src/fancy-lib/lib.rs index dc068eed143..17386cd03c2 100644 --- a/src/librustpkg/testsuite/pass/src/fancy-lib/lib.rs +++ b/src/librustpkg/testsuite/pass/src/fancy-lib/lib.rs @@ -21,4 +21,4 @@ extern mod std; pub mod foo; pub mod bar; -#[path = "../../build/fancy_lib/generated.rs"] pub mod generated; +#[path = "../../build/fancy-lib/generated.rs"] pub mod generated; diff --git a/src/librustpkg/testsuite/pass/src/fancy-lib/pkg.rs b/src/librustpkg/testsuite/pass/src/fancy-lib/pkg.rs index db11ffa0cc6..0b838b3e0f9 100644 --- a/src/librustpkg/testsuite/pass/src/fancy-lib/pkg.rs +++ b/src/librustpkg/testsuite/pass/src/fancy-lib/pkg.rs @@ -15,42 +15,37 @@ use std::{io, os}; use rustpkg::api; use rustpkg::version::NoVersion; -use rustc::metadata::filesearch; - pub fn main() { - use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; let args = os::args(); // by convention, first arg is sysroot if args.len() < 2 { + debug!("Failing, arg len"); fail!("Package script requires a directory where rustc libraries live as the first \ argument"); } let sysroot_arg = args[1].clone(); - let sysroot = Path(sysroot_arg); + let sysroot = Path::new(sysroot_arg); if !os::path_exists(&sysroot) { - fail!("Package script requires a sysroot that exists; %s doesn't", sysroot.to_str()); + debug!("Failing, sysroot"); + fail!("Package script requires a sysroot that exists;{} doesn't", sysroot.display()); } if args[2] != ~"install" { + debug!("Failing, weird command"); println!("Warning: I don't know how to {}", args[2]); return; } - let out_path = Path("build/fancy-lib"); - if !os::path_exists(&out_path) { - assert!(os::make_dir(&out_path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - } + debug!("Checking self_exe_path"); + let out_path = os::self_exe_path().expect("Couldn't get self_exe path"); - let file = io::file_writer(&out_path.push("generated.rs"), - [io::Create]).unwrap(); - file.write_str("pub fn wheeeee() { for [1, 2, 3].each() |_| { assert!(true); } }"); + debug!("Writing file"); + let file = io::file_writer(&out_path.join("generated.rs"), [io::Create]).unwrap(); + file.write_str("pub fn wheeeee() { let xs = [1, 2, 3]; \ + for _ in xs.iter() { assert!(true); } }"); - - debug!("api_____install_____lib, my sysroot:"); - debug!(sysroot.to_str()); - - api::install_lib(@sysroot, os::getcwd(), ~"fancy-lib", Path("lib.rs"), - NoVersion); + let context = api::default_context(sysroot, api::default_workspace()); + api::install_pkg(&context, os::getcwd(), ~"fancy-lib", NoVersion, ~[]); } diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index a3a4a07cfc7..0e4fbbf2c10 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -27,11 +27,13 @@ use context::{in_target, StopBefore, Link, Assemble, BuildContext}; use package_id::PkgId; use package_source::PkgSrc; use workspace::pkg_parent_workspaces; -use path_util::{U_RWX, system_library, target_build_dir}; +use path_util::{system_library, target_build_dir}; use path_util::{default_workspace, built_library_in_workspace}; pub use target::{OutputType, Main, Lib, Bench, Test, JustOne, lib_name_of, lib_crate_filename}; pub use target::{Target, Build, Install}; use extra::treemap::TreeMap; +use path_util::U_RWX; +pub use target::{lib_name_of, lib_crate_filename, WhatToBuild, MaybeCustom, Inferred}; 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, @@ -233,12 +235,14 @@ pub fn compile_input(context: &BuildContext, Nothing => link::output_type_exe }; + debug!("Output type = {:?}", output_type); + let options = @session::options { crate_type: crate_type, optimize: if opt { session::Aggressive } else { session::No }, test: what == Test || what == Bench, maybe_sysroot: Some(sysroot_to_use), - addl_lib_search_paths: @mut (~[]), + addl_lib_search_paths: @mut context.additional_library_paths(), output_type: output_type, .. (*driver::build_session_options(binary, &matches, @@ -246,6 +250,8 @@ pub fn compile_input(context: &BuildContext, @diagnostic::Emitter)).clone() }; + debug!("Created options..."); + let addl_lib_search_paths = @mut options.addl_lib_search_paths; // Make sure all the library directories actually exist, since the linker will complain // otherwise @@ -258,16 +264,22 @@ pub fn compile_input(context: &BuildContext, } } + debug!("About to build session..."); + let sess = driver::build_session(options, @diagnostic::DefaultEmitter as @diagnostic::Emitter); + debug!("About to build config..."); + // Infer dependencies that rustpkg needs to build, by scanning for // `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.clone(), crate); + debug!("About to call find_and_install_dependencies..."); + find_and_install_dependencies(context, pkg_id, in_file, sess, exec, &crate, deps, |p| { debug!("a dependency: {}", p.display()); @@ -377,7 +389,6 @@ pub fn compile_crate_from_input(input: &Path, debug!("Built {}, date = {:?}", outputs.out_filename.display(), datestamp(&outputs.out_filename)); - Some(outputs.out_filename) } @@ -431,7 +442,9 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { }; debug!("Finding and installing... {}", lib_name); // Check standard Rust library path first - match system_library(&self.context.sysroot(), lib_name) { + let whatever = system_library(&self.context.sysroot(), lib_name); + debug!("system library returned {:?}", whatever); + match whatever { Some(ref installed_path) => { debug!("It exists: {}", installed_path.display()); // Say that [path for c] has a discovered dependency on @@ -478,7 +491,10 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { self.context.context.use_rust_path_hack, pkg_id.clone()); let (outputs_disc, inputs_disc) = - self.context.install(pkg_src, &JustOne(Path::new(lib_crate_filename))); + self.context.install( + pkg_src, + &WhatToBuild::new(Inferred, + JustOne(Path::new(lib_crate_filename)))); debug!("Installed {}, returned {:?} dependencies and \ {:?} transitive dependencies", lib_name, outputs_disc.len(), inputs_disc.len());