Skip to content

Commit

Permalink
auto merge of #617 : alexcrichton/cargo/issue-597, r=brson
Browse files Browse the repository at this point in the history
Ensure that the dynamic linker search path contains the location of the output
directories for these dependencies when compiling with plugins.

Closes #597
  • Loading branch information
bors committed Sep 24, 2014
2 parents 08447e7 + 0c8f6db commit b491ffe
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 23 deletions.
68 changes: 45 additions & 23 deletions src/cargo/ops/cargo_rustc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,15 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,

// Traverse the entire dependency graph looking for -L paths to pass for
// native dependencies.
cmd = push_native_dirs(cmd, &layout, package, cx, &mut HashSet::new());
let mut dirs = Vec::new();
each_dep(package, cx, |pkg| {
if pkg.get_manifest().get_build().len() > 0 {
dirs.push(layout.native(pkg));
}
});
for dir in dirs.into_iter() {
cmd = cmd.arg("-L").arg(dir);
}

for &(_, target) in cx.dep_targets(package).iter() {
cmd = try!(link_to(cmd, target, cx, kind, Dependency));
Expand Down Expand Up @@ -471,37 +479,51 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
}
return Ok(cmd);
}

fn push_native_dirs(mut cmd: ProcessBuilder, layout: &layout::LayoutProxy,
pkg: &Package, cx: &Context,
visited: &mut HashSet<PackageId>) -> ProcessBuilder {
if !visited.insert(pkg.get_package_id().clone()) { return cmd }

if pkg.get_manifest().get_build().len() > 0 {
cmd = cmd.arg("-L").arg(layout.native(pkg));
}

match cx.resolve.deps(pkg.get_package_id()) {
Some(mut pkgids) => {
pkgids.fold(cmd, |cmd, dep_id| {
let dep = cx.get_package(dep_id);
push_native_dirs(cmd, layout, dep, cx, visited)
})
}
None => cmd
}
}
}

pub fn process<T: ToCStr>(cmd: T, pkg: &Package, cx: &Context) -> ProcessBuilder {
// When invoking a tool, we need the *host* deps directory in the dynamic
// library search path for plugins and such which have dynamic dependencies.
let layout = cx.layout(KindPlugin);
let mut search_path = DynamicLibrary::search_path();
search_path.push(cx.layout(KindPlugin).deps().clone());
let search_path = os::join_paths(search_path.as_slice()).unwrap();
search_path.push(layout.deps().clone());

// Also be sure to pick up any native build directories required by plugins
// or their dependencies
let mut native_search_paths = HashSet::new();
for &(dep, target) in cx.dep_targets(pkg).iter() {
if !target.get_profile().is_plugin() { continue }
each_dep(dep, cx, |dep| {
if dep.get_manifest().get_build().len() > 0 {
native_search_paths.insert(layout.native(dep));
}
});
}
search_path.extend(native_search_paths.into_iter());

// We want to use the same environment and such as normal processes, but we
// want to override the dylib search path with the one we just calculated.
let search_path = os::join_paths(search_path.as_slice()).unwrap();
cx.compilation.process(cmd, pkg)
.env(DynamicLibrary::envvar(), Some(search_path.as_slice()))
}

fn each_dep<'a>(pkg: &Package, cx: &'a Context, f: |&'a Package|) {
let mut visited = HashSet::new();
let pkg = cx.get_package(pkg.get_package_id());
visit_deps(pkg, cx, &mut visited, f);

fn visit_deps<'a>(pkg: &'a Package, cx: &'a Context,
visited: &mut HashSet<&'a PackageId>,
f: |&'a Package|) {
if !visited.insert(pkg.get_package_id()) { return }
f(pkg);
let mut deps = match cx.resolve.deps(pkg.get_package_id()) {
Some(deps) => deps,
None => return,
};
for dep_id in deps {
visit_deps(cx.get_package(dep_id), cx, visited, |p| f(p))
}
}
}
89 changes: 89 additions & 0 deletions tests/test_cargo_compile_plugins.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::io::fs;
use std::os;

use support::{project, execs, cargo_dir};
use hamcrest::assert_that;

Expand Down Expand Up @@ -78,3 +81,89 @@ test!(plugin_to_the_max {
assert_that(foo.process(cargo_dir().join("cargo")).arg("doc"),
execs().with_status(0));
})

test!(plugin_with_dynamic_native_dependency {
let build = project("build")
.file("Cargo.toml", r#"
[package]
name = "build"
version = "0.0.1"
authors = []
[lib]
name = "build"
crate-type = ["dylib"]
"#)
.file("src/main.rs", r#"
use std::io::fs;
use std::os;
fn main() {
let src = Path::new(os::getenv("SRC").unwrap());
let dst = Path::new(os::getenv("OUT_DIR").unwrap());
let dst = dst.join(src.filename().unwrap());
fs::rename(&src, &dst).unwrap();
}
"#)
.file("src/lib.rs", r#"
#[no_mangle]
pub extern fn foo() {}
"#);
assert_that(build.cargo_process("build"),
execs().with_status(0).with_stderr(""));
let src = build.root().join("target");
let lib = fs::readdir(&src).unwrap().into_iter().find(|lib| {
let lib = lib.filename_str().unwrap();
lib.starts_with(os::consts::DLL_PREFIX) &&
lib.ends_with(os::consts::DLL_SUFFIX)
}).unwrap();
let libname = lib.filename_str().unwrap();
let libname = libname.slice(os::consts::DLL_PREFIX.len(),
libname.len() - os::consts::DLL_SUFFIX.len());

let foo = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies.bar]
path = "bar"
"#)
.file("src/main.rs", r#"
#![feature(phase)]
#[phase(plugin)] extern crate bar;
fn main() {}
"#)
.file("bar/Cargo.toml", format!(r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
build = '{}'
[lib]
name = "bar"
plugin = true
"#, build.bin("build").display()))
.file("bar/src/lib.rs", format!(r#"
#![feature(plugin_registrar)]
extern crate rustc;
use rustc::plugin::Registry;
#[link(name = "{}")]
extern {{ fn foo(); }}
#[plugin_registrar]
pub fn bar(_reg: &mut Registry) {{
unsafe {{ foo() }}
}}
"#, libname));

assert_that(foo.cargo_process("build").env("SRC", Some(lib.as_vec())),
execs().with_status(0));
})

0 comments on commit b491ffe

Please sign in to comment.