From 2aa071fa9a3a165095b195cac298b72d5b4885a2 Mon Sep 17 00:00:00 2001 From: Dagen Brock Date: Sun, 28 Jun 2026 17:22:32 -0500 Subject: [PATCH] CI: add universal macOS build + fix minimum-OS deployment target Downloaded macOS apps failed with "You can't use this version of the application with this version of macOS" on older systems. The SDL bundle set no deployment target, so the toolchain stamped the binary's minimum-OS (LC_BUILD_VERSION minos) with the build machine's SDK -- macOS 15 on the macos-latest runner. Pin CMAKE_OSX_DEPLOYMENT_TARGET to 11.0 so the app runs on macOS 11+. Also add a second macOS CI job producing a universal (arm64 + x86_64) app alongside the arm64-only Darwin build. It skips Homebrew SDL3 (arm64-only, unlinkable into a universal binary) so CMake's FetchContent path builds a universal SDL3 from source, and passes CMAKE_OSX_ARCHITECTURES. The artifact is renamed GSplus--macOS-universal.dmg to avoid colliding with the arm64 dmg on the Release. Universal exposed three CMake fixes: - skip the legacy Swift/Cocoa app on multi-arch builds (Swift can't do universal; that target is excluded from the default build anyway) - enable_language(OBJC) so sdl_mac_menu.m gets a proper compile rule - pass the SDL3 dylib dir to fixup_bundle so it resolves the FetchContent build's @rpath/libSDL3.0.dylib Verified locally: universal dmg contains an x86_64+arm64 app and SDL3, minos 11.0, valid signature; the default single-arch path is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/build.yml | 38 ++++++++++++++++++++++++++++++++++++- gsplus/src/CMakeLists.txt | 32 ++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 26409d6..8ce6fce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,6 +37,42 @@ jobs: name: gsplus-macos path: build/*.dmg + macos-universal: + # Second macOS build producing a UNIVERSAL (arm64 + x86_64) app, alongside + # the arm64-only `macos` job above. Key difference: we do NOT install + # Homebrew's SDL3 (it ships a single-arch arm64 dylib you can't link into a + # universal binary). With no system SDL3, CMake's FetchContent path builds + # SDL3 from source, and CMAKE_OSX_ARCHITECTURES makes that build -- and ours + # -- universal. fixup_bundle then embeds the universal SDL3 into the .app. + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: Install tools + # NB: intentionally no `sdl3` here -- see the comment above. + run: brew install cmake ninja + - name: Configure (universal, SDL3 built from source) + run: | + ARGS="" + case "$GITHUB_REF" in + refs/tags/v*) ARGS="-DGSPLUS_VERSION=${GITHUB_REF_NAME#v}" ;; + esac + cmake -G Ninja -B build -S gsplus/src \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" $ARGS + - name: Build + run: cmake --build build --target gsplus-sdl + - name: Package (.dmg) and label universal + run: | + cd build && cpack + # CPack names it GSplus--Darwin.dmg; rename so it doesn't collide + # with the arm64-only dmg when both attach to the same Release. + for f in GSplus-*-Darwin.dmg; do + mv "$f" "${f/-Darwin.dmg/-macOS-universal.dmg}" + done + - uses: actions/upload-artifact@v4 + with: + name: gsplus-macos-universal + path: build/*.dmg + linux: runs-on: ubuntu-latest steps: @@ -105,7 +141,7 @@ jobs: release: if: startsWith(github.ref, 'refs/tags/') - needs: [ macos, linux, windows ] + needs: [ macos, macos-universal, linux, windows ] runs-on: ubuntu-latest permissions: contents: write # required for the token to create a Release diff --git a/gsplus/src/CMakeLists.txt b/gsplus/src/CMakeLists.txt index ab8558c..ebb0cea 100644 --- a/gsplus/src/CMakeLists.txt +++ b/gsplus/src/CMakeLists.txt @@ -149,7 +149,18 @@ message(STATUS "GSplus: configured core library (build type: ${CMAKE_BUILD_TYPE} # Swift runtime linking / rpaths automatically, which is the fragile part the # Makefile does by hand. # ---------------------------------------------------------------------------- -if(APPLE) +# CMake's Swift support can't build a universal (multi-arch) binary -- it errors +# with "multiple values for CMAKE_OSX_ARCHITECTURES not supported with Swift". +# The native Cocoa app is legacy/secondary and excluded from the default build, +# so when a universal build is requested (the CI macos-universal job passes +# arm64;x86_64) we simply skip it; the shipped SDL app (gsplus-sdl, plain C) has +# no such restriction and still builds universal. +list(LENGTH CMAKE_OSX_ARCHITECTURES _gsplus_narch) +if(APPLE AND _gsplus_narch GREATER 1) + message(STATUS "GSplus: universal build -- skipping the Swift Cocoa app (Swift can't do multi-arch)") +endif() + +if(APPLE AND _gsplus_narch LESS 2) # Turn on CMake's Swift language support. This makes CMake able to compile # .swift files and, when it links a target that contains Swift, pull in the # Swift runtime and set rpaths for us. @@ -258,6 +269,11 @@ target_link_libraries(gsplus-sdl PRIVATE gsplus_core_sdl SDL3::SDL3) if(APPLE) # macOS-only: retune SDL's default menu bar so ⌘ combos pass through to the # emulated IIgs and GSplus quits on ⌥⌘Q. The Objective-C source needs Cocoa. + # Explicitly enable the OBJC language so CMake has a proper compile rule for + # the .m file. A single-arch build tolerates compiling it via the C rule, but + # a universal build (CMAKE_OSX_ARCHITECTURES=arm64;x86_64) errors with + # "CMAKE_OBJC_COMPILE_OBJECT not set" unless OBJC is enabled. + enable_language(OBJC) target_sources(gsplus-sdl PRIVATE sdl_mac_menu.m) target_link_libraries(gsplus-sdl PRIVATE "-framework Cocoa") endif() @@ -346,12 +362,18 @@ if(APPLE) # bundle. We then re-sign ad-hoc ("-"): editing a binary invalidates its # code signature, and arm64 macOS refuses to launch a broken-signature app. install(TARGETS gsplus-sdl BUNDLE DESTINATION .) - install(CODE [[ + # Tell fixup_bundle which directory to search for @rpath-named dylibs. A + # Homebrew SDL3 records an absolute/resolvable install name, but a + # FetchContent-built SDL3 (the universal job, and any host without system + # SDL3) names itself @rpath/libSDL3.0.dylib -- fixup_bundle can't find that + # unless we hand it the directory containing the built dylib. The genex + # resolves to Homebrew's libdir or the FetchContent build dir as appropriate. + install(CODE " include(BundleUtilities) - fixup_bundle("${CMAKE_INSTALL_PREFIX}/GSplus.app" "" "") + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/GSplus.app\" \"\" \"$\") execute_process(COMMAND codesign --force --deep --sign - - "${CMAKE_INSTALL_PREFIX}/GSplus.app") - ]]) + \"\${CMAKE_INSTALL_PREFIX}/GSplus.app\") + ") set(CPACK_GENERATOR "DragNDrop") # -> a .dmg set(CPACK_DMG_VOLUME_NAME "GSplus") else()