[CI] Cache the Windows compile results with sccache

A pull request that touches one file rebuilds it, but sccache can hand
back the objects for everything else, and the GitHub Actions cache keeps
them between runs, so the second build of a branch costs about what the
first one stored and nothing more.

sccache has to wrap the compiler, and cmake only wraps it for the
generators that run the compiler themselves: the Visual Studio
generator drives MSBuild, which would drop a launcher on the floor
without a word. The cache therefore goes on the Ninja job, and
compile.sh now refuses --sccache on any other generator instead of
pretending to cache, and prints the sccache statistics next to the
existing ccache ones so the hit rate is visible in the log.

For MSVC, a cacheable object has to carry its debug information
inside, which is /Z7 rather than /Zi; sccache cannot restore the
latter, it can only restore a whole object file. Ask CMake for the
format by name through CMP0141 and CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
instead of hardcoding /Zi in the flags, where it would end up next to
/Z7, and keep the format as it was for every build that does not
cache. The linker still gets /DEBUG, so the release PDBs are written
either way, which is what that flag is there for.

The policy is set before project(), since that is when it takes
effect, and behind an if(POLICY) guard because it does not exist
before CMake 3.25 - where the old defaults remain the only option.
This commit is contained in:
Lukas Brübach 2026-10-02 14:12:39 +02:00 • committed by BruebachL
parent 24bb1de9b0
commit 8c38885b44
3 changed files with 64 additions and 5 deletions

View file

@ -14,12 +14,13 @@
# --debug or --release sets the build type ie CMAKE_BUILD_TYPE
# --ccache [<size>] uses ccache and shows stats, optionally provide size
# --evict-ccache <age> runs ccache eviction based on given age after build
# --sccache uses sccache instead, needs a generator that runs the compiler itself
# --dir <dir> sets the name of the build dir, default is "build"
# --cmake-generator <generator> sets CMAKE_GENERATOR as used by cmake
# --target-macos-version <version> sets the min os version - only used for macOS builds
# --profile writes an MSBuild performance summary and binary log, Windows only
# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_NO_CLIENT MAKE_TEST USE_CCACHE CCACHE_SIZE CCACHE_EVICTION_AGE BUILD_DIR CMAKE_GENERATOR TARGET_MACOS_VERSION MAKE_SANITIZE MAKE_COVERAGE VCPKG_TARGET_MACOS_VERSION BUILD_PARALLEL_LEVEL BUILD_PROFILE
# (correspond to args: --debug/--release --install --package <package type> --suffix <suffix> --server --test --sanitize --coverage --ccache <ccache_size> --dir <dir> --profile)
# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_NO_CLIENT MAKE_TEST USE_CCACHE CCACHE_SIZE CCACHE_EVICTION_AGE BUILD_DIR CMAKE_GENERATOR TARGET_MACOS_VERSION MAKE_SANITIZE MAKE_COVERAGE VCPKG_TARGET_MACOS_VERSION BUILD_PARALLEL_LEVEL BUILD_PROFILE USE_SCCACHE
# (correspond to args: --debug/--release --install --package <package type> --suffix <suffix> --server --test --sanitize --coverage --ccache <ccache_size> --dir <dir> --profile --sccache)
# exitcode: 1 for failure, 3 for invalid arguments
# Read arguments
@ -94,6 +95,10 @@ while [[ $# != 0 ]]; do
CCACHE_EVICTION_AGE=$1
shift
;;
'--sccache')
USE_SCCACHE=1
shift
;;
'--vcpkg')
USE_VCPKG=1
shift
@ -169,6 +174,16 @@ export CMAKE_POLICY_VERSION_MINIMUM=3.10
# Add cmake flags
flags=("-DCMAKE_BUILD_TYPE=$BUILDTYPE")
if [[ $USE_CCACHE && $USE_SCCACHE ]]; then
echo "::error file=$0::ccache and sccache at the same time is not a supported combination"
exit 1
fi
if [[ $USE_SCCACHE && $CMAKE_GENERATOR != "Ninja"* ]]; then
# A compiler cache wraps the compiler, and only generators that run the compiler
# themselves are given a launcher to wrap: cmake does not pass one to MSBuild.
echo "::error file=$0::sccache needs a Ninja generator, not '$CMAKE_GENERATOR', which would ignore it"
exit 1
fi
if [[ $MAKE_SERVER ]]; then
flags+=("-DWITH_SERVER=1")
fi
@ -199,6 +214,10 @@ if [[ $USE_CCACHE ]]; then
ccache --max-size "$CCACHE_SIZE"
fi
fi
if [[ $USE_SCCACHE ]]; then
flags+=("-DUSE_SCCACHE=1")
flags+=("-DCMAKE_C_COMPILER_LAUNCHER=sccache" "-DCMAKE_CXX_COMPILER_LAUNCHER=sccache")
fi
if [[ $PACKAGE_TYPE ]]; then
flags+=("-DCPACK_GENERATOR=$PACKAGE_TYPE")
fi
@ -384,6 +403,10 @@ if [[ $USE_CCACHE ]]; then
echo "::group::Show ccache stats again"
ccachestatsverbose
echo "::endgroup::"
elif [[ $USE_SCCACHE ]]; then
echo "::group::Show sccache stats"
sccache --show-stats
echo "::endgroup::"
elif [[ $CCACHE_EVICTION_AGE ]]; then
echo "::error file=$0::ccache eviction is enabled while ccache is disabled!"
fi

