diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 89988c391b..8673c56be6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -122,10 +122,6 @@ jobs: with: cache-targets: true # cache build artifacts cache-bin: true # cache the ~/.cargo/bin directory - - name: "Clear macOS panic-abort cache" - if: runner.os == 'macOS' - shell: bash - run: rm -rf target/panic-abort - name: "Remove nextest CI report" shell: bash run: rm -rf target/nextest/ci/junit.xml diff --git a/Cargo.toml b/Cargo.toml index 20d3714041..ee8d1cafab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -117,6 +117,10 @@ opt-level = 3 incremental = false codegen-units = 1 +[profile.panic-abort] +inherits = "dev" +panic = "abort" + [patch.crates-io] # proptest pulls in a dependency on libm, which changes the runtime of some math functions # so benchmarks are not measuring the same thing as the release build. This patch removes diff --git a/bin_tests/src/artifacts.rs b/bin_tests/src/artifacts.rs index 7963dc9fe7..953dabda31 100644 --- a/bin_tests/src/artifacts.rs +++ b/bin_tests/src/artifacts.rs @@ -18,23 +18,21 @@ pub fn crashtracker_receiver(profile: BuildProfile) -> ArtifactsBuild { } /// Creates an ArtifactsBuild for the crashtracker_bin_test binary. -pub fn crashtracker_bin_test(profile: BuildProfile, panic_abort: bool) -> ArtifactsBuild { +pub fn crashtracker_bin_test(profile: BuildProfile) -> ArtifactsBuild { ArtifactsBuild { name: "crashtracker_bin_test".to_owned(), build_profile: profile, artifact_type: ArtifactType::Bin, - panic_abort: if panic_abort { Some(true) } else { None }, ..Default::default() } } /// Creates an ArtifactsBuild for the crashing_test_app binary. -pub fn crashing_app(profile: BuildProfile, panic_abort: bool) -> ArtifactsBuild { +pub fn crashing_app(profile: BuildProfile) -> ArtifactsBuild { ArtifactsBuild { name: "crashing_test_app".to_owned(), build_profile: profile, artifact_type: ArtifactType::Bin, - panic_abort: if panic_abort { Some(true) } else { None }, ..Default::default() } } @@ -79,7 +77,7 @@ pub struct StandardArtifacts { impl StandardArtifacts { pub fn new(profile: BuildProfile) -> Self { Self { - crashtracker_bin: crashtracker_bin_test(profile, false), + crashtracker_bin: crashtracker_bin_test(profile), crashtracker_receiver: crashtracker_receiver(profile), } } @@ -95,17 +93,17 @@ pub fn all_prebuild_artifacts() -> Vec { // Standard artifacts for both Debug and Release profiles for profile in [BuildProfile::Debug, BuildProfile::Release] { - artifacts.push(crashtracker_bin_test(profile, false)); + artifacts.push(crashtracker_bin_test(profile)); artifacts.push(crashtracker_receiver(profile)); artifacts.push(crashtracker_unix_socket_receiver(profile)); artifacts.push(test_the_tests(profile)); artifacts.push(profiling_ffi(profile)); - artifacts.push(crashing_app(profile, false)); + artifacts.push(crashing_app(profile)); } // Panic abort variants (used by panic hook tests) - artifacts.push(crashtracker_bin_test(BuildProfile::Debug, true)); - artifacts.push(crashing_app(BuildProfile::Debug, true)); + artifacts.push(crashtracker_bin_test(BuildProfile::PanicAbort)); + artifacts.push(crashing_app(BuildProfile::PanicAbort)); artifacts } diff --git a/bin_tests/src/lib.rs b/bin_tests/src/lib.rs index ec892d4ba8..845233b414 100644 --- a/bin_tests/src/lib.rs +++ b/bin_tests/src/lib.rs @@ -34,27 +34,17 @@ fn get_base_target_dir() -> &'static PathBuf { }) } -/// Returns the target directory for a specific build configuration. -/// For builds with variants (like panic_abort), returns a variant-specific subdirectory. -fn get_target_dir_for_build(c: &ArtifactsBuild) -> PathBuf { - let base = get_base_target_dir().clone(); - - // If this is a variant build (e.g., panic_abort), use a variant-specific target directory - if c.panic_abort == Some(true) { - base.join("panic-abort") - } else { - base - } -} - /// Computes the path where the artifact will be located after building. pub fn compute_artifact_path(c: &ArtifactsBuild) -> anyhow::Result { - let target_dir = get_target_dir_for_build(c); + let mut artifact_path = get_base_target_dir().clone(); - let mut artifact_path = target_dir; + // Each profile writes to its own `target/` subdirectory. The `PanicAbort` + // profile is a custom Cargo profile, so it lives directly under + // `target/panic-abort/` rather than under `debug`/`release`. artifact_path.push(match c.build_profile { BuildProfile::Debug => "debug", BuildProfile::Release => "release", + BuildProfile::PanicAbort => "panic-abort", }); match c.artifact_type { @@ -101,6 +91,12 @@ pub enum BuildProfile { #[default] Debug, Release, + /// Debug opt-level built with `panic = "abort"` (via the dedicated + /// `panic-abort` Cargo profile). Used by crash-handler tests that must + /// exercise the abort path. Making this a `BuildProfile` variant — rather than + /// a separate flag — keeps illegal combinations (e.g. release + panic=abort) + /// unrepresentable. + PanicAbort, } #[derive(Debug, Default, PartialEq, Eq, Hash, Clone)] @@ -110,7 +106,6 @@ pub struct ArtifactsBuild { pub artifact_type: ArtifactType, pub build_profile: BuildProfile, pub triple_target: Option, - pub panic_abort: Option, } fn cargo_build_artifact(c: &ArtifactsBuild) -> anyhow::Result { @@ -119,15 +114,33 @@ fn cargo_build_artifact(c: &ArtifactsBuild) -> anyhow::Result { let mut build_cmd = process::Command::new(env!("CARGO")); build_cmd.arg("build"); - // For variant builds (like panic_abort), use a separate target directory - // so they don't conflict with standard builds - let target_dir = get_target_dir_for_build(c); - if target_dir != *get_base_target_dir() { - build_cmd.arg("--target-dir").arg(&target_dir); - } - - if let BuildProfile::Release = c.build_profile { - build_cmd.arg("--release"); + match c.build_profile { + BuildProfile::Debug => {} + BuildProfile::Release => { + build_cmd.arg("--release"); + } + BuildProfile::PanicAbort => { + // Build with the dedicated `panic-abort` Cargo profile (defined in the + // workspace root Cargo.toml with `panic = "abort"`), so artifacts land + // in `target/panic-abort/` under the standard target layout and + // Swatinem/rust-cache manages and cleans them like any other profile. + build_cmd.arg("--profile").arg("panic-abort"); + + // `-C force-unwind-tables=yes` has no profile equivalent, so it stays a + // scoped RUSTFLAG applied only to this build invocation. `panic=abort` + // strips the `.eh_frame` unwind tables that the aarch64 backtrace + // unwinder relies on; without them `RUST_BACKTRACE` can loop forever on + // certain binary layouts (rust-lang/rust#123733), hanging the test until + // the runner OOMs. Keep until #123733 (fix: rust-lang/rust#143613) ships + // in a stable toolchain. + let existing_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default(); + let new_rustflags = if existing_rustflags.is_empty() { + "-C force-unwind-tables=yes".to_string() + } else { + format!("{} -C force-unwind-tables=yes", existing_rustflags) + }; + build_cmd.env("RUSTFLAGS", new_rustflags); + } } match c.artifact_type { @@ -135,25 +148,6 @@ fn cargo_build_artifact(c: &ArtifactsBuild) -> anyhow::Result { ArtifactType::Bin => build_cmd.arg("--bin"), }; - if c.panic_abort == Some(true) { - // `-C panic=abort` suppresses `.eh_frame` unwind tables, which the aarch64 - // backtrace unwinder relies on; without them `RUST_BACKTRACE` can loop forever - // on certain binary layouts (rust-lang/rust#123733), hanging the test until the - // runner OOMs. Force the tables back on so backtraces terminate. This is - // layout-sensitive, so it can appear/disappear across unrelated changes; keep - // until #123733 (fix: rust-lang/rust#143613) ships in a stable toolchain. - let existing_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default(); - let new_rustflags = if existing_rustflags.is_empty() { - "-C panic=abort -C force-unwind-tables=yes".to_string() - } else { - format!( - "{} -C panic=abort -C force-unwind-tables=yes", - existing_rustflags - ) - }; - build_cmd.env("RUSTFLAGS", new_rustflags); - } - build_cmd.arg(&c.name); let output = build_cmd.output()?; diff --git a/bin_tests/tests/crashtracker_bin_test.rs b/bin_tests/tests/crashtracker_bin_test.rs index 15859f32e9..1832894f77 100644 --- a/bin_tests/tests/crashtracker_bin_test.rs +++ b/bin_tests/tests/crashtracker_bin_test.rs @@ -1153,9 +1153,9 @@ fn test_crash_tracking_bin_segfault() { fn test_crash_tracking_app(crash_type: &str) { use bin_tests::test_runner::run_custom_crash_test; - // Set up custom artifacts: receiver + crashing_test_app with panic_abort + // Set up custom artifacts: receiver + crashing_test_app with panic=abort let crashtracker_receiver = artifacts::crashtracker_receiver(BuildProfile::Release); - let crashing_app = artifacts::crashing_app(BuildProfile::Debug, true); + let crashing_app = artifacts::crashing_app(BuildProfile::PanicAbort); let artifacts_map = fetch_built_artifacts(&[&crashtracker_receiver, &crashing_app]).unwrap(); @@ -1229,7 +1229,7 @@ fn test_panic_hook_mode(mode: &str, expected_category: &str, expected_panic_mess // Set up custom artifacts: receiver + crashtracker_bin_test let crashtracker_receiver = artifacts::crashtracker_receiver(BuildProfile::Release); - let crashtracker_bin_test = artifacts::crashtracker_bin_test(BuildProfile::Debug, true); + let crashtracker_bin_test = artifacts::crashtracker_bin_test(BuildProfile::PanicAbort); let artifacts_map = fetch_built_artifacts(&[&crashtracker_receiver, &crashtracker_bin_test]).unwrap(); @@ -1303,7 +1303,7 @@ fn test_crash_tracking_callstack() { // Set up custom artifacts: receiver + crashing_test_app (in Debug mode) let crashtracker_receiver = artifacts::crashtracker_receiver(BuildProfile::Release); // compile in debug so we avoid inlining and can check the callchain - let crashing_app = artifacts::crashing_app(BuildProfile::Debug, false); + let crashing_app = artifacts::crashing_app(BuildProfile::Debug); let artifacts_map = fetch_built_artifacts(&[&crashtracker_receiver, &crashing_app]).unwrap(); @@ -2508,7 +2508,7 @@ fn setup_test_fixtures<'a>(crates: &[&'a ArtifactsBuild]) -> TestFixtures<'a> { fn setup_crashtracking_crates( crash_tracking_receiver_profile: BuildProfile, ) -> (ArtifactsBuild, ArtifactsBuild) { - let crashtracker_bin = artifacts::crashtracker_bin_test(crash_tracking_receiver_profile, false); + let crashtracker_bin = artifacts::crashtracker_bin_test(crash_tracking_receiver_profile); let crashtracker_receiver = artifacts::crashtracker_receiver(crash_tracking_receiver_profile); (crashtracker_bin, crashtracker_receiver) } diff --git a/bin_tests/tests/test_the_tests.rs b/bin_tests/tests/test_the_tests.rs index 8601ef9ee3..b5955989c8 100644 --- a/bin_tests/tests/test_the_tests.rs +++ b/bin_tests/tests/test_the_tests.rs @@ -34,7 +34,6 @@ fn test_the_tests_inner(profile: BuildProfile) { build_profile: profile, artifact_type: ArtifactType::CDylib, triple_target: None, - panic_abort: None, }, &test_the_tests, ];