diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 90166ff03..7e31fed66 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -248,20 +248,23 @@ jobs: # Debian 13 Release + suite 625 MB 800M # Fedora 45 debug Debug + suite 660 MB 850M # Fedora 45 release Release + package 614 MB 800M + # Windows 10 Release + package 1.2 GB working set 1300M # - # That is 3.3 GB of bounds, and with the fixed entries above (about 4.4 GB) it puts - # the quota at roughly 7.7 GB of 10 - so only legs worth their share get one. A leg - # keeps no cache when it duplicates another leg's coverage (Ubuntu 26.04 behind - # Debian 13's Release + suite; the macOS legs behind the Linux ones) or finishes well - # inside the Windows leg (Servatrice covers the server alone in 4.7 min). A macOS + # That is 4.6 GB of bounds; what lands in the quota is 4.3 GB of compiler cache, + # 2.97 GB stored by the Linux legs and 1.30 GB by Windows, for 8.4 GB of 10 in total. + # So only legs worth their share get one. A leg keeps no cache when it duplicates + # another leg's coverage (Ubuntu 26.04 behind Debian 13's Release + suite; the macOS + # legs behind the Linux ones) or finishes well inside the Windows leg (Servatrice + # covers the server alone in 4.7 min). A macOS # cache served 42% and saved about 32s on a 20 min leg that is already shorter than # Windows, so that bound is better spent here. The four Linux legs that stay compile # the client on the toolchains a change can break and are the ones a cache shortens # by the most. # Measured job totals on master 2026-11-04 also show the ceiling this leaves: - # Windows 10 is 29.5 min and compiles for 23.7 of it with no compiler cache at all, so - # Windows still sets the pull-request wall clock no matter how warm these legs are - # (see build-vcpkg). + # Windows 10 is 29.5 min and compiles for 23.7 of it, so Windows sets the + # pull-request wall clock no matter how warm these legs are. That is why the Windows + # leg is the one macOS's share went to (see build-vcpkg): MSVC has no -g level to + # shrink, and with /Z7 the whole configuration is cacheable, PCH included. # # Steady-state master data corrected this on 2026-11-07: Arch 90.1% and Debian 13 # 96.75% per-run hits at the 850M/800M bounds, Fedora 45 debug 99.18% and release @@ -676,6 +679,9 @@ jobs: qt_version: 6.11.* qt_modules: qtimageformats qtmultimedia qtshadertools qtwebsockets type: Release + ccache_eviction_age: 7d + ccache_size: 1300M # Release plus the package: 1.2 GB working set at /Z7 + use_ccache: 1 # Scope is named after the vs2026 toolchain, not the generator: the # ABI hash covers cl.exe, which is identical, so this leg keeps # restoring the warm archives. Renaming would orphan them. @@ -689,8 +695,13 @@ jobs: runs-on: ${{ matrix.runner }} timeout-minutes: 100 env: - # No leg in this job keeps a compiler cache: neither the macOS legs nor the Windows - # leg can fill one across runs. See the note on the removed macOS ccache steps below. + # Only the Windows leg keeps a compiler cache; the macOS legs build without one + # (see the note where their ccache steps used to be). ccache drives MSVC as long as + # the compiler emits /Z7, which CMakeLists.txt sets for exactly this reason, and it + # caches the PCH-backed targets with the sloppiness that .ci/compile.sh re-asserts. + CCACHE_DIR: ${{ github.workspace }}/.cache/ + CCACHE_SIZE: ${{ matrix.ccache_size || '700M' }} + PERSIST_CCACHE: ${{ matrix.persist_ccache || 'true' }} # MSVC has no -g level, so the Windows leg does not ask for one. COCKATRICE_DEBUG_INFO_LEVEL: ${{ matrix.os != 'Windows' && github.ref_type != 'tag' && '1' || '' }} @@ -707,6 +718,19 @@ jobs: with: msbuild-architecture: x64 + - name: "[Windows] Setup ccache" + if: matrix.os == 'Windows' && matrix.use_ccache == 1 + shell: bash + run: choco install ccache --no-progress -y + + - name: "[Windows] Restore compiler cache (ccache)" + if: matrix.os == 'Windows' && matrix.use_ccache == 1 && env.PERSIST_CCACHE == 'true' + id: restore_ccache + uses: actions/cache/restore@v6 + with: + key: ccache-${{ matrix.os }}${{ matrix.target }} + path: ${{ env.CCACHE_DIR }} + # No macOS leg runs ccache. A restored macOS cache served 42% of calls and saved # about 32s on a 20 min leg that is already shorter than the Windows leg, which did # not pay for its 800M slice of the quota; with nothing restored, the remaining @@ -820,6 +844,7 @@ jobs: shell: bash env: BUILDTYPE: '${{ matrix.type }}' + CCACHE_EVICTION_AGE: ${{ matrix.ccache_eviction_age }} CMAKE_GENERATOR: ${{ matrix.cmake_generator }} # Tag builds only: artifacts from master pushes and PR branches must not report to # Sentry. Forks get no secrets on pull_request, so they keep the mock fallback either way. @@ -834,6 +859,7 @@ jobs: MAKE_PACKAGE: '${{ matrix.make_package }}' PACKAGE_SUFFIX: '${{ matrix.package_suffix }}' TARGET_MACOS_VERSION: ${{ matrix.override_target }} + USE_CCACHE: ${{ matrix.use_ccache }} # Only pushes to the default branch may write the shared binary cache: the # archives a run compiles are only as trustworthy as the code they were # built from, and only merged code may populate the scope that master and @@ -1034,6 +1060,27 @@ jobs: path: ${{ runner.temp }}/vcpkg-new retention-days: 1 + # Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342 + - name: "[Windows] Delete remote compiler cache (ccache)" + if: matrix.os == 'Windows' && matrix.use_ccache == 1 && env.PERSIST_CCACHE == 'true' && github.ref == 'refs/heads/master' && steps.restore_ccache.outputs.cache-hit + continue-on-error: true + # Bash, not the runner's default pwsh, which cannot parse this `if`. + shell: bash + env: + CACHE_PRIMARY_KEY: ${{ steps.restore_ccache.outputs.cache-primary-key }} + GH_TOKEN: ${{ github.token }} + run: | + if gh cache delete --repo "$GITHUB_REPOSITORY" "$CACHE_PRIMARY_KEY"; then + echo "Cache deleted successfully" + fi + + - name: "[Windows] Cache updated compiler cache (ccache)" + if: matrix.os == 'Windows' && matrix.use_ccache == 1 && env.PERSIST_CCACHE == 'true' && github.ref == 'refs/heads/master' + uses: actions/cache/save@v6 + with: + key: ${{ steps.restore_ccache.outputs.cache-primary-key }} + path: ${{ env.CCACHE_DIR }} + - name: "[macOS] Sign app bundle" if: matrix.os == 'macOS' && matrix.make_package && needs.configure.outputs.tag != null id: sign_macos @@ -1222,7 +1269,7 @@ jobs: # The nightly legs and the pull-request legs that finish under the Windows leg # keep none, so their leftover entries are reclaimed here instead of waiting out # the age rule. - LIVE_CCACHE_KEYS: 'ccache-Arch ccache-Debian13 ccache-Fedora45debug ccache-Fedora45release' + LIVE_CCACHE_KEYS: 'ccache-Arch ccache-Debian13 ccache-Fedora45debug ccache-Fedora45release ccache-Windows10' run: | gh api --paginate "repos/$GITHUB_REPOSITORY/actions/caches?per_page=100" \ --jq '.actions_caches[] | [.id, .ref, .key, .last_accessed_at] | @tsv' >"$RUNNER_TEMP/caches.tsv" diff --git a/CMakeLists.txt b/CMakeLists.txt index b0f6a8db0..1edfef8e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,11 +48,12 @@ if(NOT CMAKE_BUILD_TYPE) ) endif() -# ccache does not support MSVC and must not auto-engage on Windows -# (it is installed unintentionally on the Windows CI runner). -# NOTE: this keys off the target OS, so a mingw/Ninja configuration on Windows -# also opts out of ccache even though the GNUCXX branch below supports it. -if(USE_CCACHE AND NOT WIN32) +# ccache drives MSVC too (since 4.7), but only when the compiler writes debug +# information into the object file: /Zi and /ZI keep it in a shared PDB, and ccache +# reports them as unsupported rather than risk a stale one. The MSVC flags below +# therefore use /Z7. MSVC precompiled headers are cached as well, with the same +# sloppiness the other toolchains need. +if(USE_CCACHE) find_program(CCACHE_PROGRAM ccache) if(CCACHE_PROGRAM) # Support Unix Makefiles and Ninja @@ -66,10 +67,10 @@ if(USE_CCACHE AND NOT WIN32) # check for every project on the machine, not just this one. execute_process(COMMAND ${CCACHE_PROGRAM} --set-config sloppiness=pch_defines,time_macros) message(STATUS "Found CCache ${CCACHE_PROGRAM}") + else() + # An explicit opt-in must not disappear silently. + message(STATUS "ccache requested but not found; building without a compiler cache") endif() -elseif(USE_CCACHE AND WIN32) - # An explicit opt-in must not disappear silently on Windows. - message(STATUS "ccache disabled: not supported for the MSVC toolchain on Windows") endif() if(WIN32 OR USE_VCPKG) @@ -174,8 +175,9 @@ if(MSVC) # Microsoft Visual C++ compiler # /utf-8 Set source file encoding and execution character set to UTF-8 # /W4 Enable warning level 4 # /Zc:__cplusplus Enable correct __cplusplus reporting - # /Zi Generate debugging information (Program Database, PDB) - set(CMAKE_CXX_FLAGS "/EHsc /MP /permissive- /utf-8 /W4 /Zc:__cplusplus /Zi") + # /Z7 Generate debugging information in each object file (ccache-cacheable) + # instead of a compiler-shared PDB, which ccache rejects + set(CMAKE_CXX_FLAGS "/EHsc /MP /permissive- /utf-8 /W4 /Zc:__cplusplus /Z7") # /O2 Balanced optimization # /MD Link against the multi-threaded DLL runtime library (Release CRT) set(CMAKE_CXX_FLAGS_RELEASE "/O2 /MD")