Skip to content

Commit 02e0c39

Browse files
committed
Auto merge of #7460 - alexcrichton:panic-abort-tests, r=ehuss
Support rustc's `-Z panic-abort-tests` in Cargo Recently added in rust-lang/rust#64158 the `-Z panic-abort-tests` flag to the compiler itself will activate a mode in the `test` crate which enables running tests even if they're compiled with `panic=abort`. It effectively runs a test-per-process. This commit brings the same support to Cargo, adding a `-Z panic-abort-tests` flag to Cargo which allows building tests in `panic=abort` mode. While I wanted to be sure to add support for this in Cargo before we stabilize the flag in `rustc`, I don't actually know how we're going to stabilize this here. Today Cargo will automatically switch test targets to `panic=unwind`, and so if we actually were to stabilize this flag then this configuration would break: [profile.dev] panic = 'abort' In that case tests would be compiled with `panic=unwind` (due to how profiles work today) which would clash with crates also being compiled with `panic=abort`. I'm hopeful though that we can perhaps either figure out a solution for this and maybe even integrate it with the ongoing profiles work.
2 parents eb12fff + 269624f commit 02e0c39

File tree

8 files changed

+244
-37
lines changed

8 files changed

+244
-37
lines changed

src/cargo/core/compiler/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,17 @@ fn build_base_args<'a, 'cfg>(
799799

800800
if test && unit.target.harness() {
801801
cmd.arg("--test");
802+
803+
// Cargo has historically never compiled `--test` binaries with
804+
// `panic=abort` because the `test` crate itself didn't support it.
805+
// Support is now upstream, however, but requires an unstable flag to be
806+
// passed when compiling the test. We require, in Cargo, an unstable
807+
// flag to pass to rustc, so register that here. Eventually this flag
808+
// will simply not be needed when the behavior is stabilized in the Rust
809+
// compiler itself.
810+
if *panic == PanicStrategy::Abort {
811+
cmd.arg("-Zpanic-abort-tests");
812+
}
802813
} else if test {
803814
cmd.arg("--cfg").arg("test");
804815
}

src/cargo/core/compiler/unit_dependencies.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ fn deps_of_roots<'a, 'cfg>(roots: &[Unit<'a>], mut state: &mut State<'a, 'cfg>)
161161
// without, once for `--test`). In particular, the lib included for
162162
// Doc tests and examples are `Build` mode here.
163163
let unit_for = if unit.mode.is_any_test() || state.bcx.build_config.test() {
164-
UnitFor::new_test()
164+
UnitFor::new_test(state.bcx.config)
165165
} else if unit.target.is_custom_build() {
166166
// This normally doesn't happen, except `clean` aggressively
167167
// generates all units.

src/cargo/core/features.rs

+2
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ pub struct CliUnstable {
340340
pub build_std: Option<Vec<String>>,
341341
pub timings: Option<Vec<String>>,
342342
pub doctest_xcompile: bool,
343+
pub panic_abort_tests: bool,
343344
}
344345

345346
impl CliUnstable {
@@ -399,6 +400,7 @@ impl CliUnstable {
399400
}
400401
"timings" => self.timings = Some(parse_timings(v)),
401402
"doctest-xcompile" => self.doctest_xcompile = true,
403+
"panic-abort-tests" => self.panic_abort_tests = true,
402404
_ => failure::bail!("unknown `-Z` flag specified: {}", k),
403405
}
404406

src/cargo/core/profiles.rs

+83-32
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ impl Profiles {
314314
mode: CompileMode,
315315
profile_kind: ProfileKind,
316316
) -> Profile {
317-
let profile_name = if !self.named_profiles_enabled {
317+
let (profile_name, inherits) = if !self.named_profiles_enabled {
318318
// With the feature disabled, we degrade `--profile` back to the
319319
// `--release` and `--debug` predicates, and convert back from
320320
// ProfileKind::Custom instantiation.
@@ -328,9 +328,9 @@ impl Profiles {
328328
match mode {
329329
CompileMode::Test | CompileMode::Bench => {
330330
if release {
331-
"bench"
331+
("bench", Some("release"))
332332
} else {
333-
"test"
333+
("test", Some("dev"))
334334
}
335335
}
336336
CompileMode::Build
@@ -342,25 +342,33 @@ impl Profiles {
342342
// ancestor's profile. However, `cargo clean -p` can hit this
343343
// path.
344344
if release {
345-
"release"
345+
("release", None)
346346
} else {
347-
"dev"
347+
("dev", None)
348348
}
349349
}
350-
CompileMode::Doc { .. } => "doc",
350+
CompileMode::Doc { .. } => ("doc", None),
351351
}
352352
} else {
353-
profile_kind.name()
353+
(profile_kind.name(), None)
354354
};
355355
let maker = match self.by_name.get(profile_name) {
356356
None => panic!("Profile {} undefined", profile_name),
357357
Some(r) => r,
358358
};
359359
let mut profile = maker.get_profile(Some(pkg_id), is_member, unit_for);
360-
// `panic` should not be set for tests/benches, or any of their
361-
// dependencies.
362-
if !unit_for.is_panic_abort_ok() || mode.is_any_test() {
363-
profile.panic = PanicStrategy::Unwind;
360+
361+
// Dealing with `panic=abort` and `panic=unwind` requires some special
362+
// treatment. Be sure to process all the various options here.
363+
match unit_for.panic_setting() {
364+
PanicSetting::AlwaysUnwind => profile.panic = PanicStrategy::Unwind,
365+
PanicSetting::ReadProfile => {}
366+
PanicSetting::Inherit => {
367+
if let Some(inherits) = inherits {
368+
let maker = self.by_name.get(inherits).unwrap();
369+
profile.panic = maker.get_profile(Some(pkg_id), is_member, unit_for).panic;
370+
}
371+
}
364372
}
365373

366374
// Incremental can be globally overridden.
@@ -880,11 +888,25 @@ pub struct UnitFor {
880888
/// any of its dependencies. This enables `build-override` profiles for
881889
/// these targets.
882890
build: bool,
883-
/// This is true if it is *allowed* to set the `panic=abort` flag. Currently
884-
/// this is false for test/bench targets and all their dependencies, and
885-
/// "for_host" units such as proc macro and custom build scripts and their
886-
/// dependencies.
887-
panic_abort_ok: bool,
891+
/// How Cargo processes the `panic` setting or profiles. This is done to
892+
/// handle test/benches inheriting from dev/release, as well as forcing
893+
/// `for_host` units to always unwind.
894+
panic_setting: PanicSetting,
895+
}
896+
897+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
898+
enum PanicSetting {
899+
/// Used to force a unit to always be compiled with the `panic=unwind`
900+
/// strategy, notably for build scripts, proc macros, etc.
901+
AlwaysUnwind,
902+
903+
/// Indicates that this unit will read its `profile` setting and use
904+
/// whatever is configured there.
905+
ReadProfile,
906+
907+
/// This unit will ignore its `panic` setting in its profile and will
908+
/// instead inherit it from the `dev` or `release` profile, as appropriate.
909+
Inherit,
888910
}
889911

890912
impl UnitFor {
@@ -893,42 +915,67 @@ impl UnitFor {
893915
pub fn new_normal() -> UnitFor {
894916
UnitFor {
895917
build: false,
896-
panic_abort_ok: true,
918+
panic_setting: PanicSetting::ReadProfile,
897919
}
898920
}
899921

900922
/// A unit for a custom build script or its dependencies.
901923
pub fn new_build() -> UnitFor {
902924
UnitFor {
903925
build: true,
904-
panic_abort_ok: false,
926+
// Force build scripts to always use `panic=unwind` for now to
927+
// maximally share dependencies with procedural macros.
928+
panic_setting: PanicSetting::AlwaysUnwind,
905929
}
906930
}
907931

908932
/// A unit for a proc macro or compiler plugin or their dependencies.
909933
pub fn new_compiler() -> UnitFor {
910934
UnitFor {
911935
build: false,
912-
panic_abort_ok: false,
936+
// Force plugins to use `panic=abort` so panics in the compiler do
937+
// not abort the process but instead end with a reasonable error
938+
// message that involves catching the panic in the compiler.
939+
panic_setting: PanicSetting::AlwaysUnwind,
913940
}
914941
}
915942

916943
/// A unit for a test/bench target or their dependencies.
917-
pub fn new_test() -> UnitFor {
944+
///
945+
/// Note that `config` is taken here for unstable CLI features to detect
946+
/// whether `panic=abort` is supported for tests. Historical versions of
947+
/// rustc did not support this, but newer versions do with an unstable
948+
/// compiler flag.
949+
pub fn new_test(config: &Config) -> UnitFor {
918950
UnitFor {
919951
build: false,
920-
panic_abort_ok: false,
952+
// We're testing out an unstable feature (`-Zpanic-abort-tests`)
953+
// which inherits the panic setting from the dev/release profile
954+
// (basically avoid recompiles) but historical defaults required
955+
// that we always unwound.
956+
panic_setting: if config.cli_unstable().panic_abort_tests {
957+
PanicSetting::Inherit
958+
} else {
959+
PanicSetting::AlwaysUnwind
960+
},
921961
}
922962
}
923963

924964
/// Creates a variant based on `for_host` setting.
925965
///
926-
/// When `for_host` is true, this clears `panic_abort_ok` in a sticky fashion so
927-
/// that all its dependencies also have `panic_abort_ok=false`.
966+
/// When `for_host` is true, this clears `panic_abort_ok` in a sticky
967+
/// fashion so that all its dependencies also have `panic_abort_ok=false`.
968+
/// This'll help ensure that once we start compiling for the host platform
969+
/// (build scripts, plugins, proc macros, etc) we'll share the same build
970+
/// graph where everything is `panic=unwind`.
928971
pub fn with_for_host(self, for_host: bool) -> UnitFor {
929972
UnitFor {
930973
build: self.build || for_host,
931-
panic_abort_ok: self.panic_abort_ok && !for_host,
974+
panic_setting: if for_host {
975+
PanicSetting::AlwaysUnwind
976+
} else {
977+
self.panic_setting
978+
},
932979
}
933980
}
934981

@@ -938,28 +985,32 @@ impl UnitFor {
938985
self.build
939986
}
940987

941-
/// Returns `true` if this unit is allowed to set the `panic` compiler flag.
942-
pub fn is_panic_abort_ok(self) -> bool {
943-
self.panic_abort_ok
988+
/// Returns how `panic` settings should be handled for this profile
989+
fn panic_setting(self) -> PanicSetting {
990+
self.panic_setting
944991
}
945992

946993
/// All possible values, used by `clean`.
947994
pub fn all_values() -> &'static [UnitFor] {
948-
static ALL: [UnitFor; 3] = [
995+
static ALL: &[UnitFor] = &[
949996
UnitFor {
950997
build: false,
951-
panic_abort_ok: true,
998+
panic_setting: PanicSetting::ReadProfile,
952999
},
9531000
UnitFor {
9541001
build: true,
955-
panic_abort_ok: false,
1002+
panic_setting: PanicSetting::AlwaysUnwind,
1003+
},
1004+
UnitFor {
1005+
build: false,
1006+
panic_setting: PanicSetting::AlwaysUnwind,
9561007
},
9571008
UnitFor {
9581009
build: false,
959-
panic_abort_ok: false,
1010+
panic_setting: PanicSetting::Inherit,
9601011
},
9611012
];
962-
&ALL
1013+
ALL
9631014
}
9641015
}
9651016

src/cargo/ops/cargo_compile.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ fn generate_targets<'a>(
642642
) -> CargoResult<Vec<Unit<'a>>> {
643643
// Helper for creating a `Unit` struct.
644644
let new_unit = |pkg: &'a Package, target: &'a Target, target_mode: CompileMode| {
645-
let unit_for = if bcx.build_config.mode.is_any_test() {
645+
let unit_for = if target_mode.is_any_test() {
646646
// NOTE: the `UnitFor` here is subtle. If you have a profile
647647
// with `panic` set, the `panic` flag is cleared for
648648
// tests/benchmarks and their dependencies. If this
@@ -661,7 +661,7 @@ fn generate_targets<'a>(
661661
//
662662
// Forcing the lib to be compiled three times during `cargo
663663
// test` is probably also not desirable.
664-
UnitFor::new_test()
664+
UnitFor::new_test(bcx.config)
665665
} else if target.for_host() {
666666
// Proc macro / plugin should not have `panic` set.
667667
UnitFor::new_compiler()

src/doc/src/reference/unstable.md

+14
Original file line numberDiff line numberDiff line change
@@ -475,3 +475,17 @@ that information for change-detection (if any binary dependency changes, then
475475
the crate will be rebuilt). The primary use case is for building the compiler
476476
itself, which has implicit dependencies on the standard library that would
477477
otherwise be untracked for change-detection.
478+
479+
### panic-abort-tests
480+
481+
The `-Z panic-abort-tests` flag will enable nightly support to compile test
482+
harness crates with `-Cpanic=abort`. Without this flag Cargo will compile tests,
483+
and everything they depend on, with `-Cpanic=unwind` because it's the only way
484+
`test`-the-crate knows how to operate. As of [rust-lang/rust#64158], however,
485+
the `test` crate supports `-C panic=abort` with a test-per-process, and can help
486+
avoid compiling crate graphs multiple times.
487+
488+
It's currently unclear how this feature will be stabilized in Cargo, but we'd
489+
like to stabilize it somehow!
490+
491+
[rust-lang/rust#64158]: https://github.com/rust-lang/rust/pull/64158

tests/testsuite/cross_compile.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,13 @@ fn plugin_deps() {
203203
204204
extern crate rustc_driver;
205205
extern crate syntax;
206+
extern crate syntax_expand;
206207
207208
use rustc_driver::plugin::Registry;
208209
use syntax::tokenstream::TokenStream;
209210
use syntax::source_map::Span;
210211
use syntax::ast::*;
211-
use syntax::ext::base::{ExtCtxt, MacEager, MacResult};
212+
use syntax_expand::base::{ExtCtxt, MacEager, MacResult};
212213
213214
#[plugin_registrar]
214215
pub fn foo(reg: &mut Registry) {
@@ -298,13 +299,14 @@ fn plugin_to_the_max() {
298299
299300
extern crate rustc_driver;
300301
extern crate syntax;
302+
extern crate syntax_expand;
301303
extern crate baz;
302304
303305
use rustc_driver::plugin::Registry;
304306
use syntax::tokenstream::TokenStream;
305307
use syntax::source_map::Span;
306308
use syntax::ast::*;
307-
use syntax::ext::base::{ExtCtxt, MacEager, MacResult};
309+
use syntax_expand::base::{ExtCtxt, MacEager, MacResult};
308310
use syntax::ptr::P;
309311
310312
#[plugin_registrar]

0 commit comments

Comments
 (0)