Skip to content

Cache Windows compilations with sccache - #899

Draft
mjcarroll wants to merge 1 commit into
mjcarroll/windows-ninjafrom
mjcarroll/windows-compiler-cache-v2
Draft

Cache Windows compilations with sccache#899
mjcarroll wants to merge 1 commit into
mjcarroll/windows-ninjafrom
mjcarroll/windows-compiler-cache-v2

Conversation

@mjcarroll

@mjcarroll mjcarroll commented Sep 9, 2026

Copy link
Copy Markdown
Member

Second of two, stacked on #900. Review that first — this shows only the cache commit.

Linux has had ccache since #135 via the /usr/lib/ccache symlinks on PATH. Windows has no equivalent, so the cache is named to CMake through CMAKE_<LANG>_COMPILER_LAUNCHER, which works only under Ninja — hence the split.

The launchers are exported rather than passed as -D. CMake initialises CMAKE_<LANG>_COMPILER_LAUNCHER from the environment variable of the same name, so an exported value also reaches the nested configures ament_vendor and ExternalProject_Add run. ament_vendor forwards a fixed allow-list through a -C initial-cache file and the launchers are not on it, so with -D alone every vendored package would compile uncached — most of the expensive C++ in the workspace. RUSTC_WRAPPER covers cargo in zenoh_cpp_vendor; CARGO_INCREMENTAL and CMAKE_TRY_COMPILE_CONFIGURATION are set because sccache caches neither incremental compilation nor /Zi.

The cache lives in .sccache beside the workspace — inside the bind-mounted Jenkins workspace, of which run() removes only ws. So it outlives the container, and since each job has its own workspace no two share one: ccache locks its cache and is safe to share, sccache keeps its index in memory and two servers over one directory evict each other. This deliberately is not a mount in ci_job.xml.em, because a job's build steps live in Jenkins and a template change only lands when someone pushes the job config; CI_SCRIPTS_BRANCH swaps the repository, not the job.

A missing sccache is a warning and an uncached build, not a failure. Packaging gets no cache: Visual Studio generator, and RelWithDebInfo means /Zi.

Blocked on ros2/ros2#1854 for sccache, and on gazebosim/gz-cmake#586gz_math_vendor and gz_utils_vendor are the only packages emitting /Zi under Release, and under sccache they fail to compile with C1041 rather than merely missing. That needs a gz-cmake release and a gz_cmake_vendor bump, which is why this stays draft while #900 does not.

Each build prints sccache --show-stats before and after; watch Non-cacheable compilations and Cache errors as well as hit rate.

@mjcarroll
mjcarroll force-pushed the mjcarroll/windows-compiler-cache-v2 branch from 2b22f6f to c49f32d Compare September 9, 2026 21:45
@mjcarroll
mjcarroll changed the base branch from master to mjcarroll/windows-ninja September 9, 2026 21:46
The Linux jobs have had ccache since #135, by way of the /usr/lib/ccache
symlinks that masquerade as the compilers on PATH.  Windows had nothing:
there is no equivalent masquerade, so the cache has to be named to CMake
through CMAKE_<LANG>_COMPILER_LAUNCHER instead, which is why the Ninja
generator had to come first -- CMake accepts that setting under the
Visual Studio generator and then ignores it.

Export the launchers rather than passing them as -D on the colcon command
line.  CMake initialises CMAKE_<LANG>_COMPILER_LAUNCHER from the
environment variable of the same name, so an exported value also reaches
the nested configures that ament_vendor and ExternalProject_Add run.
ament_vendor forwards a fixed allow-list of variables to its sub-builds
through a -C initial-cache file, and the launchers are not on it, so with
-D alone every vendored package would keep compiling uncached -- which is
most of the expensive C++ in the workspace.

CMAKE_<LANG>_COMPILER_LAUNCHER reaches only the C and C++ compilers, so
export RUSTC_WRAPPER too: zenoh_cpp_vendor builds several hundred crates
through cargo.  CARGO_INCREMENTAL goes off alongside it because sccache
refuses to cache incremental compilation.  CMAKE_TRY_COMPILE_CONFIGURATION
goes to Release because try_compile() otherwise builds Debug, which on
MSVC is '/Zi /Od /RTC1', and sccache will not cache /Zi.

The cache directory is .sccache beside the workspace -- inside the
Jenkins workspace, which is bind mounted into the container from the
agent, and of which run() removes only the 'ws' subdirectory.  So it
outlives the container, and since every Jenkins job has a workspace of
its own, no two jobs share one: ccache locks its cache and is safe to
share, whereas each sccache server holds its index in memory, so two
servers over one directory evict each other's entries.

That is deliberately not a mount in ci_job.xml.em.  A Jenkins job's build
steps live in Jenkins, written there by create_jenkins_job.py, so a
template change reaches a running job only once somebody pushes the job
configuration; CI_SCRIPTS_BRANCH swaps the checked-out repository, not
the job.  An earlier attempt at the mount reported

  Using sccache with SCCACHE_DIR='<unset>' and SCCACHE_CACHE_SIZE='8G'

with no cache mount on the docker run line -- the cache was working and
then being thrown away with the container on every build.  Choosing the
directory here instead takes effect as soon as CI_SCRIPTS_BRANCH points
at this branch, and wiping the workspace is then a reasonable way to ask
for a cold build.

sccache is looked up rather than assumed, and a missing one is a warning
and an uncached build rather than a failed job.  That guard is in
_setup_compiler_cache() rather than in pre(), so that it cannot also skip
the MAX_PATH work, which Ninja needs whether or not there is a cache.

The packaging jobs get no cache.  They stay on the Visual Studio
generator, and they build RelWithDebInfo, which on MSVC means /Zi, which
sccache will not cache in any case.

sccache comes from the buildfarm environment in ros2/ros2#1854.
@mjcarroll
mjcarroll force-pushed the mjcarroll/windows-ninja branch from fb3ad92 to 70e12c9 Compare September 9, 2026 21:57
@mjcarroll
mjcarroll force-pushed the mjcarroll/windows-compiler-cache-v2 branch from c49f32d to a929858 Compare September 9, 2026 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant