name: build-windows # Reusable: builds the Windows binaries. The MSVC build always runs; with pgo=both the MinGW clang # PGO build runs too (MSVC cannot read an LLVM .profdata file, so Windows PGO uses MSYS2's clang # like Eden does). on: workflow_call: inputs: sha: type: string required: true version: type: string required: true display: description: Human-facing build identity (plain variant) type: string default: "" display_pgo: description: Human-facing build identity (PGO variant) type: string default: "" refspec: type: string required: true tag: type: string required: true release: type: boolean default: false nightly: type: boolean default: true pgo: description: none or both type: string default: none save_cache: description: Force-save the compiler and CPM caches at the end of the job type: boolean default: false permissions: contents: read jobs: msvc: name: windows-x86_64 if: inputs.pgo != 'only' runs-on: windows-2025 env: PLATFORM: windows-x86_64 SCCACHE_DIR: ${{ github.workspace }}/.sccache SCCACHE_CACHE_SIZE: 1000M steps: - name: Check out source uses: actions/checkout@v7 with: ref: ${{ inputs.sha }} persist-credentials: false - name: Setup build from checked-out source if: hashFiles('.github/actions/setup-build/action.yml') != '' uses: ./.github/actions/setup-build with: platform: ${{ env.PLATFORM }} sha: ${{ inputs.sha }} refspec: ${{ inputs.refspec }} tag: ${{ inputs.tag }} release: ${{ inputs.release }} save-cache: ${{ inputs.save_cache }} display: ${{ inputs.display }} - name: Setup build from compatibility pin if: hashFiles('.github/actions/setup-build/action.yml') == '' uses: Djihads80/Eglantine/.github/actions/setup-build@583b125d5a2a330bc3e04fed213b908db5f6a8f2 with: platform: ${{ env.PLATFORM }} sha: ${{ inputs.sha }} refspec: ${{ inputs.refspec }} tag: ${{ inputs.tag }} release: ${{ inputs.release }} save-cache: ${{ inputs.save_cache }} - name: Set up MSVC uses: ilammy/msvc-dev-cmd@v1 - name: Install Qt on Windows uses: jurplel/install-qt-action@v4 with: version: "6.8.3" host: windows target: desktop arch: win64_msvc2022_64 modules: qtcharts cache: true # MSVC is not something ccache handles well, so this job uses sccache. - name: Install sccache on Windows shell: pwsh run: | $zip = Join-Path $env:RUNNER_TEMP 'sccache.zip' Invoke-WebRequest -OutFile $zip https://github.com/mozilla/sccache/releases/download/v0.19.0/sccache-v0.19.0-x86_64-pc-windows-msvc.zip Expand-Archive $zip -DestinationPath (Join-Path $env:RUNNER_TEMP 'sccache') $bin = (Get-ChildItem -Recurse -Filter sccache.exe (Join-Path $env:RUNNER_TEMP 'sccache')).DirectoryName $bin | Out-File -Append -Encoding utf8 $env:GITHUB_PATH - name: Install Vulkan SDK on Windows shell: pwsh run: ./tools/windows/install-vulkan-sdk.ps1 - name: Verify Vulkan tools on Windows shell: pwsh run: glslangValidator --version - name: Configure shell: bash run: >- cmake -S . -B build/release -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF -DYUZU_TESTS=OFF -DYUZU_CMD=ON -DYUZU_ROOM=OFF -DYUZU_ROOM_STANDALONE=OFF -DYUZU_USE_BUNDLED_QT=OFF -DYUZU_USE_BUNDLED_SDL3=ON -DENABLE_QT_TRANSLATION=ON -DENABLE_UPDATE_CHECKER=${{ (inputs.release || startsWith(inputs.tag, 'daily-')) && 'ON' || 'OFF' }} -DEGLANTINE_UPDATE_REPO=${{ github.repository }} -DEGLANTINE_RELEASE_PLATFORM=windows-x86_64 -DUSE_DISCORD_PRESENCE=ON -DNIGHTLY_BUILD=${{ inputs.nightly && 'ON' || 'OFF' }} -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache - name: sccache zero stats shell: bash run: sccache --zero-stats - name: Build shell: bash run: | targets="yuzu yuzu-cmd" # The updater helper only exists in release and daily builds, where the update checker is on. if [ "${{ inputs.release || startsWith(inputs.tag, 'daily-') }}" = "true" ]; then targets="$targets eglantine-updater"; fi cmake --build build/release --target $targets --parallel sccache --show-stats - name: Package Windows binary shell: pwsh run: | foreach ($exe in 'eglantine.exe', 'eden-cli.exe') { if (-not (Test-Path "build/release/bin/$exe")) { throw "build/release/bin/$exe was not produced" } } windeployqt --release --no-translations build/release/bin/eglantine.exe # The artifact is the bin folder itself: GitHub zips artifacts on download, and uploading a # zip gave a zip inside a zip. The release job zips it once for the release. - name: Upload Windows binary uses: actions/upload-artifact@v7 with: name: eglantine-windows-x86_64-${{ inputs.sha }} path: build/release/bin/ if-no-files-found: error retention-days: 7 mingw-pgo: name: windows-x86_64-pgo if: inputs.pgo == 'both' || inputs.pgo == 'only' runs-on: windows-2025 env: PLATFORM: windows-x86_64-pgo steps: - name: Check out source uses: actions/checkout@v7 with: ref: ${{ inputs.sha }} persist-credentials: false - name: Setup build from checked-out source if: hashFiles('.github/actions/setup-build/action.yml') != '' uses: ./.github/actions/setup-build with: platform: ${{ env.PLATFORM }} sha: ${{ inputs.sha }} refspec: ${{ inputs.refspec }} tag: ${{ inputs.tag }} release: ${{ inputs.release }} save-cache: ${{ inputs.save_cache }} - name: Setup build from compatibility pin if: hashFiles('.github/actions/setup-build/action.yml') == '' uses: Djihads80/Eglantine/.github/actions/setup-build@583b125d5a2a330bc3e04fed213b908db5f6a8f2 with: platform: ${{ env.PLATFORM }} sha: ${{ inputs.sha }} refspec: ${{ inputs.refspec }} tag: ${{ inputs.tag }} release: ${{ inputs.release }} save-cache: ${{ inputs.save_cache }} display: ${{ inputs.display_pgo }} # The profile is in the source tree, where the configure step reads it. - name: Check PGO profile shell: bash run: | set -euo pipefail test -s eglantine.profdata # Eden builds its Windows PGO binaries with MSYS2's clang toolchain, so we do the same. The # package list is docs/Deps.md (MSYS2) plus what the bundled FFmpeg needs to compile. # `update: true` (a full `pacman -Syu`) is kept on purpose: skipping it installs from # whatever package database the cached root carries, and on a cache miss that is the one # bundled with the MSYS2 release. Tried it - the fresh root plus a stale DB produced a # toolchain that could not compile a single test program ("Detecting C compiler ABI info - # failed"), so the saved ~25 s is not worth a leg that dies once the cache is evicted. - name: Set up MSYS2 uses: msys2/setup-msys2@v2 with: msystem: UCRT64 update: true cache: true install: >- git make autoconf libtool automake-wrapper patch jq pactoys ninja pacboy: >- toolchain:p clang:p lld:p cmake:p python-pip:p qt6-base:p qt6-charts:p qt6-tools:p qt6-translations:p qt6-svg:p openssl:p boost:p fmt:p lz4:p nlohmann-json:p zlib:p zstd:p libiconv:p vulkan-memory-allocator:p vulkan-devel:p glslang:p enet:p opus:p libusb:p unordered_dense:p jbigkit:p xz:p libdeflate:p bzip2:p lerc:p graphite2:p libwebp:p libtiff:p brotli:p ccache:p # MinGW build: system Qt and libraries from MSYS2, clang plus the repo's PGO profile. The profile # path is converted to its Windows form because clang runs with the MSYS mount underneath. - name: Configure shell: msys2 {0} run: | set -euo pipefail profile="-fprofile-use=$(cygpath -m "$PWD/eglantine.profdata") -Wno-backend-plugin \ -Wno-profile-instr-unprofiled -Wno-profile-instr-out-of-date" cmake -S . -B build/release -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING=OFF \ -DYUZU_TESTS=OFF \ -DYUZU_CMD=ON \ -DYUZU_ROOM=OFF \ -DYUZU_ROOM_STANDALONE=OFF \ -DYUZU_USE_BUNDLED_QT=OFF \ -DYUZU_USE_BUNDLED_SDL3=ON \ -DYUZU_USE_BUNDLED_FFMPEG=ON \ -DVulkanHeaders_FORCE_BUNDLED=ON \ -DVulkanUtilityLibraries_FORCE_BUNDLED=ON \ -DENABLE_QT_TRANSLATION=ON \ -DENABLE_UPDATE_CHECKER=${{ (inputs.release || startsWith(inputs.tag, 'daily-')) && 'ON' || 'OFF' }} \ -DEGLANTINE_UPDATE_REPO=${{ github.repository }} \ -DEGLANTINE_RELEASE_PLATFORM=windows-x86_64-pgo \ -DUSE_DISCORD_PRESENCE=ON \ -DUSE_CCACHE=ON \ -DNIGHTLY_BUILD=${{ inputs.nightly && 'ON' || 'OFF' }} \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_EXE_LINKER_FLAGS=-fuse-ld=lld \ "-DCMAKE_C_FLAGS=$profile" \ "-DCMAKE_CXX_FLAGS=$profile" - name: ccache settings shell: msys2 {0} run: | ccache --version | head -1 ccache -p | grep -vE '^#' | grep -E '(cache_dir|compiler_check|base_dir|hash_dir|sloppiness|max_size|umask)' || true pacman -Q ccache clang 2>/dev/null || true - name: Build shell: msys2 {0} run: | ccache --zero-stats targets="yuzu yuzu-cmd" # The updater helper only exists in release and daily builds, where the update checker is on. if [ "${{ inputs.release || startsWith(inputs.tag, 'daily-') }}" = "true" ]; then targets="$targets eglantine-updater"; fi cmake --build build/release --target $targets --parallel ccache --show-stats # A dynamic MinGW build is not portable on its own: windeployqt6 brings the Qt libraries and # plugins, then every non-system DLL the binaries and those plugins pull in gets copied next to # them (docs/Caveats.md#msys2). - name: Package MinGW binary shell: msys2 {0} run: | set -euo pipefail export PATH="/ucrt64/bin:$PATH" cd build/release/bin test -f eglantine.exe test -f eden-cli.exe windeployqt6 --no-translations --no-compiler-runtime --no-opengl-sw \ --no-system-dxc-compiler --no-system-d3d-compiler eglantine.exe deps() { objdump -p "$1" | awk '/DLL Name:/ {print $3}' | while read -r dll; do [ -z "$dll" ] && continue source=$(command -v "$dll" 2>/dev/null || true) [ -z "$source" ] && continue # The system DLLs ship with Windows, so they never need bundling. case "$source" in *System32* | *SysWOW64*) continue ;; esac if [ ! -f "$dll" ]; then cp "$source" "$dll" deps "$source" fi done } for exe in eglantine.exe eden-cli.exe; do deps "$exe" done find . -name '*.dll' | while read -r dll; do deps "$dll" done - name: Upload MinGW binary uses: actions/upload-artifact@v7 with: name: eglantine-windows-x86_64-pgo-${{ inputs.sha }} path: build/release/bin/ if-no-files-found: error retention-days: 7