Skip to content

Commit e9321d2

Browse files
Merge #7071: feat: integrate ctcache for clang-tidy result caching in CI
040ac27 ci: bump CTCACHE_COMMIT to e393144d5c49b060a1dbc7ae15b9c6973efb967d (UdjinM6) 7c310c7 refactor: derive ctcache Python script path from wrapper location (UdjinM6) 28577db refactor: improve ctcache configuration maintainability (UdjinM6) 7ef44b6 chore: adjust removal count message (UdjinM6) 64ff657 fix: improve file removal pipeline robustness (UdjinM6) 2944562 feat: verify clang and ctcache are installed correctly (UdjinM6) a1b98b1 chore: make paths more maintainable (UdjinM6) ead9136 feat: integrate ctcache for clang-tidy result caching in CI (UdjinM6) Pull request description: ## Issue being fixed or feature implemented Add [ctcache](https://github.com/matus-chochlik/ctcache) to cache clang-tidy analysis results, reducing time spent in "Run linters" step from 33-34 minutes to 3-4 minutes in the best case scenarios. ## What was done? - Download ctcache in `contrib/containers/ci/ci.Dockerfile` and patch it to work correctly - Add new steps in `.github/workflows/build-src.yml` to save/restore ctcache cache (for `linux64_multiprocess`, save on pushes to default branch only) - Adjust `ci/dash/lint-tidy.sh` to configure ctcache, force `run-clang-tidy` to use it - Add manual cache cleanup in `ci/dash/lint-tidy.sh` since ctcache doesn't have a way to set internal limits like ccache does ## How Has This Been Tested? Run, check "Run linters" step in `linux64_multiprocess-build` job. develop, no ctcache: [31m 43s](https://github.com/UdjinM6/dash/actions/runs/20273923736/job/58217581581) develop*, empty cache: 0% hits, [33m 19s](https://github.com/UdjinM6/dash/actions/runs/20287332888/job/58265404189) develop*, second run, using cache: 100% hits, [3m 27s](https://github.com/UdjinM6/dash/actions/runs/20287332888/job/58281396883) New branch*, merged #6947 (chainlock, evo): 96.5% hits, [5m 13s](https://github.com/UdjinM6/dash/actions/runs/20300087485/job/58303447008) New branch*, merged #7056 (chainlock, instantsend, llmq, etc): 59.3%, [19m 50s](https://github.com/UdjinM6/dash/actions/runs/20300940029/job/58306338715) New branch*, merged #7005 (wallet, utils): 40.9% hits, [25m 49s](https://github.com/UdjinM6/dash/actions/runs/20293363566/job/58282142424) _* with this patch merged into develop_ ## Breaking Changes n/a ## Checklist: - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: kwvg: utACK 040ac27 PastaPastaPasta: utACK 040ac27 Tree-SHA512: 3fe629b533773c6200caf0037c541180c91d7d9b9468eba410c0c51c17ae5df28d3d02380a4d4c14654eb73e20ea07d2c0748462874b11e33e1a7a1ae0dd22f4
2 parents a77ef57 + 040ac27 commit e9321d2

File tree

4 files changed

+101
-1
lines changed

4 files changed

+101
-1
lines changed

.github/workflows/build-src.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,36 @@ jobs:
113113
/cache/ccache
114114
key: ccache-${{ hashFiles('contrib/containers/ci/ci.Dockerfile', 'depends/packages/*') }}-${{ inputs.build-target }}-${{ github.sha }}
115115

116+
- name: Restore ctcache cache
117+
if: inputs.build-target == 'linux64_multiprocess'
118+
uses: actions/cache/restore@v4
119+
with:
120+
path: |
121+
/cache/ctcache
122+
key: ctcache-${{ hashFiles('contrib/containers/ci/ci.Dockerfile', 'depends/packages/*') }}-${{ inputs.build-target }}-${{ github.sha }}
123+
restore-keys: |
124+
ctcache-${{ hashFiles('contrib/containers/ci/ci.Dockerfile', 'depends/packages/*') }}-${{ inputs.build-target }}-
125+
116126
- name: Run linters
117127
if: inputs.build-target == 'linux64_multiprocess'
118128
run: |
129+
CACHE_DIR="/cache"
119130
export BUILD_TARGET="${{ inputs.build-target }}"
120131
source ./ci/dash/matrix.sh
121132
./ci/dash/lint-tidy.sh
122133
shell: bash
123134

