Skip to content

Cache the cibuildwheel C extension build with ccache on Linux and macOS#233

Merged
bdraco merged 2 commits into
masterfrom
ci-compiler-cache
May 17, 2026
Merged

Cache the cibuildwheel C extension build with ccache on Linux and macOS#233
bdraco merged 2 commits into
masterfrom
ci-compiler-cache

Conversation

@bdraco

@bdraco bdraco commented May 17, 2026

Copy link
Copy Markdown
Member

What do these changes do?

Wrap the C compiler inside the cibuildwheel matrix with
ccache so that _helpers_c.c is not rebuilt from scratch
for every Python version on every CI run.
hendrikmuhs/ccache-action provisions the cache on the runner.

On Linux, cibuildwheel already bind-mounts the runner
filesystem at /host inside the manylinux/musllinux container,
so no custom container-engine override is needed; the
in-container ccache (installed alongside libffi-devel in
the existing before-all) is wired up by prepending the
ccache compiler symlink dirs to PATH and pointing
CCACHE_DIR at /host\${cache_dir} via
CIBW_ENVIRONMENT_LINUX. The cache dir itself is read from
ccache --get-config cache_dir because the action does not
export it to GITHUB_ENV.

macOS picks up the action's compiler symlinks directly through
PATH. QEMU jobs are skipped (host cache holds foreign-arch
objects).

Windows is intentionally left out for now: setuptools'
MSVCCompiler invokes cl.exe directly and ignores
CC/CXX, so an sccache integration there needs a
cl.exe shim earlier in PATH and is deferred to a separate
piece of work.

Expected effect on warm cache: the per-Python-version compile of
_helpers_c.c drops from roughly 25 to 40 seconds to a
sub-second hit, bringing the slowest Linux and macOS wheel jobs
down by roughly half.

Are there changes in behavior for the user?

No, contributor tooling only.

Related issue number

N/A

Checklist

  • I think the code is well written
  • Unit tests for the changes exist: N/A (CI-only change; ruff/ruff format/yamllint run via pre-commit, and make doc-spelling is clean against the new fragment)
  • Documentation reflects the changes: N/A (no user-visible API change)
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt: N/A (no such file in this repo)
  • Add a new news fragment into the CHANGES/ folder (CHANGES/233.contrib.rst)

Drafted with Claude Code (Opus 4.7); reviewed by @bdraco.

Agent run details (optional, for reviewers)

First push (commit 2ad0e88) crashed all four wheel jobs on
Linux with docker: invalid spec: :/host_ccache: empty section between colons. Root cause: hendrikmuhs/ccache-action does
not export CCACHE_DIR to GITHUB_ENV (it configures ccache
via ccache --set-config cache_dir=...), so the custom
CIBW_CONTAINER_ENGINE mount expanded to an empty source.
Windows and macOS were cancelled by fail-fast.

Follow-up commit (efe94c3) drops the custom container-engine
override, relies on cibuildwheel's default /:/host mount,
and reads the cache dir from ccache --get-config cache_dir,
referencing it at /host\${cache_dir} inside the container.
The "Wire ccache" step shrank by roughly half.

Local validation:

```
$ python3 -c "import yaml; yaml.safe_load(open('.github/workflows/reusable-build-wheel.yml'))"
$ python3 -c "import tomllib; tomllib.load(open('pyproject.toml','rb'))"
$ make doc-spelling
2 pre-existing misspellings only ("subdirectory" in CHANGES/207.contrib.rst, "postfix" in CHANGES.rst); CHANGES/233.contrib.rst is clean.
$ SKIP=actionlint-docker pre-commit run --all-files
all hooks pass (actionlint-docker skipped: Docker daemon not running locally; runs in CI).
```

Container behavior cannot be exercised locally; the live CI run
on this branch is the verification.

Wrap the C compiler inside the cibuildwheel matrix with ccache so
that _helpers_c.c is not rebuilt from scratch for every Python
version on every run. hendrikmuhs/ccache-action sets up the cache
on the runner; for Linux the manylinux/musllinux container picks
ccache up via the existing before-all install plus a bind-mounted
CCACHE_DIR and a PATH prepend in CIBW_ENVIRONMENT_LINUX; macOS
uses the action's compiler symlinks directly. QEMU jobs are
skipped because the host cache contains foreign-arch objects.
Windows is intentionally left out: setuptools' MSVCCompiler
invokes cl.exe without honoring CC/CXX, so sccache wiring there
needs a cl.exe shim and is deferred.
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided There is a change note present in this PR label May 17, 2026
The previous version assumed hendrikmuhs/ccache-action exports
CCACHE_DIR via GITHUB_ENV. It does not (the action configures
ccache itself via `ccache --set-config cache_dir=...`), so the
CIBW_CONTAINER_ENGINE override expanded to `--volume=:/host_ccache`
and docker rejected the empty source path.

Drop the bespoke container-engine override and rely on
cibuildwheel's default `/:/host` host mount; pull the cache dir
from `ccache --get-config cache_dir` and reference it inside the
container at `/host\${cache_dir}`.
@bdraco bdraco changed the title Cache the cibuildwheel C compile with ccache on Linux and macOS Cache the cibuildwheel C extension build with ccache on Linux and macOS May 17, 2026
@codspeed-hq

codspeed-hq Bot commented May 17, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 4 untouched benchmarks


Comparing ci-compiler-cache (efe94c3) with master (bdf25f6)

Open in CodSpeed

@webknjaz

Copy link
Copy Markdown
Member

This looks interesting. How reusable the patch is? I've been having plans of extracting reusable-cibuildwheel.yml externally at some point. We should keep that in mind when making changes to this workflow + consider copying them across all our repos in the same shape.

@bdraco

bdraco commented May 17, 2026

Copy link
Copy Markdown
Member Author

Probably pretty reusable. ccache is sad state on windows though. Will likely want it to smoke for a few weeks before making it reuable

@bdraco

bdraco commented May 17, 2026

Copy link
Copy Markdown
Member Author

seems to be working fine on my fork testing. going to merge it. not a big deal to revert

@bdraco bdraco marked this pull request as ready for review May 17, 2026 22:06
@bdraco bdraco merged commit eeedfdb into master May 17, 2026
59 of 60 checks passed
@bdraco bdraco deleted the ci-compiler-cache branch May 17, 2026 22:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants