From f8058f21385a31230d3a2d27fbb65007da3bd2b7 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sun, 10 Nov 2024 11:06:56 -0500 Subject: [PATCH] Support Apple Silicon via Rosetta2 on MacOS Sequoia and Above (#3754) --- .github/workflows/build-matrix.yaml | 14 ++++++------- .github/workflows/macos-build-arm.yaml | 9 +++------ .github/workflows/release-pipeline.yaml | 25 ++++++++++++++++++++++- CMakeLists.txt | 10 ++++++++- CMakePresets.json | 20 ++++++++++++++++++ Taskfile.yml | 8 ++++++++ common/util/os.cpp | 27 +++++++++++++++++++++++++ common/util/os.h | 3 +++ game/CMakeLists.txt | 5 ++++- game/main.cpp | 16 +++++++++++++++ 10 files changed, 120 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build-matrix.yaml b/.github/workflows/build-matrix.yaml index 59601c577f..a3d83d7d1e 100644 --- a/.github/workflows/build-matrix.yaml +++ b/.github/workflows/build-matrix.yaml @@ -52,11 +52,9 @@ jobs: cmakePreset: "Release-macos-clang" cachePrefix: "" - # Q4 2023 there will hopefully be native arm64 runners - # https://github.com/github/roadmap/issues/528 - # build_macos_arm: - # name: "🍎 MacOS" - # uses: ./.github/workflows/macos-build-arm.yaml - # with: - # cmakePreset: "Release-macos-clang" - # cachePrefix: "" + build_macos_arm: + name: "🍎 MacOS" + uses: ./.github/workflows/macos-build-arm.yaml + with: + cmakePreset: "Release-macos-rosetta-clang" + cachePrefix: "" diff --git a/.github/workflows/macos-build-arm.yaml b/.github/workflows/macos-build-arm.yaml index 032dd27fb2..7f10156a84 100644 --- a/.github/workflows/macos-build-arm.yaml +++ b/.github/workflows/macos-build-arm.yaml @@ -13,18 +13,15 @@ on: jobs: build: name: ARM - runs-on: macos-12 + runs-on: macos-15 timeout-minutes: 120 steps: - name: Checkout Repository uses: actions/checkout@v4 - - name: Set up ARM64 environment - run: sudo softwareupdate --install-rosetta --agree-to-license - - name: Install Package Dependencies - run: arch -arm64 brew install cmake ninja + run: brew install cmake ninja nasm - name: Setup sccache uses: hendrikmuhs/ccache-action@v1.2.14 @@ -52,7 +49,7 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: opengoal-macos-${{ inputs.cachePrefix }} + name: opengoal-macos-arm-${{ inputs.cachePrefix }} if-no-files-found: error path: | ./build/goalc/goalc diff --git a/.github/workflows/release-pipeline.yaml b/.github/workflows/release-pipeline.yaml index d1aa0244fa..cd29a9aef7 100644 --- a/.github/workflows/release-pipeline.yaml +++ b/.github/workflows/release-pipeline.yaml @@ -79,6 +79,17 @@ jobs: uploadArtifacts: true secrets: inherit + build_macos_arm: + needs: + - cut_release + name: "🍎 MacOS" + uses: ./.github/workflows/macos-build.yaml + with: + cmakePreset: "Release-macos-rosetta-clang-static" + cachePrefix: "static" + uploadArtifacts: true + secrets: inherit + # Upload the Artifacts upload_artifacts: if: github.repository == 'open-goal/jak-project' @@ -87,6 +98,7 @@ jobs: - build_windows_clang - build_linux_clang - build_macos_intel + - build_macos_arm name: "Upload Artifacts" runs-on: ubuntu-latest steps: @@ -124,7 +136,7 @@ jobs: 7z a -tzip ./ci-artifacts/final/opengoal-windows-${TAG_VAL}.zip ./ci-artifacts/windows/* cp ./ci-artifacts/opengoal-windows-static/lsp.exe ./ci-artifacts/final/opengoal-lsp-windows-${TAG_VAL}.exe - - name: Prepare macOS Build Assets + - name: Prepare Intel macOS Build Assets run: | mkdir -p ./ci-artifacts/macos ./.github/scripts/releases/extract_build_unix.sh ./ci-artifacts/macos ./ci-artifacts/opengoal-macos-static ./ @@ -135,6 +147,17 @@ jobs: chmod +x ./ci-artifacts/opengoal-macos-static/lsp/lsp cp ./ci-artifacts/opengoal-macos-static/lsp/lsp ./ci-artifacts/final/opengoal-lsp-macos-intel-${TAG_VAL}.bin + - name: Prepare ARM macOS Build Assets + run: | + mkdir -p ./ci-artifacts/macos-arm + ./.github/scripts/releases/extract_build_unix.sh ./ci-artifacts/macos-arm ./ci-artifacts/opengoal-macos-arm-static ./ + pushd ci-artifacts/macos-arm + TAG_VAL=${{ needs.cut_release.outputs.new_tag }} + tar czf ../final/opengoal-macos-arm-${TAG_VAL}.tar.gz . + popd + chmod +x ./ci-artifacts/opengoal-macos-arm-static/lsp/lsp + cp ./ci-artifacts/opengoal-macos-arm-static/lsp/lsp ./ci-artifacts/final/opengoal-lsp-macos-arm-${TAG_VAL}.bin + - name: Upload Assets env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CMakeLists.txt b/CMakeLists.txt index d065b5877a..74357a2e06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,13 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) project(jak) include(CTest) +# Options +option(BUILD_X86_ON_MACOS "Builds the project for macOS for Rosetta 2" OFF) + # Include third-party modules set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/third-party/cmake/modules/) @@ -119,7 +123,11 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # pin to AVX for macOS, hopefully all macOS runners have atleast this architecture # technically speaking, SSE4 is the cutoff for Apple Silicon so...only a matter of time! - if(NOT CMAKE_CXX_COMPILER_TARGET STREQUAL "arm64-apple-darwin") + if (BUILD_X86_ON_MACOS) + message("Targetting x86 on macOS via Rosetta 2 (requires Sequoia)") + set(CMAKE_OSX_ARCHITECTURES "x86_64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx") + elseif(NOT CMAKE_CXX_COMPILER_TARGET STREQUAL "arm64-apple-darwin") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx") endif() diff --git a/CMakePresets.json b/CMakePresets.json index 1fe236756b..1ff3b0a1fd 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -164,6 +164,15 @@ "description": "Build with Clang as Release without Debug Symbols", "inherits": ["base-macos-release", "base-clang"] }, + { + "name": "Release-macos-rosetta-clang", + "displayName": "MacOS Release Rosetta (clang)", + "description": "Build with Clang as Release without Debug Symbols", + "inherits": ["base-macos-release", "base-clang"], + "cacheVariables": { + "BUILD_X86_ON_MACOS": "ON" + } + }, { "name": "Release-macos-clang-static", "displayName": "MacOS Static Release (clang)", @@ -174,6 +183,17 @@ "ZYDIS_BUILD_SHARED_LIB": "OFF" } }, + { + "name": "Release-macos-rosetta-clang-static", + "displayName": "MacOS Rosetta Static Release (clang)", + "description": "Build with Clang as Release without Debug Symbols but statically linked, requires MacOS Sequoia", + "inherits": ["base-macos-release", "base-clang"], + "cacheVariables": { + "BUILD_X86_ON_MACOS": "ON", + "STATICALLY_LINK": "true", + "ZYDIS_BUILD_SHARED_LIB": "OFF" + } + }, { "name": "Release-linux-clang-asan", "displayName": "Linux Release (clang-asan)", diff --git a/Taskfile.yml b/Taskfile.yml index 0dbea08e39..41e54ddf86 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -67,6 +67,14 @@ tasks: cmds: - "{{.GK_BIN_RELEASE_DIR}}/gk -v --game {{.GAME}} -- -fakeiso -debug" # DEVELOPMENT + gen-cmake: + desc: "Generate the CMake" + cmds: + - "cmake -B build --preset={{.CMAKE_PRESET}}" + build: + desc: "Build the project using the generated CMake" + cmds: + - "cmake --build build --parallel {{.CMAKE_NUM_THREADS}}" repl: desc: "Start the REPL" preconditions: diff --git a/common/util/os.cpp b/common/util/os.cpp index 397a0b62fd..f77491c1c4 100644 --- a/common/util/os.cpp +++ b/common/util/os.cpp @@ -3,6 +3,13 @@ #include "common/common_types.h" #include "common/log/log.h" +#ifdef __APPLE__ +#include + +#include +#include +#endif + #ifdef _WIN32 // clang-format off #define NOMINMAX @@ -104,3 +111,23 @@ void setup_cpu_info() { CpuInfo& get_cpu_info() { return gCpuInfo; } + +std::optional get_macos_version() { +#ifndef __APPLE__ + return {}; +#else + char buffer[128]; + size_t bufferlen = 128; + auto ok = sysctlbyname("kern.osproductversion", &buffer, &bufferlen, NULL, 0); + if (ok != 0) { + lg::warn("Unable to check for `kern.osproductversion` to determine macOS version"); + return {}; + } + try { + return std::stod(buffer); + } catch (std::exception& e) { + lg::error("Error occured when attempting to convert sysctl value {} to number", buffer); + return {}; + } +#endif +} \ No newline at end of file diff --git a/common/util/os.h b/common/util/os.h index de779ee67e..79dc63b029 100644 --- a/common/util/os.h +++ b/common/util/os.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include size_t get_peak_rss(); @@ -16,3 +17,5 @@ struct CpuInfo { }; CpuInfo& get_cpu_info(); + +std::optional get_macos_version(); diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index df32b480ad..d4bada0a36 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -1,7 +1,10 @@ # Set a more convenient ARM flag -if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") +if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64" AND (NOT BUILD_X86_ON_MACOS)) set(ARM64_ARCH TRUE) message(STATUS "ARM64 architecture detected") +elseif() + set(ARM64_ARCH FALSE) + message(STATUS "Forcefully targetting x86 via Rosetta 2") else() set(ARM64_ARCH FALSE) message(STATUS "Non-ARM64 architecture detected") diff --git a/game/main.cpp b/game/main.cpp index 32e982783c..3322e468a3 100644 --- a/game/main.cpp +++ b/game/main.cpp @@ -179,10 +179,26 @@ int main(int argc, char** argv) { setup_cpu_info(); // If the CPU doesn't have AVX, GOAL code won't work and we exit. if (!get_cpu_info().has_avx) { +// Check if we are on a modern enough version of macOS so that AVX can be +// emulated via rosetta +#ifdef __APPLE__ + auto macos_version = get_macos_version(); + if (macos_version < 15.0) { + lg::info( + "Your CPU does not support AVX. But the newer version of Rosetta supports it, update to " + "atleast Sequoia to run OpenGOAL!"); + dialogs::create_error_message_dialog( + "Unmet Requirements", + "Your CPU does not support AVX. But the newer version of Rosetta supports it, update to " + "atleast Sequoia to run OpenGOAL!"); + return -1; + } +#else lg::info("Your CPU does not support AVX, which is required for OpenGOAL."); dialogs::create_error_message_dialog( "Unmet Requirements", "Your CPU does not support AVX, which is required for OpenGOAL."); return -1; +#endif } // set up file paths for resources. This is the full repository when developing, and the data