135+
- name: Save ctcache cache
136+
if: |
137+
inputs.build-target == 'linux64_multiprocess' &&
138+
github.event_name == 'push' &&
139+
github.ref_name == github.event.repository.default_branch
140+
uses: actions/cache/save@v4
141+
with:
142+
path: |
143+
/cache/ctcache
144+
key: ctcache-${{ hashFiles('contrib/containers/ci/ci.Dockerfile', 'depends/packages/*') }}-${{ inputs.build-target }}-${{ github.sha }}
145+
124146
- name: Run unit tests
125147
run: |
126148
BUILD_TARGET="${{ inputs.build-target }}"

ci/dash/lint-tidy.sh

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,76 @@ set -eo pipefail
1111
# only on nor do they set the requisite build parameters. Make sure you do
1212
# that *before* running this script.
1313

14+
# Cleanup old ctcache entries to keep cache size under limit
15+
cleanup_ctcache() {
16+
local cache_dir=$1
17+
local max_size_mb=$2
18+
local cache_size_mb
19+
20+
cache_size_mb=$(du -sm "${cache_dir}" 2>/dev/null | cut -f1)
21+
if [ "${cache_size_mb}" -gt "${max_size_mb}" ]; then
22+
echo "Cache size ${cache_size_mb}MB exceeds limit ${max_size_mb}MB, cleaning old entries..."
23+
# Remove files older than 7 days, or if still too large, oldest 20% of files
24+
find "${cache_dir}" -type f -mtime +7 -delete 2>/dev/null || true
25+
cache_size_mb=$(du -sm "${cache_dir}" 2>/dev/null | cut -f1)
26+
if [ "${cache_size_mb}" -gt "${max_size_mb}" ]; then
27+
local file_count remove_count
28+
file_count=$(find "${cache_dir}" -type f | wc -l)
29+
remove_count=$((file_count / 5))
30+
find "${cache_dir}" -type f -printf '%T+ %p\0' | sort -z | head -z -n "${remove_count}" | cut -z -d' ' -f2- | xargs -0 rm -f 2>/dev/null || true
31+
echo "Attempted to remove ${remove_count} oldest cache entries"
32+
fi
33+
echo "Cache size after cleanup: $(du -sh "${cache_dir}" 2>/dev/null | cut -f1)"
34+
fi
35+
}
36+
37+
# Verify clang-tidy is available
38+
CLANG_TIDY_BIN=$(command -v clang-tidy)
39+
if [ -z "${CLANG_TIDY_BIN}" ]; then
40+
echo "Error: clang-tidy not found in PATH"
41+
exit 1
42+
fi
43+
44+
# Setup ctcache for clang-tidy result caching
45+
export CTCACHE_SAVE_OUTPUT=1
46+
export CTCACHE_CLANG_TIDY="${CLANG_TIDY_BIN}"
47+
mkdir -p "${CTCACHE_DIR}"
48+
49+
CLANG_TIDY_CACHE=$(command -v clang-tidy-cache)
50+
51+
# Verify ctcache is installed
52+
if [ -z "${CLANG_TIDY_CACHE}" ]; then
53+
echo "Error: clang-tidy-cache not found in PATH"
54+
exit 1
55+
fi
56+
57+
# Derive Python script path from wrapper location (matches wrapper's own logic)
58+
CLANG_TIDY_CACHE_PY="$(dirname "${CLANG_TIDY_CACHE}")/src/ctcache/clang_tidy_cache.py"
59+
if [ ! -f "${CLANG_TIDY_CACHE_PY}" ]; then
60+
echo "Error: ctcache Python script not found at ${CLANG_TIDY_CACHE_PY}"
61+
exit 1
62+
fi
63+
64+
# Zero stats before run to get accurate statistics for this run only
65+
python3 "${CLANG_TIDY_CACHE_PY}" --zero-stats 2>&1 || true
66+
1467
cd "${BASE_ROOT_DIR}/build-ci/dashcore-${BUILD_TARGET}/src"
15-
if ! ( run-clang-tidy -quiet "${MAKEJOBS}" | tee tmp.tidy-out.txt ); then
68+
69+
if ! ( run-clang-tidy -clang-tidy-binary="${CLANG_TIDY_CACHE}" -quiet "${MAKEJOBS}" | tee tmp.tidy-out.txt ); then
1670
grep -C5 "error: " tmp.tidy-out.txt
1771
echo "^^^ ⚠️ Failure generated from clang-tidy"
1872
false
1973
fi
2074

