authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-03 14:25:27-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-03 15:20:12-07:00
log26d313ff895bef3a6121acee7d27aaa42b494384
tree15ead0ee403f12c291997c75291fc941d2bf14a6
parent2fbbd55d139b80699e548bc1014aeef2c714bdbc
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

chore: release builds abort on a panic instead of unwinding

The panic hook still logs the backtrace from the unwind tables, which stay. On Windows the hook shows the stopped alert itself, as no catch_unwind outlives the abort, and x86_64's std is built with panic_abort beside panic_unwind. Release executables after the debug split: Linux x86_64 47.08 to 42.15 MB (-4.92), macOS aarch64 34.17 to 29.37 MB (-4.80), Windows x86_64 37.28 to 33.90 MB (-3.38). Linux keeps 1.14 MB of .eh_frame; landing pads and their tables go. Assisted-by: claude-opus-5.5

3 files changed, 24 insertions(+), 21 deletions(-)

Cargo.toml+1
...@@ -16,6 +16,7 @@ sctk-adwaita = { path = "vendor/sctk-adwaita" }...@@ -16,6 +16,7 @@ sctk-adwaita = { path = "vendor/sctk-adwaita" }
16[profile.release]16[profile.release]
17lto = "fat"17lto = "fat"
18codegen-units = 118codegen-units = 1
19panic = "abort"
1920
20# Code that runs at a person's pace, not a frame's: system services, the network, file and21# Code that runs at a person's pace, not a frame's: system services, the network, file and
21# image formats, shader translation.22# image formats, shader translation.
crates/snowbound/src/windows.rs+22-20
...@@ -935,42 +935,44 @@ pub fn cover_border_line(_: &mut Ui, _: f32) {}...@@ -935,42 +935,44 @@ pub fn cover_border_line(_: &mut Ui, _: f32) {}
935pub fn with_pool(935pub fn with_pool(
936 run: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,936 run: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
937) -> Result<(), Box<dyn std::error::Error>> {937) -> Result<(), Box<dyn std::error::Error>> {
938 use std::panic;938 let details = log_stderr().map(|log| format!("The log is at {}.", log.display()));
939 let log = log_stderr();
940 let (major, minor, build) = version();939 let (major, minor, build) = version();
941 eprintln!(940 eprintln!(
942 "Snowbound {} on Windows {major}.{minor}.{build}",941 "Snowbound {} on Windows {major}.{minor}.{build}",
943 option_env!("SNOWBOUND_BUILD").unwrap_or("development")942 option_env!("SNOWBOUND_BUILD").unwrap_or("development")
944 );943 );
945 panic::set_hook(Box::new(|info| {944 let shown = details.clone();
945 std::panic::set_hook(Box::new(move |info| {
946 let thread = std::thread::current();946 let thread = std::thread::current();
947 let backtrace = std::backtrace::Backtrace::force_capture();947 let backtrace = std::backtrace::Backtrace::force_capture();
948 eprintln!(948 eprintln!(
949 "Thread {:?} {info}\n{backtrace}",949 "Thread {:?} {info}\n{backtrace}",
950 thread.name().unwrap_or("")950 thread.name().unwrap_or("")
951 );951 );
952 // The panic aborts the process once this returns, taking an alert's thread with it.
953 if cfg!(panic = "abort")
954 && let Some(details) = &shown
955 {
956 let text = wide(format!(
957 "Snowbound stopped because of a problem.\n\n{details}"
958 ));
959 let caption = wide("Snowbound");
960 let style = wm::MB_OK | wm::MB_ICONWARNING;
961 unsafe {
962 wm::MessageBoxW(std::ptr::null_mut(), text.as_ptr(), caption.as_ptr(), style)
963 };
964 }
952 }));965 }));
953 unsafe {966 unsafe {
954 windows_sys::Win32::System::Diagnostics::Debug::AddVectoredExceptionHandler(0, Some(fault))967 windows_sys::Win32::System::Diagnostics::Debug::AddVectoredExceptionHandler(0, Some(fault))
955 };968 };
956 let result = panic::catch_unwind(panic::AssertUnwindSafe(run));969 let result = run();
957 let Some(log) = log else {970 if let (Err(error), Some(details)) = (&result, details) {
958 return result.unwrap_or_else(|payload| panic::resume_unwind(payload));971 // The window is gone, and a message box it owned wouldn't show.
959 };972 WINDOW.store(0, Ordering::Relaxed);
960 let details = format!("The log is at {}.", log.display());973 alert(&error.to_string(), &details);
961 // The window is gone, and a message box it owned wouldn't show.
962 WINDOW.store(0, Ordering::Relaxed);
963 match result {
964 Ok(Err(error)) => {
965 alert(&error.to_string(), &details);
966 Err(error)
967 }
968 Ok(done) => done,
969 Err(payload) => {
970 alert("Snowbound stopped because of a problem.", &details);
971 panic::resume_unwind(payload)
972 }
973 }974 }
975 result
974}976}
975977
976/// Points stderr at `snowbound.log` in the cache folder where it goes nowhere, keeping the978/// Points stderr at `snowbound.log` in the cache folder where it goes nowhere, keeping the
platform/windows/cargo.sh+1-1
...@@ -22,7 +22,7 @@ x86_64)...@@ -22,7 +22,7 @@ x86_64)
22 # raw-dylib imports (the windows crates) are made by llvm-mingw's dlltool. Cargo22 # raw-dylib imports (the windows crates) are made by llvm-mingw's dlltool. Cargo
23 # can't see link.sh's inputs; folding their hash into the flags relinks on change.23 # can't see link.sh's inputs; folding their hash into the flags relinks on change.
24 runtime=$(cat "$here/link.sh" "$here"/rt/* | shasum | cut -c1-12)24 runtime=$(cat "$here/link.sh" "$here"/rt/* | shasum | cut -c1-12)
25 set -- -Zbuild-std=std,panic_unwind \25 set -- -Zbuild-std=std,panic_abort,panic_unwind \
26 --config "target.$target.linker='$here/link.sh'" \26 --config "target.$target.linker='$here/link.sh'" \
27 --config "target.$target.rustflags=['-C','linker-flavor=gcc','-C','metadata=rt-$runtime','-C','dlltool=$LLVM_MINGW/bin/x86_64-w64-mingw32-dlltool']" "$@"27 --config "target.$target.rustflags=['-C','linker-flavor=gcc','-C','metadata=rt-$runtime','-C','dlltool=$LLVM_MINGW/bin/x86_64-w64-mingw32-dlltool']" "$@"
28 ;;28 ;;