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()