75+
# Show ctcache statistics and manage cache size
76+
echo "=== ctcache statistics ==="
77+
du -sh "${CTCACHE_DIR}" 2>/dev/null || echo "Cache directory not found"
78+
python3 "${CLANG_TIDY_CACHE_PY}" --show-stats 2>&1 || true
79+
80+
# Limit cache size (ctcache has no built-in size management)
81+
cleanup_ctcache "${CTCACHE_DIR}" "${CTCACHE_MAXSIZE_MB}"
82+
echo "=========================="
83+
2184
cd "${BASE_ROOT_DIR}/build-ci/dashcore-${BUILD_TARGET}"
2285
iwyu_tool.py \
2386
"src/compat" \

ci/test/00_setup_env.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ export CCACHE_COMPRESS=${CCACHE_COMPRESS:-1}
6161
# The cache dir.
6262
# This folder exists on the ci host and ci guest. Changes are propagated back and forth.
6363
export CCACHE_DIR=${CCACHE_DIR:-$CACHE_DIR/ccache}
64+
# ctcache (clang-tidy cache) configuration
65+
export CTCACHE_DIR=${CTCACHE_DIR:-$CACHE_DIR/ctcache}
66+
export CTCACHE_MAXSIZE_MB=${CTCACHE_MAXSIZE_MB:-50}
6467
# The depends dir.
6568
# This folder exists on the ci host and ci guest. Changes are propagated back and forth.
6669
export DEPENDS_DIR=${DEPENDS_DIR:-$BASE_ROOT_DIR/depends}

contrib/containers/ci/ci.Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,20 @@ RUN set -ex; \
6565
make install -j "$(( $(nproc) - 1 ))"; \
6666
cd /opt && rm -rf /opt/iwyu;
6767

68+
# Install ctcache for clang-tidy result caching
69+
# Pin to specific commit to ensure patch applies correctly
70+
ARG CTCACHE_COMMIT=e393144d5c49b060a1dbc7ae15b9c6973efb967d
71+
RUN set -ex; \
72+
mkdir -p /usr/local/bin/src/ctcache; \
73+
curl -fsSL "https://raw.githubusercontent.com/matus-chochlik/ctcache/${CTCACHE_COMMIT}/src/ctcache/clang_tidy_cache.py" \
74+
-o /usr/local/bin/src/ctcache/clang_tidy_cache.py; \
75+
curl -fsSL "https://raw.githubusercontent.com/matus-chochlik/ctcache/${CTCACHE_COMMIT}/clang-tidy" \
76+
-o /usr/local/bin/clang-tidy-cache; \
77+
chmod +x /usr/local/bin/clang-tidy-cache;
78+
6879
RUN \
6980
mkdir -p /cache/ccache && \
81+
mkdir /cache/ctcache && \
7082
mkdir /cache/depends && \
7183
mkdir /cache/sdk-sources && \
7284
chown ${USER_ID}:${GROUP_ID} /cache && \

0 commit comments

Comments
 (0)