View file

@ -415,6 +415,7 @@ jobs:
qt_version: 6.11.*
qt_modules: qtimageformats qtmultimedia qtshadertools qtwebsockets
type: Release
use_sccache: 1
variant: ' (Ninja)'
# The same scope as the Visual Studio leg, so both halves of the A/B
# start from the same vcpkg cache and the comparison is about the
@ -521,6 +522,20 @@ jobs:
with:
arch: x64
# sccache is only useful for the Ninja job: a compiler cache has to wrap the
# compiler, and MSBuild is driven by cmake instead of running it, so a
# launcher handed to MSBuild is ignored without a word.
- name: "[Windows/Ninja] Set up sccache"
if: matrix.os == 'Windows' && matrix.cmake_generator == 'Ninja'
uses: mozilla-actions/sccache-action@v0.0.11
env:
SCCACHE_GHA_ENABLED: "true"
# A fork pull request can read the cache, but the token it runs with
# cannot write to the base branch's scope.
SCCACHE_GHA_RW_MODE: ${{ github.event.pull_request.head.repo.fork && 'READ_ONLY' || 'READ_WRITE' }}
# A cache that cannot be reached must not fail the build.
SCCACHE_IGNORE_SERVER_IO_ERROR: "1"
- name: "[Windows] Install NSIS"
if: matrix.os == 'Windows'
shell: bash
@ -569,8 +584,9 @@ jobs:
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
MAKE_PACKAGE: '${{ matrix.make_package }}'
PACKAGE_SUFFIX: '${{ matrix.package_suffix }}'
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
TARGET_MACOS_VERSION: '${{ matrix.override_target }}'
USE_CCACHE: ${{ matrix.use_ccache }}
USE_SCCACHE: ${{ matrix.use_sccache }}
# 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

View file

@ -8,8 +8,19 @@
# 3.16 required for Qt6 and target_precompile_headers()
cmake_minimum_required(VERSION 3.16)
# Let CMake pick the MSVC debug information format through
# CMAKE_MSVC_DEBUG_INFORMATION_FORMAT instead of writing it into the
# CMAKE_<LANG>_FLAGS_<CONFIG> defaults. The policy has to be settled before the
# first project() enables a language, and it does not exist before CMake 3.25,
# where the old defaults are the only thing available.
if(POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
endif()
# Use compiler cache (ccache)
option(USE_CCACHE "Cache the build results with ccache" ON)
# Use compiler cache (sccache), for generators that invoke the compiler themselves
option(USE_SCCACHE "Cache the build results with sccache" OFF)
# Treat warnings as errors (Debug builds only)
option(WARNING_AS_ERROR "Treat warnings as errors in debug builds" ON)
# Check for translation updates
@ -156,8 +167,17 @@ endif()
# Define proper compilation flags
if(MSVC)
# Disable Warning C4251, C++20 compatibility, Multi-threaded Builds, Warn Detection, Unwind Semantics, Debug Symbols
set(CMAKE_CXX_FLAGS "/wd4251 /Zc:__cplusplus /std:c++20 /permissive- /W4 /MP /EHsc /Zi")
# Debug information: a cache can only store and restore objects that carry it
# inside, so a cached build asks for /Z7 (Embedded); otherwise /Zi (ProgramDatabase).
# Either way the linker is told to write the PDB, in every configuration.
if(USE_SCCACHE)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded)
else()
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT ProgramDatabase)
endif()
# Disable Warning C4251, C++20 compatibility, Multi-threaded Builds, Warn Detection, Unwind Semantics
set(CMAKE_CXX_FLAGS "/wd4251 /Zc:__cplusplus /std:c++20 /permissive- /W4 /MP /EHsc")
# Visual Studio: Maximum Optimization, Multi-threaded DLL
set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD")
# Visual Studio: No Optimization, Multi-threaded Debug DLL