diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 78731f1..00e3a58 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,7 +41,7 @@ jobs: - uses: actions/checkout@v4 - name: Install SFML 2.x via Homebrew - run: brew install sfml@2 cmake + run: brew install sfml@2 openal-soft cmake - name: Configure working-directory: "SuperMario 2.0" diff --git a/SuperMario 2.0/CMakeLists.txt b/SuperMario 2.0/CMakeLists.txt index 9ccfd33..6790889 100644 --- a/SuperMario 2.0/CMakeLists.txt +++ b/SuperMario 2.0/CMakeLists.txt @@ -63,6 +63,27 @@ add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD "$/Levels" ) +# macOS only: SFML 2.6's audio links Apple's deprecated OpenAL.framework, which +# races in CoreAudio and crashes intermittently on launch. Ship a copy of +# libsfml-audio relinked against openal-soft beside the binary so the game uses +# a modern OpenAL instead. Requires `brew install openal-soft`. +if(APPLE) + find_library(OPENAL_SOFT_LIB NAMES openal + PATHS /opt/homebrew/opt/openal-soft/lib /usr/local/opt/openal-soft/lib + NO_DEFAULT_PATH) + if(OPENAL_SOFT_LIB) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND bash "${CMAKE_SOURCE_DIR}/cmake/use_openal_soft.sh" + "$" + "$" + "${OPENAL_SOFT_LIB}" + VERBATIM + COMMENT "Relinking libsfml-audio against openal-soft (macOS OpenAL.framework workaround)") + else() + message(WARNING "openal-soft not found - run 'brew install openal-soft' to avoid the intermittent macOS startup crash (see docs/KNOWN_ISSUES.md).") + endif() +endif() + # Headless unit tests for the pure-logic parts (no SFML / GL context needed). enable_testing() add_executable(SuperMario2_tests tests/tests.cpp LevelManager.cpp) diff --git a/SuperMario 2.0/cmake/use_openal_soft.sh b/SuperMario 2.0/cmake/use_openal_soft.sh new file mode 100755 index 0000000..66a28c9 --- /dev/null +++ b/SuperMario 2.0/cmake/use_openal_soft.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Ship a copy of libsfml-audio relinked against openal-soft next to the +# executable, so the game never touches Apple's deprecated, crash-prone +# OpenAL.framework (see docs/KNOWN_ISSUES.md). Invoked as a CMake POST_BUILD +# step on macOS. Idempotent: safe to re-run on every build. +# +# Args: +# $1 = path to the built executable +# $2 = path to the real libsfml-audio dylib (from SFML) +# $3 = path to openal-soft's libopenal dylib +set -euo pipefail + +EXE="$1" +SFML_AUDIO="$2" +OPENAL="$3" +EXE_DIR="$(cd "$(dirname "$EXE")" && pwd)" +LOCAL="$EXE_DIR/libsfml-audio.2.6.dylib" + +# Local, writable copy of libsfml-audio pointed at openal-soft. +cp -f "$SFML_AUDIO" "$LOCAL" +chmod u+w "$LOCAL" +framework=$(otool -L "$LOCAL" | awk '/OpenAL\.framework/{print $1; exit}') +if [ -n "${framework:-}" ]; then + install_name_tool -change "$framework" "$OPENAL" "$LOCAL" +fi +install_name_tool -id @rpath/libsfml-audio.2.6.dylib "$LOCAL" +codesign --force --sign - "$LOCAL" + +# Point the freshly-linked executable at the local copy beside it. +current=$(otool -L "$EXE" | awk '/libsfml-audio/{print $1; exit}') +if [ "$current" != "@loader_path/libsfml-audio.2.6.dylib" ]; then + install_name_tool -change "$current" @loader_path/libsfml-audio.2.6.dylib "$EXE" +fi +codesign --force --sign - "$EXE" + +echo "Relinked libsfml-audio against openal-soft: $OPENAL" diff --git a/docs/BUILD.md b/docs/BUILD.md index 8329415..93843a0 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -11,11 +11,13 @@ The game is written in C++17 against **SFML 2.6** and built with CMake. ### macOS ```sh -brew install sfml@2 cmake +brew install sfml@2 openal-soft cmake ``` `sfml@2` is keg-only; the `CMakeLists.txt` already adds the Homebrew prefix to -the search path, so no extra flags are needed. +the search path, so no extra flags are needed. `openal-soft` is used to work +around a crash in Apple's deprecated OpenAL framework — see +[KNOWN_ISSUES.md](KNOWN_ISSUES.md). ### Ubuntu / Debian diff --git a/docs/KNOWN_ISSUES.md b/docs/KNOWN_ISSUES.md index 15ec603..00abc66 100644 --- a/docs/KNOWN_ISSUES.md +++ b/docs/KNOWN_ISSUES.md @@ -1,34 +1,31 @@ # Known issues -## Intermittent crash on startup (macOS, Apple Silicon) +## macOS startup crash — fixed via openal-soft -**Symptom:** the game occasionally crashes immediately on launch with -`EXC_BAD_ACCESS` / `SIGSEGV` before the window is usable. Re-launching works. +**History:** the game used to crash intermittently on launch on macOS (Apple +Silicon) with `EXC_BAD_ACCESS` before the window was usable. -**Cause:** this is *not* a bug in the game. Homebrew's `sfml@2` audio library -links Apple's **deprecated `OpenAL.framework`**: +**Cause:** Homebrew's `sfml@2` audio library links Apple's **deprecated +`OpenAL.framework`**, which races inside CoreAudio's HAL +(`AudioDeviceGetPropertyInfo`) when OpenAL first opens the audio device. -``` -$ otool -L libsfml-audio.2.6.dylib | grep -i openal - /System/Library/Frameworks/OpenAL.framework/.../OpenAL -``` +**Fix (automatic):** the macOS build now ships a copy of `libsfml-audio` +relinked against **openal-soft** next to the binary, so the game never touches +the Apple framework. Just install openal-soft once: -Apple deprecated that framework in macOS 10.15 and it races inside CoreAudio's -HAL (`HAL_HardwarePlugIn_ObjectHasProperty` / `AudioDeviceGetPropertyInfo`) -when OpenAL first opens the audio device. The crash happens on the first sound -played, regardless of whether it is `sf::Music` or `sf::Sound`. +```sh +brew install openal-soft +``` -**Workarounds:** +CMake detects it and applies the relink as a post-build step +(`cmake/use_openal_soft.sh`). You can confirm the framework is gone: -- Use the retry launcher, which re-runs past a failed audio init: - ```sh - ./run.sh # from the "SuperMario 2.0" directory - ``` -- Or simply launch again — a successful start runs normally for the whole - session. +```sh +otool -L build/SuperMario2 build/libsfml-audio.2.6.dylib | grep -i OpenAL.framework # no output +``` -**Not affected:** Linux/CI builds, which use `openal-soft` (`libopenal-dev`) -instead of the Apple framework, do not exhibit this race. +If openal-soft is **not** installed, CMake prints a warning and the game falls +back to the system framework (and the old intermittent crash). The bundled +`run.sh` retry launcher remains as a fallback for that case. -A permanent fix requires SFML to link a modern `openal-soft` on macOS, which is -a packaging concern outside this repository's source. +**Not affected:** Linux/CI uses `openal-soft` (`libopenal-dev`) already.