diff --git a/src/cargo/ops/cargo_rustc/context.rs b/src/cargo/ops/cargo_rustc/context.rs index fb9acab3055..49236afea9d 100644 --- a/src/cargo/ops/cargo_rustc/context.rs +++ b/src/cargo/ops/cargo_rustc/context.rs @@ -39,6 +39,7 @@ pub struct Context<'a, 'cfg: 'a> { pub build_config: BuildConfig, pub build_scripts: HashMap, Arc>, pub links: Links<'a>, + pub used_in_plugin: HashSet>, host: Layout, target: Option, @@ -91,6 +92,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { build_scripts: HashMap::new(), build_explicit_deps: HashMap::new(), links: Links::new(), + used_in_plugin: HashSet::new(), }) } @@ -235,6 +237,41 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Ok(()) } + /// Builds up the `used_in_plugin` internal to this context from the list of + /// top-level units. + /// + /// This will recursively walk `units` and all of their dependencies to + /// determine which crate are going to be used in plugins or not. + pub fn build_used_in_plugin_map(&mut self, units: &[Unit<'a>]) + -> CargoResult<()> { + let mut visited = HashSet::new(); + for unit in units { + try!(self.walk_used_in_plugin_map(unit, + unit.target.for_host(), + &mut visited)); + } + Ok(()) + } + + fn walk_used_in_plugin_map(&mut self, + unit: &Unit<'a>, + is_plugin: bool, + visited: &mut HashSet<(Unit<'a>, bool)>) + -> CargoResult<()> { + if !visited.insert((*unit, is_plugin)) { + return Ok(()) + } + if is_plugin { + self.used_in_plugin.insert(*unit); + } + for unit in try!(self.dep_targets(unit)) { + try!(self.walk_used_in_plugin_map(&unit, + is_plugin || unit.target.for_host(), + visited)); + } + Ok(()) + } + /// Returns the appropriate directory layout for either a plugin or not. pub fn layout(&self, unit: &Unit) -> LayoutProxy { let primary = unit.pkg.package_id() == self.resolve.root(); diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index a743315b248..27719613905 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -89,6 +89,7 @@ pub fn compile_targets<'a, 'cfg: 'a>(ws: &Workspace<'cfg>, try!(cx.prepare()); try!(cx.probe_target_info(&units)); + try!(cx.build_used_in_plugin_map(&units)); try!(custom_build::build_map(&mut cx, &units)); for unit in units.iter() { @@ -512,8 +513,20 @@ fn build_base_args(cx: &Context, cmd.arg("-C").arg(&format!("opt-level={}", opt_level)); } + // If a panic mode was configured *and* we're not ever going to be used in a + // plugin, then we can compile with that panic mode. + // + // If we're used in a plugin then we'll eventually be linked to libsyntax + // most likely which isn't compiled with a custom panic mode, so we'll just + // get an error if we actually compile with that. This fixes `panic=abort` + // crates which have plugin dependencies, but unfortunately means that + // dependencies shared between the main application and plugins must be + // compiled without `panic=abort`. This isn't so bad, though, as the main + // application will still be compiled with `panic=abort`. if let Some(panic) = panic.as_ref() { - cmd.arg("-C").arg(format!("panic={}", panic)); + if !cx.used_in_plugin.contains(unit) { + cmd.arg("-C").arg(format!("panic={}", panic)); + } } // Disable LTO for host builds as prefer_dynamic and it are mutually diff --git a/tests/plugins.rs b/tests/plugins.rs index 2da8965da2b..cd762377ab9 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -274,3 +274,93 @@ fn native_plugin_dependency_with_custom_ar_linker() { [ERROR] could not exec the linker [..] ")); } + +#[test] +fn panic_abort_plugins() { + if !is_nightly() { + return + } + + let bar = project("bar") + .file("Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [profile.dev] + panic = 'abort' + + [dependencies] + foo = { path = "foo" } + "#) + .file("src/lib.rs", "") + .file("foo/Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + plugin = true + "#) + .file("foo/src/lib.rs", r#" + #![feature(rustc_private)] + extern crate syntax; + "#); + + assert_that(bar.cargo_process("build"), + execs().with_status(0)); +} + +#[test] +fn shared_panic_abort_plugins() { + if !is_nightly() { + return + } + + let bar = project("top") + .file("Cargo.toml", r#" + [package] + name = "top" + version = "0.0.1" + authors = [] + + [profile.dev] + panic = 'abort' + + [dependencies] + foo = { path = "foo" } + bar = { path = "bar" } + "#) + .file("src/lib.rs", " + extern crate bar; + ") + .file("foo/Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + plugin = true + + [dependencies] + bar = { path = "../bar" } + "#) + .file("foo/src/lib.rs", r#" + #![feature(rustc_private)] + extern crate syntax; + extern crate bar; + "#) + .file("bar/Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + "#) + .file("bar/src/lib.rs", ""); + + assert_that(bar.cargo_process("build"), + execs().with_status(0)); +}