From e88a28c39f7b6a20c200d3fe8e1db6e364aee595 Mon Sep 17 00:00:00 2001 From: walkawayy <81546780+walkawayy@users.noreply.github.com> Date: Thu, 2 Jan 2025 00:04:22 -0500 Subject: [PATCH] tr2: add mac builds --- .../actions/prepare_macos_tooling/action.yml | 181 ++++++++++++++++++ .github/workflows/job_build_tr1_macos.yml | 177 ++--------------- .github/workflows/job_build_tr2_macos.yml | 132 +++++++++++++ .github/workflows/pr_builds.yml | 9 + .github/workflows/prerelease.yml | 12 ++ data/tr2/mac/Info.plist | 12 ++ data/tr2/mac/icon.icns | Bin 0 -> 60325 bytes docs/CONTRIBUTING.md | 22 +++ docs/tr1/SECRETS.md | 1 + docs/tr2/CHANGELOG.md | 1 + docs/tr2/README.md | 1 + src/tr1/meson.build | 2 +- src/tr2/game/output.c | 4 +- src/tr2/game/render/common.c | 63 +++++- src/tr2/meson.build | 15 ++ tools/{tr1 => shared}/mac/bundle_dylibs | 62 +++--- tools/{tr1 => shared}/mac/create_installer | 30 ++- .../{tr1 => shared}/mac/x86-64_cross_file.txt | 0 18 files changed, 500 insertions(+), 224 deletions(-) create mode 100644 .github/actions/prepare_macos_tooling/action.yml create mode 100644 .github/workflows/job_build_tr2_macos.yml create mode 100644 data/tr2/mac/Info.plist create mode 100644 data/tr2/mac/icon.icns rename tools/{tr1 => shared}/mac/bundle_dylibs (61%) rename tools/{tr1 => shared}/mac/create_installer (55%) rename tools/{tr1 => shared}/mac/x86-64_cross_file.txt (100%) diff --git a/.github/actions/prepare_macos_tooling/action.yml b/.github/actions/prepare_macos_tooling/action.yml new file mode 100644 index 000000000..ce199194a --- /dev/null +++ b/.github/actions/prepare_macos_tooling/action.yml @@ -0,0 +1,181 @@ +name: Cache TRX MacOS Dependencies + +inputs: + FFMPEG_INSTALL_TMP_ARM64: + required: false + default: /opt/local/install_arm64 + FFMPEG_INSTALL_TMP_X86_64: + required: false + default: /opt/local/install_x86_64 + CACHE_SRC_DIR: + required: false + default: /opt/local + CACHE_DIR: + required: true + +runs: + using: "composite" + steps: + - name: Select latest stable Xcode + if: steps.restore-cache.outputs.cache-hit != 'true' + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + - name: Install and update MacPorts + if: steps.restore-cache.outputs.cache-hit != 'true' + shell: bash + run: | + wget -O ${{ github.workspace }}/macports.pkg https://github.com/macports/macports-base/releases/download/v2.9.2/MacPorts-2.9.2-14-Sonoma.pkg + sudo installer -pkg ${{ github.workspace }}/macports.pkg -target / + + - name: Install build and deployment tools + if: steps.restore-cache.outputs.cache-hit != 'true' + shell: bash + run: | + # Install Python first to avoid multiple Python in the dep tree later on. + sudo port -N install python312 py312-pip + sudo port select --set python python312 + sudo port select --set python3 python312 + sudo port select --set pip pip312 + sudo port select --set pip3 pip312 + + # Install the rest. + sudo port -N install create-dmg meson ninja pkgconfig + sudo pip3 install pyjson5 + + - name: "Build dependencies: Compression libraries (universal)" + if: steps.restore-cache.outputs.cache-hit != 'true' + shell: bash + run: | + sudo port -N install zlib +universal + sudo port -N install bzip2 +universal + sudo port -N install xz +universal + + - name: "Build dependency: pcre2 (universal)" + if: steps.restore-cache.outputs.cache-hit != 'true' + shell: bash + run: sudo port -N install pcre2 +universal + + - name: "Build dependency: libsdl2 (universal)" + if: steps.restore-cache.outputs.cache-hit != 'true' + shell: bash + run: sudo port -N install libsdl2 +universal + + - name: "Build dependency: uthash (universal)" + if: steps.restore-cache.outputs.cache-hit != 'true' + shell: bash + run: sudo port -N install uthash +universal + + - name: "Build dependency: ffmpeg (universal)" + if: steps.restore-cache.outputs.cache-hit != 'true' + shell: bash + run: | + # Install to separate staging paths for all architectures in + # preparation for fusing universal libraries in a follow-up step. + cd "$RUNNER_TEMP" + git clone --depth 1 --branch "n4.4.1" https://github.com/FFmpeg/FFmpeg + cd FFmpeg + + # Common FFmpeg configure options + FFMPEG_CONFIG_OPTIONS=" \ + --enable-shared \ + --disable-static \ + $(cat $GITHUB_WORKSPACE/tools/ffmpeg_flags.txt)" + + # Configure for arm64. + ./configure \ + --arch=arm64 \ + --prefix=${{ inputs.FFMPEG_INSTALL_TMP_ARM64 }} \ + --cc='clang' \ + $FFMPEG_CONFIG_OPTIONS + + # Build and install. + make -j$(sysctl -n hw.ncpu) + sudo make install + + # Reset build directory. + make clean + + # Configure for x86-64. + ./configure \ + --arch=x86_64 \ + --enable-cross-compile \ + --prefix=${{ inputs.FFMPEG_INSTALL_TMP_X86_64 }} \ + --cc='clang -arch x86_64' \ + $FFMPEG_CONFIG_OPTIONS + + # Build and install. + make -j$(sysctl -n hw.ncpu) + sudo make install + + - name: "Build dependency: ffmpeg (fuse universal libraries)" + if: steps.restore-cache.outputs.cache-hit != 'true' + shell: bash + run: | + # Libs + FFMPEG_LIBS=( + "libavcodec" + "libavdevice" + "libavfilter" + "libavformat" + "libavutil" + "libpostproc" + "libswresample" + "libswscale" + ) + + # Recreate include tree in MacPorts install prefix. + sudo rsync -arvL ${{ inputs.FFMPEG_INSTALL_TMP_ARM64 }}/include/ ${{ inputs.CACHE_SRC_DIR }}/include/ + + # Recreate library symlinks in MacPorts install prefix. + sudo find ${{ inputs.FFMPEG_INSTALL_TMP_ARM64 }}/lib/ -type l -exec cp -P '{}' ${{ inputs.CACHE_SRC_DIR }}/lib/ ';' + + # Fuse platform-specific binaries into a universal binary. + for LIB in ${FFMPEG_LIBS[@]}; do + RESOLVED_LIB=$(ls -l ${{ inputs.FFMPEG_INSTALL_TMP_ARM64 }}/lib/${LIB}* \ + | grep -v '^l' \ + | awk -F'/' '{print $NF}') + + sudo lipo -create \ + ${{ inputs.FFMPEG_INSTALL_TMP_ARM64 }}/lib/$RESOLVED_LIB \ + ${{ inputs.FFMPEG_INSTALL_TMP_X86_64 }}/lib/$RESOLVED_LIB \ + -output ${{ inputs.CACHE_SRC_DIR }}/lib/$RESOLVED_LIB + + sudo ln -s -f \ + ${{ inputs.CACHE_SRC_DIR }}/lib/$RESOLVED_LIB \ + ${{ inputs.FFMPEG_INSTALL_TMP_ARM64 }}/lib/$RESOLVED_LIB + sudo ln -s -f \ + ${{ inputs.CACHE_SRC_DIR }}/lib/$RESOLVED_LIB \ + ${{ inputs.FFMPEG_INSTALL_TMP_X86_64 }}/lib/$RESOLVED_LIB + done + + # Update and install pkgconfig files. + for file in "${{ inputs.FFMPEG_INSTALL_TMP_ARM64 }}/lib/pkgconfig"/*.pc; do + sudo sed -i '' "s:${{ inputs.FFMPEG_INSTALL_TMP_ARM64 }}:${{ inputs.CACHE_SRC_DIR }}:g" "$file" + done + sudo mv ${{ inputs.FFMPEG_INSTALL_TMP_ARM64 }}/lib/pkgconfig/* ${{ inputs.CACHE_SRC_DIR }}/lib/pkgconfig/ + + - name: "Prepare dependencies for caching" + if: steps.restore-cache.outputs.cache-hit != 'true' + shell: bash + run: | + # Remove MacPorts leftover build and download files + sudo rm -rf /opt/local/var/macports/build/* + sudo rm -rf /opt/local/var/macports/distfiles/* + sudo rm -rf /opt/local/var/macports/packages/* + + # Delete broken symlinks + sudo find ${{ inputs.CACHE_SRC_DIR }} -type l ! -exec test -e {} \; -exec rm {} \; + + # Trying to cache the source directory directly leads to permission errors, + # so copy it to an intermediate temporary directory. Also, expands all the symlinks to hard copies. + sudo rsync -arvqL ${{ inputs.CACHE_SRC_DIR }}/ ${{ inputs.CACHE_DIR }} + + - name: "Save dependencies to cache" + if: steps.restore-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + key: ${{ runner.os }}-tooling-${{ hashFiles('.github/actions/prepare_macos_tooling/action.yml') }} + path: | + ${{ inputs.CACHE_DIR }} diff --git a/.github/workflows/job_build_tr1_macos.yml b/.github/workflows/job_build_tr1_macos.yml index 466943276..08af166de 100644 --- a/.github/workflows/job_build_tr1_macos.yml +++ b/.github/workflows/job_build_tr1_macos.yml @@ -59,174 +59,23 @@ jobs: echo -e "/opt/local/bin" >> $GITHUB_PATH echo -e "/opt/local/sbin" >> $GITHUB_PATH - - name: "Restore dependencies from cache" + - name: "Try restore dependencies from cache" id: restore-cache uses: actions/cache/restore@v4 with: - key: ${{ runner.os }}-tooling-${{ hashFiles('.github/workflows/job_build_tr1_macos.yml') }} + key: ${{ runner.os }}-tooling-${{ hashFiles('.github/actions/prepare_macos_tooling/action.yml') }} path: | - ${{ env.CACHE_TMP_DIR }} - ${{ env.FFMPEG_INSTALL_TMP_ARM64 }} - ${{ env.FFMPEG_INSTALL_TMP_X86_64 }} - ${{ env.FFMPEG_INSTALL_TMP_UNIVERSAL }} - + /tmp/opt_local/ + - name: "Build MacOS dependencies" + if: steps.restore-cache.outputs.cache-hit != 'true' + uses: ./.github/actions/prepare_macos_tooling + with: + CACHE_DIR: /tmp/opt_local/ - name: "Prepare cached dependencies for use" if: steps.restore-cache.outputs.cache-hit == 'true' + shell: bash run: | - sudo rsync -arvq ${{ env.CACHE_TMP_DIR }} ${{ env.CACHE_DST_DIR }} - - - name: Select latest stable Xcode - if: steps.restore-cache.outputs.cache-hit != 'true' - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: latest-stable - - - name: Install and update MacPorts - if: steps.restore-cache.outputs.cache-hit != 'true' - run: | - wget -O ${{ github.workspace }}/macports.pkg https://github.com/macports/macports-base/releases/download/v2.9.2/MacPorts-2.9.2-14-Sonoma.pkg - sudo installer -pkg ${{ github.workspace }}/macports.pkg -target / - - - name: Install build and deployment tools - if: steps.restore-cache.outputs.cache-hit != 'true' - run: | - # Install Python first to avoid multiple Python in the dep tree later on. - sudo port -N install python312 py312-pip - sudo port select --set python python312 - sudo port select --set python3 python312 - sudo port select --set pip pip312 - sudo port select --set pip3 pip312 - - # Install the rest. - sudo port -N install create-dmg meson ninja pkgconfig - sudo pip3 install pyjson5 - - - name: "Build dependencies: Compression libraries (universal)" - if: steps.restore-cache.outputs.cache-hit != 'true' - run: | - sudo port -N install zlib +universal - sudo port -N install bzip2 +universal - sudo port -N install xz +universal - - - name: "Build dependency: pcre2 (universal)" - if: steps.restore-cache.outputs.cache-hit != 'true' - run: sudo port -N install pcre2 +universal - - - name: "Build dependency: libsdl2 (universal)" - if: steps.restore-cache.outputs.cache-hit != 'true' - run: sudo port -N install libsdl2 +universal - - - name: "Build dependency: uthash (universal)" - if: steps.restore-cache.outputs.cache-hit != 'true' - run: sudo port -N install uthash +universal - - - name: "Build dependency: ffmpeg (universal)" - if: steps.restore-cache.outputs.cache-hit != 'true' - run: | - # Install to separate staging paths for all architectures in - # preparation for fusing universal libraries in a follow-up step. - cd "$RUNNER_TEMP" - git clone --depth 1 --branch "n4.4.1" https://github.com/FFmpeg/FFmpeg - cd FFmpeg - - # Common FFmpeg configure options - FFMPEG_CONFIG_OPTIONS=" \ - --enable-shared \ - --disable-static \ - $(cat $GITHUB_WORKSPACE/tools/ffmpeg_flags.txt)" - - # Configure for arm64. - ./configure \ - --arch=arm64 \ - --prefix=$FFMPEG_INSTALL_TMP_ARM64 \ - --cc='clang' \ - $FFMPEG_CONFIG_OPTIONS - - # Build and install. - make -j$(sysctl -n hw.ncpu) - sudo make install - - # Reset build directory. - make clean - - # Configure for x86-64. - ./configure \ - --arch=x86_64 \ - --enable-cross-compile \ - --prefix=$FFMPEG_INSTALL_TMP_X86_64 \ - --cc='clang -arch x86_64' \ - $FFMPEG_CONFIG_OPTIONS - - # Build and install. - make -j$(sysctl -n hw.ncpu) - sudo make install - - - name: "Build dependency: ffmpeg (fuse universal libraries)" - if: steps.restore-cache.outputs.cache-hit != 'true' - run: | - # Libs - FFMPEG_LIBS=( - "libavcodec" - "libavdevice" - "libavfilter" - "libavformat" - "libavutil" - "libpostproc" - "libswresample" - "libswscale" - ) - - # Recreate library symlinks in MacPorts install prefix. - sudo find $FFMPEG_INSTALL_TMP_ARM64/lib -type l -exec cp -P '{}' $FFMPEG_INSTALL_FINAL/LIB ';' - - # `lipo` cannot overwrite binaries in place, so we stage the - # fused binaries in a temporary directory. - mkdir -p $FFMPEG_INSTALL_TMP_UNIVERSAL - for LIB in ${FFMPEG_LIBS[@]}; do - RESOLVED_LIB=$(ls -l $FFMPEG_INSTALL_TMP_ARM64/lib/${LIB}* \ - | grep -v '^l' \ - | awk -F'/' '{print $NF}') - - lipo -create \ - $FFMPEG_INSTALL_TMP_ARM64/lib/$RESOLVED_LIB \ - $FFMPEG_INSTALL_TMP_X86_64/lib/$RESOLVED_LIB \ - -output $FFMPEG_INSTALL_TMP_UNIVERSAL/$RESOLVED_LIB - - # Replace the arch-specific libraries with links to the universal - # binary, so `bundle_dylibs` will always gather a universal build. - sudo ln -s -f $FFMPEG_INSTALL_TMP_UNIVERSAL/$RESOLVED_LIB $FFMPEG_INSTALL_TMP_ARM64/lib/$RESOLVED_LIB - sudo ln -s -f $FFMPEG_INSTALL_TMP_UNIVERSAL/$RESOLVED_LIB $FFMPEG_INSTALL_TMP_X86_64/lib/$RESOLVED_LIB - done - - # Copy the fused binaries to the MacPorts install prefix. - sudo cp $FFMPEG_INSTALL_TMP_UNIVERSAL/*.dylib $FFMPEG_INSTALL_FINAL/lib/ - - # Update and install pkgconfig files. - for file in "$FFMPEG_INSTALL_TMP_ARM64/lib/pkgconfig"/*.pc; do - sudo sed -i '' "s|^prefix=.*|prefix=$FFMPEG_INSTALL_FINAL|" "$file" - sudo sed -i '' "s|^libdir=.*|libdir=$FFMPEG_INSTALL_FINAL/lib|" "$file" - done - sudo mv $FFMPEG_INSTALL_TMP_ARM64/lib/pkgconfig/* $FFMPEG_INSTALL_FINAL/lib/pkgconfig/ - - - name: "Prepare dependencies for caching" - if: steps.restore-cache.outputs.cache-hit != 'true' - run: | - # Remove MacPorts leftover build and download files - sudo rm -rf /opt/local/var/macports/build/* - sudo rm -rf /opt/local/var/macports/distfiles/* - sudo rm -rf /opt/local/var/macports/packages/* - sudo rsync -arvq ${{ env.CACHE_DST_DIR }} ${{ env.CACHE_TMP_DIR }} - - - name: "Save dependencies to cache" - if: steps.restore-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - key: ${{ steps.restore-cache.outputs.cache-primary-key }} - path: | - ${{ env.CACHE_TMP_DIR }} - ${{ env.FFMPEG_INSTALL_TMP_ARM64 }} - ${{ env.FFMPEG_INSTALL_TMP_X86_64 }} - ${{ env.FFMPEG_INSTALL_TMP_UNIVERSAL }} + sudo rsync -arvq /tmp/opt_local/ /opt/local/ - name: Setup CA run: | @@ -242,7 +91,7 @@ jobs: - name: Build x86-64 run: | BUILD_DIR=build-x86-64 - BUILD_OPTIONS="src/tr1 --prefix=/tmp/TR1X.app --bindir=Contents/MacOS --cross-file tools/tr1/mac/x86-64_cross_file.txt --buildtype ${{ inputs.target }}" + BUILD_OPTIONS="src/tr1 --prefix=/tmp/TR1X.app --bindir=Contents/MacOS --cross-file tools/shared/mac/x86-64_cross_file.txt --buildtype ${{ inputs.target }}" meson setup $BUILD_DIR $BUILD_OPTIONS meson compile -C $BUILD_DIR @@ -255,7 +104,7 @@ jobs: mv $BUNDLE_EXEC_DIR/TR1X_universal $BUNDLE_EXEC_DIR/TR1X # Update dynamic library links in the fused executable. - ./tools/tr1/mac/bundle_dylibs --links-only + ./tools/shared/mac/bundle_dylibs -a TR1X --links-only - name: Sign app bundle run: | @@ -272,7 +121,7 @@ jobs: run: | KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db IDENTITY=$(security find-identity -v -p codesigning $KEYCHAIN_PATH | awk -F'"' '{print $2}') - tools/tr1/mac/create_installer + tools/shared/mac/create_installer -a TR1X -i data/tr1/mac/icon.icns xattr -cr TR1X-Installer.dmg /usr/bin/codesign --force --options runtime -s "${IDENTITY}" --keychain $KEYCHAIN_PATH -v TR1X-Installer.dmg xcrun notarytool submit --wait --apple-id "$MACOS_APPLEID" --password "$MACOS_APP_PWD" --team-id "$MACOS_TEAMID" TR1X-Installer.dmg diff --git a/.github/workflows/job_build_tr2_macos.yml b/.github/workflows/job_build_tr2_macos.yml new file mode 100644 index 000000000..2c7e51d21 --- /dev/null +++ b/.github/workflows/job_build_tr2_macos.yml @@ -0,0 +1,132 @@ +name: Build TR2X and the installer (macOS) + +on: + workflow_call: + inputs: + target: + type: string + description: "Target to build for" + required: true + let_mac_fail: + type: boolean + description: "Do not require Mac builds to pass" + required: false + default: false + +env: + C_INCLUDE_PATH: /opt/local/include/uthash/:/opt/local/include/ + +jobs: + build: + name: Build release assets (mac) + runs-on: macos-14 + continue-on-error: ${{ inputs.let_mac_fail == true || inputs.let_mac_fail == 'true' }} + steps: + - name: Set up signing certificate + env: + MACOS_KEYCHAIN_PWD: ${{ secrets.MACOS_KEYCHAIN_PWD }} + MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }} + run: | + CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + echo -n "$MACOS_CERTIFICATE" | base64 --decode -o $CERTIFICATE_PATH + security create-keychain -p "$MACOS_KEYCHAIN_PWD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$MACOS_KEYCHAIN_PWD" $KEYCHAIN_PATH + security import $CERTIFICATE_PATH -P "$MACOS_KEYCHAIN_PWD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH -T /usr/bin/codesign + security list-keychain -d user -s $KEYCHAIN_PATH + security set-key-partition-list -S "apple-tool:,apple:,codesign:" -s -k $MACOS_KEYCHAIN_PWD $KEYCHAIN_PATH + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Extend PATH for MacPorts + run: | + echo -e "/opt/local/bin" >> $GITHUB_PATH + echo -e "/opt/local/sbin" >> $GITHUB_PATH + + - name: "Try restore dependencies from cache" + id: restore-cache + uses: actions/cache/restore@v4 + with: + key: ${{ runner.os }}-tooling-${{ hashFiles('.github/actions/prepare_macos_tooling/action.yml') }} + path: | + /tmp/opt_local/ + - name: "Build MacOS dependencies" + if: steps.restore-cache.outputs.cache-hit != 'true' + uses: ./.github/actions/prepare_macos_tooling + with: + CACHE_DIR: /tmp/opt_local/ + - name: "Prepare cached dependencies for use" + if: steps.restore-cache.outputs.cache-hit == 'true' + shell: bash + run: | + sudo rsync -arvq /tmp/opt_local/ /opt/local/ + + - name: Setup CA + run: | + sudo port -N install apple-pki-bundle curl-ca-bundle + + - name: Build arm64 and create app bundle + run: | + BUILD_DIR=build-arm64 + BUILD_OPTIONS="src/tr2 --prefix=/tmp/TR2X.app --bindir=Contents/MacOS --buildtype ${{ inputs.target }}" + meson setup $BUILD_DIR $BUILD_OPTIONS + meson install -C $BUILD_DIR + + - name: Build x86-64 + run: | + BUILD_DIR=build-x86-64 + BUILD_OPTIONS="src/tr2 --prefix=/tmp/TR2X.app --bindir=Contents/MacOS --cross-file tools/shared/mac/x86-64_cross_file.txt --buildtype ${{ inputs.target }}" + meson setup $BUILD_DIR $BUILD_OPTIONS + meson compile -C $BUILD_DIR + + - name: Fuse universal executable + run: | + BUNDLE_EXEC_DIR=/tmp/TR2X.app/Contents/MacOS + + # Fuse executable and move it into the app bundle. + lipo -create build-x86-64/TR2X $BUNDLE_EXEC_DIR/TR2X -output $BUNDLE_EXEC_DIR/TR2X_universal + mv $BUNDLE_EXEC_DIR/TR2X_universal $BUNDLE_EXEC_DIR/TR2X + + # Update dynamic library links in the fused executable. + ./tools/shared/mac/bundle_dylibs -a TR2X --links-only + + - name: Sign app bundle + run: | + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + IDENTITY=$(security find-identity -v -p codesigning $KEYCHAIN_PATH | awk -F'"' '{print $2}') + xattr -cr /tmp/TR2X.app + /usr/bin/codesign --force --deep --options runtime -s "${IDENTITY}" --keychain $KEYCHAIN_PATH -v /tmp/TR2X.app + + - name: Create, sign and notarize disk image + env: + MACOS_APPLEID: ${{ secrets.MACOS_APPLEID }} + MACOS_APP_PWD: ${{ secrets.MACOS_APP_PWD }} + MACOS_TEAMID: ${{ secrets.MACOS_TEAMID }} + run: | + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + IDENTITY=$(security find-identity -v -p codesigning $KEYCHAIN_PATH | awk -F'"' '{print $2}') + tools/shared/mac/create_installer -a TR2X -i data/tr2/mac/icon.icns + xattr -cr TR2X-Installer.dmg + /usr/bin/codesign --force --options runtime -s "${IDENTITY}" --keychain $KEYCHAIN_PATH -v TR2X-Installer.dmg + xcrun notarytool submit --wait --apple-id "$MACOS_APPLEID" --password "$MACOS_APP_PWD" --team-id "$MACOS_TEAMID" TR2X-Installer.dmg + xcrun stapler staple -v TR2X-Installer.dmg + mv TR2X-Installer.dmg "TR2X-$(tools/get_version 1)-Installer.dmg" + + - id: vars + name: Prepare variables + run: echo "version=$(tools/get_version 1)" >> $GITHUB_OUTPUT + + - name: Upload signed+notarized installer image + uses: actions/upload-artifact@v4 + with: + name: TR2X-${{ steps.vars.outputs.version }}-mac + path: | + *.dmg + compression-level: 0 # .dmg is already compressed. diff --git a/.github/workflows/pr_builds.yml b/.github/workflows/pr_builds.yml index 7eb5fe6a6..ba549eff8 100644 --- a/.github/workflows/pr_builds.yml +++ b/.github/workflows/pr_builds.yml @@ -33,3 +33,12 @@ jobs: with: target: 'debug' secrets: inherit + + package_tr2_mac: + name: Build TR2 + if: vars.MACOS_ENABLE == 'true' + uses: ./.github/workflows/job_build_tr2_macos.yml + with: + target: 'debug' + let_mac_fail: true + secrets: inherit diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 8d283f489..b6dbe4548 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -36,6 +36,17 @@ jobs: target: 'debug' secrets: inherit + package_tr2_mac: + name: Build TR2 + if: | + vars.PRERELEASE_ENABLE == 'true' && + vars.MACOS_ENABLE == 'true' + uses: ./.github/workflows/job_build_tr2_macos.yml + with: + target: 'debug' + let_mac_fail: true + secrets: inherit + publish_prerelease: if: vars.PRERELEASE_ENABLE == 'true' name: Create a prerelease @@ -43,6 +54,7 @@ jobs: - package_tr1_multiplatform - package_tr1_mac - package_tr2_multiplatform + - package_tr2_mac with: draft: false prerelease: true diff --git a/data/tr2/mac/Info.plist b/data/tr2/mac/Info.plist new file mode 100644 index 000000000..c523722d5 --- /dev/null +++ b/data/tr2/mac/Info.plist @@ -0,0 +1,12 @@ + + + + + CFBundleIdentifier + com.lostartifacts.tr1x + CFBundleName + TR2X + CFBundleIconFile + icon + + diff --git a/data/tr2/mac/icon.icns b/data/tr2/mac/icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..01c5a1b7e4d0be99e77693e29b42d34e3fabee17 GIT binary patch literal 60325 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�I4+0tSvv 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 base64 TR1X.pem|tr -d '\n' + > base64 -i BUILD_CERTIFICATE.p12 | pbcopy (macos) The result is to be put as the value of the `MACOS_CERTIFICATE` secret. diff --git a/docs/tr2/CHANGELOG.md b/docs/tr2/CHANGELOG.md index 77626fabf..4738b81ed 100644 --- a/docs/tr2/CHANGELOG.md +++ b/docs/tr2/CHANGELOG.md @@ -1,5 +1,6 @@ ## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-0.8...develop) - ××××-××-×× - added Linux builds and toolchain (#1598) +- added macOS builds (for both Apple Silicon and Intel) (#2226) - added pause dialog (#1638) - added a photo mode feature (#2277) - added fade-out effect to the demos diff --git a/docs/tr2/README.md b/docs/tr2/README.md index 7a7ad3b25..e88da1f44 100644 --- a/docs/tr2/README.md +++ b/docs/tr2/README.md @@ -131,6 +131,7 @@ game with new enhancements and features. #### Miscellaneous - added Linux builds +- added macOS builds - added .jpeg/.png screenshots - added ability to skip FMVs with both the Action key - added ability to skip end credits with the Action and Escape keys diff --git a/src/tr1/meson.build b/src/tr1/meson.build index 430261feb..30a1a8963 100644 --- a/src/tr1/meson.build +++ b/src/tr1/meson.build @@ -301,5 +301,5 @@ if host_machine.system() == 'darwin' install_subdir('../../data/tr1/ship/shaders', install_dir : 'Contents/Resources') install_data('../../data/tr1/mac/icon.icns', install_dir : 'Contents/Resources') install_data('../../data/tr1/mac/Info.plist', install_dir : 'Contents') - meson.add_install_script('../../tools/tr1/mac/bundle_dylibs') + meson.add_install_script('../../tools/shared/mac/bundle_dylibs', '-a', 'TR1X') endif diff --git a/src/tr2/game/output.c b/src/tr2/game/output.c index af4e18d6a..235240537 100644 --- a/src/tr2/game/output.c +++ b/src/tr2/game/output.c @@ -824,8 +824,8 @@ int32_t Output_GetObjectBounds(const BOUNDS_16 *const bounds) return 0; } - constexpr int32_t vtx_count = 8; - const XYZ_32 vtx[vtx_count] = { + const int32_t vtx_count = 8; + const XYZ_32 vtx[] = { { .x = bounds->min.x, .y = bounds->min.y, .z = bounds->min.z }, { .x = bounds->max.x, .y = bounds->min.y, .z = bounds->min.z }, { .x = bounds->max.x, .y = bounds->max.y, .z = bounds->min.z }, diff --git a/src/tr2/game/render/common.c b/src/tr2/game/render/common.c index 6bf23aad7..c52d5270b 100644 --- a/src/tr2/game/render/common.c +++ b/src/tr2/game/render/common.c @@ -15,6 +15,9 @@ #include #include +#include +#include + static RENDERER m_Renderer_SW = {}; static RENDERER m_Renderer_HW = {}; static RENDERER *m_PreviousRenderer = NULL; @@ -32,6 +35,7 @@ static struct { static RENDERER *M_GetRenderer(void); static void M_ReuploadBackground(void); static void M_ResetPolyList(void); +static void M_SetGLBackend(GFX_GL_BACKEND backend); static RENDERER *M_GetRenderer(void) { @@ -69,15 +73,60 @@ static void M_ResetPolyList(void) } } +static void M_SetGLBackend(const GFX_GL_BACKEND backend) +{ + switch (backend) { + case GFX_GL_21: + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); + break; + + case GFX_GL_33C: + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute( + SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + break; + + case GFX_GL_INVALID_BACKEND: + ASSERT_FAIL(); + break; + } +} + void Render_Init(void) { - LOG_DEBUG(""); - GFX_Context_Attach(g_SDLWindow, GFX_GL_33C); - GFX_Context_SetRenderingMode(GFX_RM_FRAMEBUFFER); - m_FadeRenderer = GFX_FadeRenderer_Create(); - m_BackgroundRenderer = GFX_2D_Renderer_Create(); - Renderer_SW_Prepare(&m_Renderer_SW); - Renderer_HW_Prepare(&m_Renderer_HW); + // TODO Move to libtrx later and combine with S_Shell_CreateWindow. + const GFX_GL_BACKEND backends_to_try[] = { + // clang-format off + GFX_GL_33C, + GFX_GL_21, + GFX_GL_INVALID_BACKEND, // guard + // clang-format on + }; + + for (int32_t i = 0; backends_to_try[i] != GFX_GL_INVALID_BACKEND; i++) { + const GFX_GL_BACKEND backend = backends_to_try[i]; + + M_SetGLBackend(backend); + + int32_t major; + int32_t minor; + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); + LOG_DEBUG("Trying GL backend %d.%d", major, minor); + if (GFX_Context_Attach(g_SDLWindow, backend)) { + GFX_Context_SetRenderingMode(GFX_RM_FRAMEBUFFER); + m_FadeRenderer = GFX_FadeRenderer_Create(); + m_BackgroundRenderer = GFX_2D_Renderer_Create(); + Renderer_SW_Prepare(&m_Renderer_SW); + Renderer_HW_Prepare(&m_Renderer_HW); + return; + } + } + + Shell_ExitSystem("System Error: cannot attach opengl context"); } void Render_Shutdown(void) diff --git a/src/tr2/meson.build b/src/tr2/meson.build index af7ae420b..2d5ab8086 100644 --- a/src/tr2/meson.build +++ b/src/tr2/meson.build @@ -36,6 +36,11 @@ build_opts = [ add_project_arguments(build_opts, language: 'c') +# Always dynamically link on macOS +if host_machine.system() == 'darwin' + staticdeps = false +endif + null_dep = dependency('', required: false) dep_trx = trx.get_variable('dep_trx') dep_sdl2 = dependency('SDL2', static: staticdeps) @@ -301,4 +306,14 @@ executable( link_args: link_args, dependencies: dependencies, win_subsystem: 'windows', + install: true, ) + +if host_machine.system() == 'darwin' + install_subdir('../../data/tr2/ship/cfg', install_dir : 'Contents/Resources') + install_subdir('../../data/tr2/ship/data', install_dir : 'Contents/Resources') + install_subdir('../../data/tr2/ship/shaders', install_dir : 'Contents/Resources') + install_data('../../data/tr2/mac/icon.icns', install_dir : 'Contents/Resources') + install_data('../../data/tr2/mac/Info.plist', install_dir : 'Contents') + meson.add_install_script('../../tools/shared/mac/bundle_dylibs', '-a', 'TR2X') +endif diff --git a/tools/tr1/mac/bundle_dylibs b/tools/shared/mac/bundle_dylibs similarity index 61% rename from tools/tr1/mac/bundle_dylibs rename to tools/shared/mac/bundle_dylibs index 6d86e6a3c..69b2f2616 100755 --- a/tools/tr1/mac/bundle_dylibs +++ b/tools/shared/mac/bundle_dylibs @@ -2,28 +2,19 @@ import argparse import re import shutil +from collections.abc import Iterable from pathlib import Path from subprocess import check_output, run -# Configuration -APP_NAME = "TR1X" -APP_BUNDLE_PATH = Path(f"/tmp/{APP_NAME}.app") -APP_BINARY_PATH = APP_BUNDLE_PATH / f"Contents/MacOS/{APP_NAME}" -FRAMEWORKS_PATH = APP_BUNDLE_PATH / "Contents/Frameworks" IGNORE_LIB_PREFIXES = ("/usr/lib/", "/System/", "@executable_path") - - -# Other global state -LIBRARY_PATHS: set[Path] = set() +RPATH_PATTERN = re.compile(r"@rpath/(.*)") def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( - description=( - f"Copies shared libraries into the macOS app bundle " - "for {APP_NAME}." - ) + description="Copies shared libraries into the macOS app bundle." ) + parser.add_argument("-a", "--app-name") parser.add_argument( "--copy-only", action="store_true", @@ -41,32 +32,33 @@ def should_ignore_lib(lib_path: str) -> bool: return any(lib_path.startswith(prefix) for prefix in IGNORE_LIB_PREFIXES) -def gather_libs(binary_path: Path) -> None: +def gather_libs( + binary_path: Path, visited_paths: set[Path] | None = None +) -> Iterable[Path]: + if visited_paths is None: + visited_paths = set() + visited_paths.add(binary_path) + parent_path = binary_path.parent output = check_output(["otool", "-L", str(binary_path)], text=True) libs = [line.split()[0] for line in output.split("\n")[1:] if line] - rpath_pattern = re.compile(r"@rpath/(.*)") - for lib in libs: - match = rpath_pattern.match(lib) + match = RPATH_PATTERN.match(lib) lib_path = parent_path / (match.group(1) if match else lib) - if ( - should_ignore_lib(str(lib_path)) - or lib_path in LIBRARY_PATHS - or lib_path == binary_path - ): + if should_ignore_lib(str(lib_path)) or lib_path == binary_path: continue - LIBRARY_PATHS.add(lib_path) - gather_libs(lib_path) + yield lib_path + if lib_path not in visited_paths: + yield from gather_libs(lib_path, visited_paths=visited_paths) -def copy_libs() -> None: - FRAMEWORKS_PATH.mkdir(parents=True, exist_ok=True) - for lib_path in LIBRARY_PATHS: - target_path = FRAMEWORKS_PATH / lib_path.name +def copy_libs(frameworks_path: Path, library_paths: set[Path]) -> None: + frameworks_path.mkdir(parents=True, exist_ok=True) + for lib_path in library_paths: + target_path = frameworks_path / lib_path.name if not target_path.exists(): print(f"Copying {lib_path} to {target_path}") shutil.copy2(lib_path, target_path) @@ -93,17 +85,21 @@ def update_links(binary_path: Path) -> None: def main() -> None: args = parse_args() + app_bundle_path = Path(f"/tmp/{args.app_name}.app") + app_binary_path = app_bundle_path / f"Contents/MacOS/{args.app_name}" + frameworks_path = app_bundle_path / "Contents/Frameworks" + if args.copy_only or not args.links_only: - gather_libs(APP_BINARY_PATH) - copy_libs() + library_paths = set(gather_libs(app_binary_path)) + copy_libs(frameworks_path, library_paths) if args.links_only or not args.copy_only: - for lib_path in FRAMEWORKS_PATH.glob("*"): + for lib_path in frameworks_path.glob("*"): update_links(lib_path) - update_links(APP_BINARY_PATH) + update_links(app_binary_path) - print(f"Libraries for {APP_NAME} copied and updated.") + print(f"Libraries for {args.app_name} copied and updated.") if __name__ == "__main__": diff --git a/tools/tr1/mac/create_installer b/tools/shared/mac/create_installer similarity index 55% rename from tools/tr1/mac/create_installer rename to tools/shared/mac/create_installer index 921a5b054..24345c647 100755 --- a/tools/tr1/mac/create_installer +++ b/tools/shared/mac/create_installer @@ -4,27 +4,21 @@ import os import subprocess from pathlib import Path -# Configuration -APP_NAME = "TR1X" -APP_BUNDLE_PATH = Path(f"/tmp/{APP_NAME}.app") -DMG_NAME = Path(f"{APP_NAME}-Installer.dmg") - - def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description=f"Create a DMG installer for {APP_NAME}." - ) + parser = argparse.ArgumentParser(description=f"Create a DMG installer.") + parser.add_argument("-a", "--app-name") + parser.add_argument("-i", "--icon-path", type=Path) return parser.parse_args() -def create_dmg(app_name: str, dmg_name: str, app_bundle_path: Path) -> None: +def create_dmg(app_name: str, dmg_name: str, icon_path: Path, app_bundle_path: Path) -> None: subprocess.run( ( "create-dmg", "--volname", - f"{APP_NAME} Installer", + f"{app_name} Installer", "--volicon", - "data/tr1/mac/icon.icns", + str(icon_path), "--window-pos", "200", "120", @@ -34,11 +28,11 @@ def create_dmg(app_name: str, dmg_name: str, app_bundle_path: Path) -> None: "--icon-size", "100", "--icon", - f"{APP_NAME}.app", + f"{app_name}.app", "200", "190", "--hide-extension", - f"{APP_NAME}.app", + f"{app_name}.app", "--app-drop-link", "600", "185", @@ -51,10 +45,12 @@ def create_dmg(app_name: str, dmg_name: str, app_bundle_path: Path) -> None: def main() -> None: args = parse_args() - if DMG_NAME.is_file(): - DMG_NAME.unlink() + dmg_name = Path(f"{args.app_name}-Installer.dmg") + if dmg_name.is_file(): + dmg_name.unlink() - create_dmg(APP_NAME, DMG_NAME, APP_BUNDLE_PATH) + app_bundle_path = Path(f"/tmp/{args.app_name}.app") + create_dmg(args.app_name, dmg_name, args.icon_path, app_bundle_path) if __name__ == "__main__": diff --git a/tools/tr1/mac/x86-64_cross_file.txt b/tools/shared/mac/x86-64_cross_file.txt similarity index 100% rename from tools/tr1/mac/x86-64_cross_file.txt rename to tools/shared/mac/x86-64_cross_file.txt