Release builds #42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Setup-host multi-platform release template for a PSXRecomp *game* repository. | |
| # | |
| # Install: | |
| # mkdir -p .github/workflows | |
| # cp psxrecomp/docs/ci/templates/setup-release.yml .github/workflows/release.yml | |
| # Then replace every YOUR_* token (or let setup_project / fill_tokens.py do it). | |
| # See docs/GAME_PROJECT_SETUP.md. | |
| # | |
| # Shared (do not fork these into the title): | |
| # ./psxrecomp/.github/actions/{build-emitters,fetch-toolchain (optional embed),stage-setup-sdk} | |
| # psxrecomp/tools/ci/*.sh | |
| # psxrecomp/tools/{fetch_toolchain,stage_setup_sdk,bundle_mingw_dlls,package_setup_host}.sh | |
| # psxrecomp/psxrecomp_cli.py (CLI lives in the submodule — no psxrecomp-sdk/) | |
| # | |
| # Title-owned: | |
| # CMake setup-host flags, thin scripts/package_setup_release.sh wrapper | |
| # | |
| # No private CI assets. No BIOS dumps. No generated/ required. | |
| # Do not set PSX_PGO in CI (PGO stays user-local after Generate). | |
| # Release hosts always build with PSX_HAVE_VULKAN (headers + glslc); runtime | |
| # still loads the ICD dynamically via SDL. | |
| # | |
| # workflow_dispatch: leave version empty to auto-bump patch (or choose bump). | |
| # Host-focused releases: CI never ships game C. Players Generate once; RetComM | |
| # Update reuses codegen-cache when ROM/BIOS/emitters match (cmake-only). | |
| # See docs/ci/HOST_ONLY_RELEASES.md. reuse_cached_emitters skips rebuilding | |
| # psxrecomp-game/bios when the psxrecomp SHA cache hits (host/UI bumps). | |
| name: Release builds | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: >- | |
| Release version (e.g. 0.1.0 or v0.1.0). Leave empty to auto-bump | |
| from the latest vX.Y.Z git tag (see bump). | |
| required: false | |
| type: string | |
| default: '' | |
| bump: | |
| description: 'When version is empty, which semver component to increment' | |
| required: false | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| default: patch | |
| publish: | |
| description: 'Create git tag vX.Y.Z and GitHub Release' | |
| required: true | |
| type: boolean | |
| default: true | |
| reuse_cached_emitters: | |
| description: 'Skip emitter rebuild when build-recompiler cache hits (psxrecomp SHA)' | |
| required: false | |
| type: boolean | |
| default: true | |
| push: | |
| tags: | |
| - 'v*' | |
| concurrency: | |
| group: twistedmetal4-release-${{ github.event_name }}-${{ github.ref }}-${{ github.run_id }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: write | |
| env: | |
| CMAKE_BUILD_TYPE: Release | |
| jobs: | |
| prepare: | |
| name: Resolve version | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| version: ${{ steps.meta.outputs.version }} | |
| tag: ${{ steps.meta.outputs.tag }} | |
| publish: ${{ steps.meta.outputs.publish }} | |
| reuse_emitters: ${{ steps.meta.outputs.reuse_emitters }} | |
| steps: | |
| - name: Checkout (tags for auto-bump) | |
| if: github.event_name == 'workflow_dispatch' | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Normalize version / tag | |
| id: meta | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| RAW="${{ github.event.inputs.version }}" | |
| BUMP="${{ github.event.inputs.bump || 'patch' }}" | |
| PUBLISH="${{ github.event.inputs.publish }}" | |
| REUSE="${{ github.event.inputs.reuse_cached_emitters }}" | |
| else | |
| RAW="${{ github.ref_name }}" | |
| BUMP="patch" | |
| PUBLISH="true" | |
| REUSE="true" | |
| fi | |
| RAW="$(printf '%s' "${RAW}" | tr -d '[:space:]')" | |
| if [[ -z "${RAW}" ]]; then | |
| # Prefer shared helper when present (optional); else inline bump. | |
| if [[ -x psxrecomp/tools/ci/normalize_version.sh ]]; then | |
| eval "$(bash psxrecomp/tools/ci/normalize_version.sh --next "${BUMP}" | grep -E '^(VERSION|TAG)=')" | |
| VER="${VERSION}" | |
| TAG="${TAG}" | |
| else | |
| LATEST="$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -1 || true)" | |
| BASE="${LATEST#v}" | |
| if [[ -z "${BASE}" && -f VERSION ]]; then | |
| BASE="$(tr -d '[:space:]' <VERSION)" | |
| BASE="${BASE#v}" | |
| fi | |
| BASE="${BASE:-0.0.0}" | |
| BASE="${BASE%%[-+]*}" | |
| IFS=. read -r MA MI PA <<<"${BASE}" | |
| MA="${MA:-0}"; MI="${MI:-0}"; PA="${PA:-0}" | |
| case "${BUMP}" in | |
| major) MA=$((MA + 1)); MI=0; PA=0 ;; | |
| minor) MI=$((MI + 1)); PA=0 ;; | |
| *) PA=$((PA + 1)) ;; | |
| esac | |
| VER="${MA}.${MI}.${PA}" | |
| TAG="v${VER}" | |
| echo "Auto-bumped ${BUMP}: ${LATEST:-none} → ${VER}" | |
| fi | |
| else | |
| VER="${RAW#v}" | |
| if [[ ! "${VER}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.+-][A-Za-z0-9.+-]*)?$ ]]; then | |
| echo "::error::invalid version '${RAW}' (want X.Y.Z or vX.Y.Z)" | |
| exit 1 | |
| fi | |
| TAG="v${VER}" | |
| fi | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && \ | |
| git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null 2>&1; then | |
| echo "::error::tag ${TAG} already exists — bump again or pass an explicit version" | |
| exit 1 | |
| fi | |
| echo "version=${VER}" >>"$GITHUB_OUTPUT" | |
| echo "tag=${TAG}" >>"$GITHUB_OUTPUT" | |
| echo "publish=${PUBLISH}" >>"$GITHUB_OUTPUT" | |
| echo "reuse_emitters=${REUSE}" >>"$GITHUB_OUTPUT" | |
| echo "Using version ${VER} (tag ${TAG}), publish=${PUBLISH}, reuse_emitters=${REUSE}" | |
| build: | |
| name: ${{ matrix.artifact }} | |
| needs: prepare | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-24.04 | |
| artifact: linux-x64 | |
| shell: bash | |
| - os: windows-2022 | |
| artifact: windows-x64 | |
| shell: 'msys2 {0}' | |
| - os: macos-15 | |
| artifact: macos-arm64 | |
| shell: bash | |
| deployment_target: '11.0' | |
| - os: macos-15-intel | |
| artifact: macos-x64 | |
| shell: bash | |
| deployment_target: '11.0' | |
| defaults: | |
| run: | |
| shell: ${{ matrix.shell }} | |
| env: | |
| # Rename to match your packager (e.g. BPE_RELEASE_VERSION). | |
| RELEASE_VERSION: ${{ needs.prepare.outputs.version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| fetch-depth: 1 | |
| - name: Pin VERSION | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| printf '%s\n' "${RELEASE_VERSION}" >VERSION | |
| echo "VERSION=$(cat VERSION)" | |
| # Submodule gitlinks are the pin; this step only logs SHAs for the job. | |
| - name: Record framework revisions | |
| shell: bash | |
| run: bash psxrecomp/tools/ci/record_pins.sh | |
| - name: Setup host mode (no game C / BIOS backends in CI) | |
| shell: bash | |
| run: bash psxrecomp/tools/ci/clear_generated.sh | |
| - name: Setup MSYS2 (Windows) | |
| if: runner.os == 'Windows' | |
| uses: msys2/setup-msys2@v2 | |
| with: | |
| msystem: MINGW64 | |
| update: true | |
| install: >- | |
| mingw-w64-x86_64-toolchain | |
| mingw-w64-x86_64-cmake | |
| mingw-w64-x86_64-ninja | |
| mingw-w64-x86_64-SDL2 | |
| mingw-w64-x86_64-pkgconf | |
| mingw-w64-x86_64-vulkan-headers | |
| mingw-w64-x86_64-shaderc | |
| curl | |
| zip | |
| - name: Install deps (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| # X11 + xtst required when FetchContent builds SDL3 (XTEST). | |
| sudo apt-get install -y --no-install-recommends \ | |
| build-essential cmake ninja-build pkg-config \ | |
| libsdl2-dev libgl1-mesa-dev libxtst-dev \ | |
| libx11-dev libxext-dev libxrandr-dev libxcursor-dev \ | |
| libxfixes-dev libxi-dev libxss-dev libxkbcommon-dev \ | |
| libasound2-dev libpulse-dev libudev-dev zip \ | |
| libvulkan-dev glslc | |
| - name: Install deps (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| brew install cmake ninja pkg-config sdl3 \ | |
| vulkan-headers shaderc molten-vk \ | |
| || brew install cmake ninja pkg-config sdl2 \ | |
| vulkan-headers shaderc molten-vk | |
| # Headers + glslc are enough to compile PSX_HAVE_VULKAN (runtime still | |
| # loads the ICD dynamically via SDL). Fail early if the stub path would | |
| # be selected so release hosts always ship the real Vulkan backend. | |
| - name: Verify Vulkan build tools | |
| run: | | |
| set -euo pipefail | |
| command -v glslc | |
| glslc --version || true | |
| HDR="" | |
| for candidate in \ | |
| /mingw64/include/vulkan/vulkan.h \ | |
| /usr/include/vulkan/vulkan.h \ | |
| /opt/homebrew/include/vulkan/vulkan.h \ | |
| /usr/local/include/vulkan/vulkan.h; do | |
| if [[ -f "${candidate}" ]]; then | |
| HDR="${candidate}" | |
| break | |
| fi | |
| done | |
| if [[ -z "${HDR}" ]] && command -v brew >/dev/null 2>&1; then | |
| BREW_HDR="$(brew --prefix)/include/vulkan/vulkan.h" | |
| if [[ -f "${BREW_HDR}" ]]; then | |
| HDR="${BREW_HDR}" | |
| fi | |
| fi | |
| if [[ -z "${HDR}" ]]; then | |
| echo "::error::vulkan/vulkan.h not found" | |
| exit 1 | |
| fi | |
| echo "Found ${HDR}" | |
| # Ubuntu 24.04 has no libsdl3-dev; Windows MSYS ships SDL2. Setup-host | |
| # therefore FetchContents the pinned SDL3 tarball. CMake's HTTP/2 download | |
| # from github.com release assets intermittently dies with REFUSED_STREAM | |
| # (curl status 56). Prefetch with curl --http1.1 and point FetchContent | |
| # at the extracted tree (macOS uses brew sdl3 and skips this). | |
| - name: Cache SDL3 source | |
| if: runner.os != 'macOS' | |
| uses: actions/cache@v4 | |
| with: | |
| path: .cache/sdl3-src | |
| key: sdl3-3.4.10-src | |
| - name: Prefetch SDL3 source | |
| if: runner.os != 'macOS' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # Use repo-relative paths for tar: Windows GITHUB_WORKSPACE is D:\… | |
| # and GNU tar treats drive-letter abs paths as remote hosts | |
| # ("Cannot connect to D"). Prefer prefetch_sdl3.sh when present. | |
| OUT=".cache/sdl3-src" | |
| sdl3_env() { | |
| local abs="${OUT}" | |
| if command -v cygpath >/dev/null 2>&1; then | |
| abs="$(cygpath -m "${OUT}")" | |
| elif [[ -d "${OUT}" ]]; then | |
| abs="$(cd "${OUT}" && pwd)" | |
| fi | |
| echo "PSX_SDL3_SOURCE_DIR=${abs}" >>"$GITHUB_ENV" | |
| echo "SDL3 source ready: ${abs}" | |
| } | |
| if [[ -f "${OUT}/CMakeLists.txt" ]]; then | |
| sdl3_env | |
| echo "SDL3 source cache hit" | |
| exit 0 | |
| fi | |
| if [[ -f psxrecomp/tools/ci/prefetch_sdl3.sh ]]; then | |
| chmod +x psxrecomp/tools/ci/prefetch_sdl3.sh | |
| bash psxrecomp/tools/ci/prefetch_sdl3.sh --out-dir "${OUT}" | |
| exit 0 | |
| fi | |
| # Submodule may predate the helper — inline HTTP/1.1 prefetch. | |
| echo "prefetch_sdl3.sh missing; using inline curl --http1.1" | |
| mkdir -p .cache | |
| VER=3.4.10 | |
| SHA=12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 | |
| URL="https://github.com/libsdl-org/SDL/releases/download/release-${VER}/SDL3-${VER}.tar.gz" | |
| TAR=".cache/SDL3-${VER}.tar.gz" | |
| curl -fsSL --http1.1 --retry 5 --retry-all-errors --retry-delay 2 -o "${TAR}" "${URL}" | |
| echo "${SHA} ${TAR}" | sha256sum -c - | |
| rm -rf "${OUT}.extracting" | |
| mkdir -p "${OUT}.extracting" | |
| # --force-local: belt-and-suspenders if a path ever gains a colon. | |
| if tar --help 2>&1 | grep -q -- '--force-local'; then | |
| tar --force-local -xzf "${TAR}" -C "${OUT}.extracting" | |
| else | |
| tar -xzf "${TAR}" -C "${OUT}.extracting" | |
| fi | |
| INNER="$(find "${OUT}.extracting" -mindepth 1 -maxdepth 1 -type d | head -n1)" | |
| rm -rf "${OUT}" | |
| mv "${INNER}" "${OUT}" | |
| rm -rf "${OUT}.extracting" | |
| sdl3_env | |
| # Windows: build emitters with llvm-mingw from cmake-clang-v1 (static CLI). | |
| # Zip still omits toolchain/ — fetch is for the build machine only. | |
| - name: Fetch portable toolchain (Windows emitters) | |
| if: runner.os == 'Windows' | |
| uses: ./psxrecomp/.github/actions/fetch-toolchain | |
| with: | |
| artifact: ${{ matrix.artifact }} | |
| # Host/UI-only bumps keep the same psxrecomp gitlink → cache hit skips | |
| # rebuilding emitters (see reuse_cached_emitters / HOST_ONLY_RELEASES.md). | |
| - name: Emitter cache key | |
| id: emitter_cache | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| SHA="$(git -C psxrecomp rev-parse HEAD)" | |
| echo "psxrecomp_sha=${SHA}" >>"$GITHUB_OUTPUT" | |
| echo "key=${{ runner.os }}-${{ matrix.artifact }}-emitters-${SHA}" >>"$GITHUB_OUTPUT" | |
| echo "psxrecomp SHA ${SHA}" | |
| - name: Cache emitters (build-recompiler) | |
| id: cache_emitters | |
| uses: actions/cache@v4 | |
| with: | |
| path: build-recompiler | |
| key: ${{ steps.emitter_cache.outputs.key }} | |
| - name: Build recompiler tools (emitters) | |
| if: >- | |
| steps.cache_emitters.outputs.cache-hit != 'true' || | |
| needs.prepare.outputs.reuse_emitters != 'true' | |
| uses: ./psxrecomp/.github/actions/build-emitters | |
| - name: Verify emitters present | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # Restored cache or fresh build must leave packager inputs in place. | |
| test -d build-recompiler | |
| find build-recompiler -type f \( -name 'psxrecomp-game*' -o -name 'psxrecomp-bios*' \) | head | |
| count="$(find build-recompiler -type f \( -name 'psxrecomp-game*' -o -name 'psxrecomp-bios*' \) | wc -l | tr -d ' ')" | |
| if [[ "${count}" -lt 2 ]]; then | |
| echo "::error::expected psxrecomp-game + psxrecomp-bios under build-recompiler (found ${count})" | |
| exit 1 | |
| fi | |
| - name: Setup ccache (Linux/macOS host build) | |
| if: runner.os != 'Windows' | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| key: setup-host-${{ matrix.artifact }} | |
| max-size: 800M | |
| - name: Configure & build setup host | |
| env: | |
| CMAKE_OSX_DEPLOYMENT_TARGET: ${{ matrix.deployment_target || '' }} | |
| run: | | |
| set -euo pipefail | |
| EXTRA=() | |
| if [[ -n "${CMAKE_OSX_DEPLOYMENT_TARGET:-}" ]]; then | |
| EXTRA+=("-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") | |
| fi | |
| if [[ "$(uname -s)" != MINGW* && "$(uname -s)" != MSYS* ]] && command -v ccache >/dev/null 2>&1; then | |
| EXTRA+=("-DCMAKE_C_COMPILER_LAUNCHER=ccache") | |
| EXTRA+=("-DCMAKE_CXX_COMPILER_LAUNCHER=ccache") | |
| fi | |
| # Point CMake at Vulkan headers/glslc. Prefer VULKAN_SDK layout | |
| # (runtime.cmake checks $VULKAN_SDK first); also seed CMAKE_PREFIX_PATH | |
| # so find_path works after the empty-_vk_inc fix in runtime.cmake. | |
| if [[ "$(uname -s)" == "Darwin" ]]; then | |
| BREW_PREFIX="$(brew --prefix)" | |
| export PATH="${BREW_PREFIX}/bin${PATH:+:${PATH}}" | |
| if command -v brew >/dev/null 2>&1; then | |
| SHADERC_PREFIX="$(brew --prefix shaderc 2>/dev/null || true)" | |
| VK_HDR_PREFIX="$(brew --prefix vulkan-headers 2>/dev/null || true)" | |
| if [[ -n "${SHADERC_PREFIX}" && -d "${SHADERC_PREFIX}/bin" ]]; then | |
| export PATH="${SHADERC_PREFIX}/bin:${PATH}" | |
| fi | |
| for p in "${VK_HDR_PREFIX}" "${SHADERC_PREFIX}" "${BREW_PREFIX}"; do | |
| if [[ -n "${p}" && -d "${p}" ]]; then | |
| export CMAKE_PREFIX_PATH="${p}${CMAKE_PREFIX_PATH:+:${CMAKE_PREFIX_PATH}}" | |
| fi | |
| done | |
| else | |
| export CMAKE_PREFIX_PATH="${BREW_PREFIX}${CMAKE_PREFIX_PATH:+:${CMAKE_PREFIX_PATH}}" | |
| fi | |
| if [[ -f "${BREW_PREFIX}/include/vulkan/vulkan.h" ]]; then | |
| export VULKAN_SDK="${BREW_PREFIX}" | |
| elif [[ -n "${VK_HDR_PREFIX:-}" && -f "${VK_HDR_PREFIX}/include/vulkan/vulkan.h" ]]; then | |
| export VULKAN_SDK="${VK_HDR_PREFIX}" | |
| fi | |
| elif [[ "$(uname -s)" == "Linux" ]]; then | |
| if [[ -z "${VULKAN_SDK:-}" && -f /usr/include/vulkan/vulkan.h ]]; then | |
| export VULKAN_SDK=/usr | |
| fi | |
| elif [[ "$(uname -s)" == MINGW* || "$(uname -s)" == MSYS* ]]; then | |
| if [[ -z "${VULKAN_SDK:-}" && -f /mingw64/include/vulkan/vulkan.h ]]; then | |
| export VULKAN_SDK=/mingw64 | |
| fi | |
| fi | |
| if command -v glslc >/dev/null 2>&1; then | |
| EXTRA+=("-DGLSLC_EXE=$(command -v glslc)") | |
| fi | |
| JOBS="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)" | |
| # Emitters may have set PSXRECOMP_TOOLCHAIN_DIR (llvm-mingw) via | |
| # GITHUB_ENV. Clear it before the MSYS2 g++ setup-host configure so | |
| # pack lib/ cannot win -L for -static-libstdc++ (codecvt link errors). | |
| if [[ "$(uname -s)" == MINGW* || "$(uname -s)" == MSYS* ]]; then | |
| unset PSXRECOMP_TOOLCHAIN_DIR TOOLCHAIN_DIR BPE_TOOLCHAIN_DIR RETCOMM_TOOLCHAIN_DIR || true | |
| unset CMAKE_PREFIX_PATH SDL3_DIR ZLIB_ROOT || true | |
| fi | |
| # Setup host: no game C, no BIOS backends linked. clear_generated.sh | |
| # already wiped generated/; end users Generate OpenBIOS (+ optional | |
| # SCPH1001) locally via the wizard, then rebuild. | |
| # Prefer -DPSXRECOMP_FORCE_SETUP_HOST=ON (psxrecomp_add_game_runtime). | |
| # Legacy -DYOURGAME_FORCE_SETUP_HOST=ON still works if the title wires it. | |
| # PSX_SETUP_WIZARD is required with FORCE_SETUP_HOST (CMakeLists | |
| # ENABLE_SETUP_WIZARD + -D here). Without it the zip never opens | |
| # first-run / Generate & rebuild. | |
| # Title root codegen_setup.c must define | |
| # psx_game_codegen_forward_if_built (see codegen_setup.c.in) or link | |
| # fails — main.cpp always calls it, including setup-host CI builds. | |
| # Netplay titles: set PSX_NETPLAY ON in CMakeLists *before* | |
| # include(runtime.cmake). ENABLE_NETPLAY_IF_PRESENT alone is too late | |
| # (option() already cached OFF) and psx_netplay_sched.c still needs | |
| # recomp_net/session.h. Also add -DPSX_NETPLAY=ON to this cmake | |
| # invocation for CI belt-and-suspenders (do not leave it commented | |
| # mid-continuation — that breaks the shell line). | |
| # With PSX_NETPLAY, runtime.cmake defaults RNET_ENABLE_ICE=ON. | |
| # recomp-net FetchContents libjuice via a pinned URL tarball (not | |
| # git clone) so AppImage / mismatched libcurl hosts still configure. | |
| # Offline: vendor under psxrecomp/lib/recomp-net/third_party/libjuice | |
| # or pass -DRNET_LIBJUICE_ROOT=… / -DRNET_ENABLE_ICE=OFF. | |
| # Add title-specific -D flags here if needed (e.g. -DMOTK_NATIVE=OFF). | |
| # Netplay titles: also pass -DPSX_NETPLAY=ON here (belt-and-suspenders | |
| # when CMakeLists already FORCE-caches it before include(runtime.cmake)). | |
| # Prefer pre-fetched SDL3 (see Prefetch SDL3 source) so configure does | |
| # not call CMake file(DOWNLOAD) against GitHub release assets. | |
| if [[ -n "${PSX_SDL3_SOURCE_DIR:-}" && -f "${PSX_SDL3_SOURCE_DIR}/CMakeLists.txt" ]]; then | |
| EXTRA+=("-DFETCHCONTENT_SOURCE_DIR_SDL3=${PSX_SDL3_SOURCE_DIR}") | |
| elif [[ -f "${GITHUB_WORKSPACE}/.cache/sdl3-src/CMakeLists.txt" ]]; then | |
| EXTRA+=("-DFETCHCONTENT_SOURCE_DIR_SDL3=${GITHUB_WORKSPACE}/.cache/sdl3-src") | |
| fi | |
| cmake -S . -B build-ci -G Ninja \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DPSXRECOMP_FORCE_SETUP_HOST=ON \ | |
| -DPSXRECOMP_ALLOW_NO_BIOS=ON \ | |
| -DPSX_SETUP_WIZARD=ON \ | |
| -DPSX_GAME_VERSION="${RELEASE_VERSION}" \ | |
| "${EXTRA[@]}" 2>&1 | tee build-ci-configure.log | |
| if ! grep -q 'Vulkan backend: headers' build-ci-configure.log; then | |
| echo "::error::CMake did not enable PSX_HAVE_VULKAN (see configure log)" | |
| exit 1 | |
| fi | |
| # Ninja does not emit flags.make (Makefiles generator only). Probe | |
| # whichever compile-flag artifact exists for PSX_HAVE_VULKAN. | |
| FOUND_VK_DEFINE=0 | |
| for f in \ | |
| build-ci/build.ninja \ | |
| build-ci/compile_commands.json \ | |
| build-ci/CMakeFiles/psx-runtime.dir/flags.make; do | |
| if [[ -f "${f}" ]] && grep -q 'PSX_HAVE_VULKAN' "${f}"; then | |
| FOUND_VK_DEFINE=1 | |
| echo "Found PSX_HAVE_VULKAN in ${f}" | |
| break | |
| fi | |
| done | |
| if [[ "${FOUND_VK_DEFINE}" -ne 1 ]]; then | |
| echo "::error::PSX_HAVE_VULKAN missing from psx-runtime compile flags" | |
| exit 1 | |
| fi | |
| cmake --build build-ci --target psx-runtime -j"${JOBS}" | |
| # Lobby list pin must match RELEASE_VERSION (package_setup_host enforces). | |
| STAMP="" | |
| for cand in \ | |
| build-ci/psx_game_version.txt \ | |
| build-ci/Release/psx_game_version.txt \ | |
| build-ci/*/psx_game_version.txt | |
| do | |
| if [[ -f "${cand}" ]]; then STAMP="${cand}"; break; fi | |
| done | |
| if [[ -z "${STAMP}" ]]; then | |
| # Fallback: stamp next to the host exe | |
| STAMP="$(find build-ci -name psx_game_version.txt -type f 2>/dev/null | head -n1 || true)" | |
| fi | |
| if [[ -z "${STAMP}" || ! -f "${STAMP}" ]]; then | |
| echo "::error::missing psx_game_version.txt after build (update psxrecomp submodule)" | |
| exit 1 | |
| fi | |
| BUILT="$(tr -d '[:space:]' <"${STAMP}")" | |
| BUILT="${BUILT#v}" | |
| if [[ "${BUILT}" != "${RELEASE_VERSION}" ]]; then | |
| echo "::error::lobby pin stamp ${BUILT} != RELEASE_VERSION ${RELEASE_VERSION} (${STAMP})" | |
| exit 1 | |
| fi | |
| echo "lobby pin ok: ${BUILT} (${STAMP})" | |
| - name: Package setup zip | |
| run: | | |
| set -euo pipefail | |
| chmod +x scripts/package_setup_release.sh | |
| if [[ "${{ runner.os }}" == "Windows" ]]; then | |
| export PSXRECOMP_RUNTIME_BIN_DIR=/mingw64/bin | |
| export BPE_RUNTIME_BIN_DIR=/mingw64/bin | |
| fi | |
| export RELEASE_VERSION | |
| # Lean zip (no embedded toolchain/). RetComM / wizard download | |
| # cmake-clang-v1; pass --embed-toolchain only for offline-first packs. | |
| scripts/package_setup_release.sh build-ci "${{ matrix.artifact }}" build-recompiler | |
| ls -la dist/ | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| # Artifact name uses the title zip prefix (e.g. bpe, motk). | |
| name: twistedmetal4-${{ matrix.artifact }} | |
| path: dist/twistedmetal4-*-${{ matrix.artifact }}.zip | |
| if-no-files-found: error | |
| retention-days: 30 | |
| release: | |
| name: GitHub Release | |
| needs: [prepare, build] | |
| if: needs.prepare.outputs.publish == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout (for tagging on workflow_dispatch) | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| merge-multiple: false | |
| - name: Flatten zips | |
| run: | | |
| set -euo pipefail | |
| mkdir -p dist | |
| find artifacts -type f -name 'twistedmetal4-*.zip' -exec cp -v {} dist/ \; | |
| ls -la dist/ | |
| - name: Ensure release tag exists | |
| if: github.event_name == 'workflow_dispatch' | |
| env: | |
| TAG: ${{ needs.prepare.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| if ! git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then | |
| git tag -a "${TAG}" -m "TwistedMetal4 Recompiled ${TAG}" | |
| fi | |
| if ! git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then | |
| git push origin "refs/tags/${TAG}" | |
| fi | |
| - name: Publish release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.prepare.outputs.tag }} | |
| # Quoted: titles may contain ':' (e.g. "Marvel vs. Capcom: …"). | |
| name: "TwistedMetal4 Recompiled ${{ needs.prepare.outputs.tag }}" | |
| files: | | |
| dist/twistedmetal4-*.zip | |
| generate_release_notes: true | |
| fail_on_unmatched_files: true | |
| target_commitish: ${{ github.sha }} |