From ee1d3b004ebdb5dd3c4a8cc279dc49d00ce74834 Mon Sep 17 00:00:00 2001 From: Ivana Date: Wed, 3 Jun 2026 11:02:44 +0000 Subject: [PATCH] Exempt first cibuildwheel build from ccache-hit enforcement The first python-version wheel in a cibuildwheel run compiles the C++ sources from a cold cache, so zero ccache hits is expected there; only later versions reuse those objects and must hit the cache. Track the first build with a marker file in CCACHE_DIR (shared across the per-version build containers via the bind mount) and skip hit enforcement when it is absent. The release_pypi workflow removes the marker once per run, after the cache is restored and before cibuildwheel, so a marker carried over inside the cached CCACHE_DIR cannot mask a genuine failure. Co-authored-by: Claude --- .github/workflows/release_pypi.yml | 7 +++++++ tools/validate_ccache_stats.py | 21 ++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml index fdcb8749b..919269ab6 100644 --- a/.github/workflows/release_pypi.yml +++ b/.github/workflows/release_pypi.yml @@ -69,6 +69,13 @@ jobs: # below; the in-container CCACHE_DIR is set in the Build Wheels # step's env: block (and forwarded via environment-pass). echo "HOST_CCACHE_DIR=${HOME}/.ccache" >> "$GITHUB_ENV" + # Reset the "first wheel build" marker once per run (after the cache is + # restored, before cibuildwheel). tools/validate_ccache_stats.py allows + # zero cache hits only for the first python-version build of a run; this + # ensures a marker carried over inside the cached CCACHE_DIR can't mask a + # genuine cache-miss failure on later runs. + mkdir -p "${HOME}/.ccache" + rm -f "${HOME}/.ccache/.cytnx_first_wheel_build_done" - name: Build Wheels uses: pypa/cibuildwheel@v3.3.0 diff --git a/tools/validate_ccache_stats.py b/tools/validate_ccache_stats.py index 68a951577..84bc30a0c 100644 --- a/tools/validate_ccache_stats.py +++ b/tools/validate_ccache_stats.py @@ -82,5 +82,24 @@ def to_num(v: str) -> float: # Reset counters so the next Python-version build observes fresh, per-build stats. subprocess.check_call(['ccache', '--zero-stats']) +# cibuildwheel runs this script once per Python-version build. The very first +# build of a run compiles the C++ sources from a cold cache, so zero hits is +# expected; later versions reuse those object files and must hit the cache. +# A marker file inside CCACHE_DIR (shared across the per-version build +# containers via the bind mount) lets us tell the first build apart. The CI +# workflow removes this marker once per run before invoking cibuildwheel, so a +# marker carried over inside the cached CCACHE_DIR never masks a real failure. +ccache_dir = os.getenv('CCACHE_DIR') +first_build_marker = pathlib.Path(ccache_dir) / '.cytnx_first_wheel_build_done' if ccache_dir else None +is_first_build = first_build_marker is not None and not first_build_marker.exists() +if first_build_marker is not None: + first_build_marker.parent.mkdir(parents=True, exist_ok=True) + first_build_marker.touch() + if total <= 0: - raise SystemExit('No ccache hits detected for this python-version build.') + if is_first_build: + print('ccache_first_build_no_hits_ok') + print('No ccache hits detected, but this is the first python-version build ' + 'of the cibuildwheel run (cold cache); skipping hit enforcement.') + else: + raise SystemExit('No ccache hits detected for this python-version build.')