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
This commit is contained in:
Tim Chevalier 2013-09-30 22:39:50 -07:00
parent 22a5ebdc6b
commit c97957588b
16 changed files with 514 additions and 104 deletions

View File

@ -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: $$@)

View File

@ -12,22 +12,35 @@
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 {

View File

@ -54,3 +54,9 @@
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;
}

View File

@ -54,6 +54,15 @@ pub fn flag_strs(&self) -> ~[~str] {
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 @@ fn clone(&self) -> 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 @@ pub fn flag_strs(&self) -> ~[~str] {
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 @@ pub fn default() -> RustcFlags {
save_temps: false,
target: None,
target_cpu: None,
additional_library_paths: ~[],
experimental_features: None
}
}

View File

@ -23,7 +23,7 @@
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 @@ fn build_crates(&self,
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 @@ fn build_crates(&self,
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 @@ pub fn build(&self,
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
}

View File

@ -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)
}

View File

@ -33,7 +33,6 @@
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 @@
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 @@
#[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 @@ fn parse<'a>(sysroot: @Path,
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 @@ fn parse<'a>(sysroot: @Path,
}
}
/// Run the contents of this package script, where <what>
/// 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 @@ fn run_custom(&mut self, exec: &mut workcache::Exec,
&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 <what>
/// 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 @@ fn run_custom(&mut self, exec: &mut workcache::Exec,
// 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 @@ fn run(&self, cmd: &str, args: ~[~str]) {
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 @@ fn run(&self, cmd: &str, args: ~[~str]) {
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 @@ fn run(&self, cmd: &str, args: ~[~str]) {
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 @@ fn run(&self, cmd: &str, args: ~[~str]) {
dest,
self.context.use_rust_path_hack,
pkgid.clone());
self.install(src, &Everything);
self.install(src, &WhatToBuild::new(MaybeCustom, Everything));
};
}
}
@ -354,7 +363,8 @@ fn run(&self, cmd: &str, args: ~[~str]) {
}
"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 @@ fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) {
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 @@ fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) {
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 @@ fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) {
// 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 @@ fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) {
// 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 @@ fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) {
}
}
// Build it!
pkg_src.build(self, cfgs);
pkg_src.build(self, cfgs, []);
}
}
@ -551,6 +566,8 @@ fn install(&self, mut pkg_src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str,
// 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,

View File

@ -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

View File

@ -28,7 +28,7 @@
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};

View File

@ -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 <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.
pub fn assert_true() {
assert!(true);
}

View File

@ -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 <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.
pub fn do_nothing() {
}

View File

@ -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 <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.
extern mod std;
pub mod foo;
pub mod bar;

View File

@ -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 <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.
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);
}
}

View File

@ -21,4 +21,4 @@
pub mod foo;
pub mod bar;
#[path = "../../build/fancy_lib/generated.rs"] pub mod generated;
#[path = "../../build/fancy-lib/generated.rs"] pub mod generated;

View File

@ -15,42 +15,37 @@
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, ~[]);
}

View File

@ -27,11 +27,13 @@
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 @@ fn visit_view_item(&mut self, vi: &ast::view_item, env: ()) {
};
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 @@ fn visit_view_item(&mut self, vi: &ast::view_item, env: ()) {
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());