From b84d42f5f9128d5d8f06011c97c5a303b09b00e8 Mon Sep 17 00:00:00 2001 From: skal Date: Sat, 7 Feb 2026 11:24:56 +0100 Subject: feat(build): Add FINAL_STRIP mode for maximum size optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented systematic fatal error checking infrastructure that can be stripped for final builds. This addresses the need to remove all error checking (abort() calls) from the production binary while maintaining safety during development. ## New Infrastructure ### 1. CMake Option: DEMO_FINAL_STRIP - New build mode for absolute minimum binary size - Implies DEMO_STRIP_ALL (stricter superset) - NOT included in DEMO_ALL_OPTIONS (manual opt-in only) - Message printed during configuration ### 2. Header: src/util/fatal_error.h - Systematic macro-based error checking - Zero cost when FINAL_STRIP enabled (compiles to ((void)0)) - Full error messages with file:line info when enabled - Five macros for different use cases: - FATAL_CHECK(cond, msg, ...): Conditional checks (most common) - FATAL_ERROR(msg, ...): Unconditional errors - FATAL_UNREACHABLE(): Unreachable code markers - FATAL_ASSERT(cond): Assertion-style invariants - FATAL_CODE_BEGIN/END: Complex validation blocks ### 3. CMake Target: make final - Convenience target for triggering final build - Reconfigures with FINAL_STRIP and rebuilds demo64k - Only available when NOT in FINAL_STRIP mode (prevents recursion) ### 4. Script: scripts/build_final.sh - Automated final build workflow - Creates build_final/ directory - Shows size comparison with STRIP_ALL build (if available) - Comprehensive warnings about stripped error checking ## Build Mode Hierarchy | Mode | Error Checks | Debug Features | Size Opt | |-------------|--------------|----------------|----------| | Debug | ✅ | ✅ | ❌ | | STRIP_ALL | ✅ | ❌ | ✅ | | FINAL_STRIP | ❌ | ❌ | ✅✅ | ## Design Decisions (All Agreed Upon) 1. **FILE:LINE Info**: ✅ Include (worth 200 bytes for debugging) 2. **ALL_OPTIONS**: ❌ Manual opt-in only (too dangerous for testing) 3. **FATAL_ASSERT**: ✅ Add macro (semantic clarity for invariants) 4. **Strip Hierarchy**: ✅ STRIP_ALL keeps checks, FINAL_STRIP removes all 5. **Naming**: ✅ FATAL_* prefix (clear intent, conventional) ## Size Impact Current: 10 abort() calls in production code - ring_buffer.cc: 7 checks (~350 bytes) - miniaudio_backend.cc: 3 checks (~240 bytes) Estimated savings with FINAL_STRIP: ~500-600 bytes ## Documentation Updated: - doc/HOWTO.md: Added FINAL_STRIP build instructions - doc/CONTRIBUTING.md: Added fatal error checking guidelines - src/util/fatal_error.h: Comprehensive usage documentation ## Next Steps (Not in This Commit) Phase 2: Convert ring_buffer.cc abort() calls to FATAL_CHECK() Phase 3: Convert miniaudio_backend.cc abort() calls to FATAL_CHECK() Phase 4: Systematic scan for remaining abort() calls Phase 5: Verify size reduction with actual measurements ## Usage # Convenience methods make final # From normal build directory ./scripts/build_final.sh # Creates build_final/ # Manual cmake -S . -B build_final -DDEMO_FINAL_STRIP=ON cmake --build build_final ⚠️ WARNING: FINAL_STRIP builds have NO error checking. Use ONLY for final release, never for development/testing. Co-Authored-By: Claude Sonnet 4.5 --- doc/CONTRIBUTING.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) (limited to 'doc/CONTRIBUTING.md') diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md index ae8c35a..d7ea448 100644 --- a/doc/CONTRIBUTING.md +++ b/doc/CONTRIBUTING.md @@ -340,6 +340,82 @@ For tests that only need synth or tracker (not both), you can still call `synth_ These are performance-critical APIs and should be called directly, not wrapped by AudioEngine. +### Fatal Error Checking + +The project uses fatal error checking macros that can be stripped for final builds. These are defined in `src/util/fatal_error.h`. + +**When to use fatal error checks:** + +Use fatal error checking for conditions that indicate programming errors or corrupt state that cannot be recovered from: +- Bounds checking (array/buffer overflows) +- Null pointer checks +- Invariant violations +- Invalid state transitions + +**DO NOT use for:** +- Expected runtime errors (file not found, network errors) +- Recoverable conditions +- Input validation (use return codes instead) + +**Macro Reference:** + +```cpp +#include "util/fatal_error.h" + +// Conditional check (most common - 90% of cases) +FATAL_CHECK(write_pos >= capacity, + "write_pos out of bounds: %d >= %d\n", + write_pos, capacity); + +// Unconditional error (should-never-happen) +FATAL_ERROR("Invalid state: %d\n", state); + +// Unreachable code marker (switch defaults) +switch (type) { + case TYPE_A: return handle_a(); + case TYPE_B: return handle_b(); + default: FATAL_UNREACHABLE(); +} + +// Assertion-style (documenting invariants) +FATAL_ASSERT(buffer != nullptr); +FATAL_ASSERT(count > 0 && count < MAX_COUNT); + +// Complex validation blocks +FATAL_CODE_BEGIN + static int callback_depth = 0; + ++callback_depth; + if (callback_depth > 1) { + FATAL_ERROR("Callback re-entered! depth=%d", callback_depth); + } +FATAL_CODE_END +``` + +**Build modes:** +- **Debug/STRIP_ALL**: All checks enabled, full error messages with file:line info +- **FINAL_STRIP**: All checks compiled out (zero runtime cost, ~500-600 bytes saved) + +**Testing requirement:** + +Before committing code with fatal error checks, verify both modes compile: + +```bash +# Normal build (checks enabled) +cmake -S . -B build +cmake --build build && cd build && ctest + +# FINAL_STRIP build (checks stripped) +cmake -S . -B build_final -DDEMO_FINAL_STRIP=ON +cmake --build build_final +``` + +Or use the convenience script: +```bash +./scripts/build_final.sh +``` + +**Important:** Never use `FATAL_CHECK` for conditions that can legitimately occur during normal operation. These are for programming errors only. + ### Script Maintenance After Hierarchy Changes After any major source hierarchy change (moving, renaming, or reorganizing files), you **must** review and update all scripts in the `scripts/` directory to ensure they remain functional. -- cgit v1.2.3