From cc88eb37713a9325f92b1fe8cb35ee6c6dcb141b Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Thu, 23 Jul 2026 15:55:17 -0700 Subject: [PATCH 1/2] CI runtime: add ccache --- .github/actions/ccache-setup/action.yml | 140 ++++++++++++++++++ .github/workflows/build-and-bench.yml | 10 ++ .../workflows/build-and-test-clientonly.yml | 11 ++ .github/workflows/build-and-test-refactor.yml | 13 ++ .github/workflows/build-and-test-stress.yml | 10 ++ .../workflows/build-and-test-whnvmtool.yml | 10 ++ .../workflows/build-and-test-wolfssl-lib.yml | 10 ++ .github/workflows/build-and-test.yml | 13 ++ 8 files changed, 217 insertions(+) create mode 100644 .github/actions/ccache-setup/action.yml diff --git a/.github/actions/ccache-setup/action.yml b/.github/actions/ccache-setup/action.yml new file mode 100644 index 000000000..04032d468 --- /dev/null +++ b/.github/actions/ccache-setup/action.yml @@ -0,0 +1,140 @@ +name: 'Set up ccache' +description: > + Install ccache (Ubuntu via apt, macOS via brew), restore the ccache + directory from a previous run, and prepend the ccache compiler-symlink + dir to PATH. Subsequent cc/gcc/clang invocations are transparently + intercepted by ccache, so no build step needs to change. On scheduled + (cron) runs the cache is reseeded from clean compiles (CCACHE_RECACHE) + instead of only being updated incrementally, so it cannot drift + indefinitely. + +inputs: + workflow-id: + description: 'Cache namespace - typically the calling workflow name.' + required: true + config-hash: + description: > + Optional short string distinguishing matrix entries. Each unique + value gets its own primary cache key. Leave empty to share one + cache across all entries in the workflow. + required: false + default: 'shared' + max-size: + description: 'Per-job ccache max size (passed to ccache -M).' + required: false + default: '2G' + read-only: + description: > + When 'true', restore the cache but do NOT save it (no post-job + upload). Callers should set this to the result of the expression + github.event_name == 'pull_request' so PR runs consume the shared + cache read-only - no per-PR entries, no churn - while + scheduled/push runs (read-only false) refresh it. + required: false + default: 'false' + +runs: + using: 'composite' + steps: + - name: Install ccache + shell: bash + run: | + if command -v ccache >/dev/null 2>&1; then + echo "ccache already installed: $(ccache --version | head -1)" + elif [ "${{ runner.os }}" = "Linux" ]; then + sudo apt-get update -q + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ + --no-install-recommends ccache + elif [ "${{ runner.os }}" = "macOS" ]; then + brew install ccache + else + echo "::error::ccache install not supported on ${{ runner.os }}" + exit 1 + fi + + # Read the wolfSSL commit SHA for the cache key to be used later + - name: Resolve cache key parts + id: ids + shell: bash + run: | + if [ -d wolfssl/.git ]; then + echo "wolfssl=$(git -C wolfssl rev-parse --short HEAD)" \ + >> "$GITHUB_OUTPUT" + else + echo "wolfssl=none" >> "$GITHUB_OUTPUT" + fi + + - name: Restore + save ccache + if: inputs.read-only != 'true' + uses: actions/cache@v5 + with: + path: ~/.ccache + # GH caches are immutable, so give it a unique name. + key: ccache-${{ inputs.workflow-id }}-${{ runner.os }}-${{ runner.arch }}-${{ inputs.config-hash }}-wolfssl-${{ steps.ids.outputs.wolfssl }}-${{ github.run_id }}-${{ github.run_attempt }} + # However, when restoring, use the most recent applicable entry. + restore-keys: | + ccache-${{ inputs.workflow-id }}-${{ runner.os }}-${{ runner.arch }}-${{ inputs.config-hash }}-wolfssl-${{ steps.ids.outputs.wolfssl }}- + ccache-${{ inputs.workflow-id }}-${{ runner.os }}-${{ runner.arch }}-${{ inputs.config-hash }}- + ccache-${{ inputs.workflow-id }}-${{ runner.os }}-${{ runner.arch }}- + + - name: Restore ccache (read-only, not saved) + if: inputs.read-only == 'true' + uses: actions/cache/restore@v5 + with: + path: ~/.ccache + key: ccache-${{ inputs.workflow-id }}-${{ runner.os }}-${{ runner.arch }}-${{ inputs.config-hash }}-wolfssl-${{ steps.ids.outputs.wolfssl }}-${{ github.run_id }}-${{ github.run_attempt }} + restore-keys: | + ccache-${{ inputs.workflow-id }}-${{ runner.os }}-${{ runner.arch }}-${{ inputs.config-hash }}-wolfssl-${{ steps.ids.outputs.wolfssl }}- + ccache-${{ inputs.workflow-id }}-${{ runner.os }}-${{ runner.arch }}-${{ inputs.config-hash }}- + ccache-${{ inputs.workflow-id }}-${{ runner.os }}-${{ runner.arch }}- + + - name: Configure ccache and PATH + shell: bash + run: | + # Set CCACHE_DIR for the commands below too, not only via + # GITHUB_ENV (which applies to later steps): without it the + # settings land in a different default dir on runs where + # ~/.ccache does not exist yet, and seed runs then compile + # with mismatched hash settings that later runs cannot reuse. + export CCACHE_DIR="$HOME/.ccache" + mkdir -p "$CCACHE_DIR" + ccache -M "${{ inputs.max-size }}" + # base_dir lets ccache reuse hits across different workspace + # checkout paths (runs can use different work dirs). + ccache --set-config=base_dir="$GITHUB_WORKSPACE" + ccache --set-config=hash_dir=false + # Tie compiler identity to the runner image version: exact + # invalidation when the image (and its compilers) updates, at + # zero per-compile cost. compiler_check=content would hash the + # compiler binary on every invocation, which is minutes per + # job on macOS clang. + ccache --set-config=compiler_check="string:${ImageVersion:-unknown}" + ccache -z + # The ccache compiler-symlink dir (cc, gcc, clang, ... all + # resolving to ccache) differs by platform. Prepending it to + # PATH makes the build transparently use ccache without + # changing any make invocation. On macOS the symlinks live + # under the Homebrew libexec dir, whose prefix differs between + # arm64 and Intel - resolve it via `brew --prefix`. + if [ "${{ runner.os }}" = "macOS" ]; then + CCACHE_LIBEXEC="$(brew --prefix)/opt/ccache/libexec" + else + CCACHE_LIBEXEC="/usr/lib/ccache" + fi + echo "$CCACHE_LIBEXEC" >> "$GITHUB_PATH" + echo "CCACHE_DIR=$HOME/.ccache" >> "$GITHUB_ENV" + + # On the scheduled (cron) refresh, force every compile to miss the + # cache and re-store a fresh result (CCACHE_RECACHE still writes, + # it just skips lookups). This reseeds the shared cache from clean + # compiles instead of only layering deltas onto whatever + # accumulated, so a bad or stale entry cannot live forever. PR and + # push runs are unaffected - they keep their warm hits. + - name: Force fresh compiles on scheduled reseed + if: github.event_name == 'schedule' + shell: bash + run: echo "CCACHE_RECACHE=1" >> "$GITHUB_ENV" + + - name: Show ccache stats (initial) + shell: bash + run: ccache -s diff --git a/.github/workflows/build-and-bench.yml b/.github/workflows/build-and-bench.yml index af8e830fb..81efd23e1 100644 --- a/.github/workflows/build-and-bench.yml +++ b/.github/workflows/build-and-bench.yml @@ -3,6 +3,9 @@ name: Benchmark on: push: branches: [ 'master', 'main', 'release/**' ] + schedule: + # Weekly ccache reseed; also keeps caches from 7-day eviction + - cron: '0 6 * * 1' pull_request: branches: [ '*' ] @@ -32,6 +35,13 @@ jobs: repository: wolfssl/wolfssl path: wolfssl + # Compiler cache; PR runs restore without saving + - name: Set up ccache + uses: ./.github/actions/ccache-setup + with: + workflow-id: build-and-bench + read-only: ${{ github.event_name == 'pull_request' }} + # Benchmark with everything enabled - name: Benchmark All run: cd benchmark && make clean && make WOLFSSL_DIR=../wolfssl DMA=1 && make run diff --git a/.github/workflows/build-and-test-clientonly.yml b/.github/workflows/build-and-test-clientonly.yml index 1c7e92308..6a14b8c4d 100644 --- a/.github/workflows/build-and-test-clientonly.yml +++ b/.github/workflows/build-and-test-clientonly.yml @@ -3,6 +3,9 @@ name: Build and Test Client-Only on: push: branches: [ 'master', 'main', 'release/**' ] + schedule: + # Weekly ccache reseed; also keeps caches from 7-day eviction + - cron: '0 6 * * 1' pull_request: branches: [ '*' ] @@ -34,6 +37,14 @@ jobs: repository: wolfssl/wolfssl path: wolfssl + # Compiler cache; PR runs restore without saving + - name: Set up ccache + uses: ./.github/actions/ccache-setup + with: + workflow-id: build-and-test-clientonly + config-hash: ${{ matrix.transport }} + read-only: ${{ github.event_name == 'pull_request' }} + # Build example server - name: Build POSIX server run: | diff --git a/.github/workflows/build-and-test-refactor.yml b/.github/workflows/build-and-test-refactor.yml index 15253f618..6c68a009d 100644 --- a/.github/workflows/build-and-test-refactor.yml +++ b/.github/workflows/build-and-test-refactor.yml @@ -3,6 +3,9 @@ name: Build and Test Refactor on: push: branches: [ 'master', 'main', 'release/**' ] + schedule: + # Weekly ccache reseed; also keeps caches from 7-day eviction + - cron: '0 6 * * 1' pull_request: branches: [ '*' ] @@ -37,6 +40,13 @@ jobs: repository: wolfssl/wolfssl path: wolfssl + # Compiler cache; PR runs restore without saving + - name: Set up ccache + uses: ./.github/actions/ccache-setup + with: + workflow-id: build-and-test-refactor + read-only: ${{ github.event_name == 'pull_request' }} + # Build and test standard build - name: Build and test refactor run: cd test-refactor/posix && make clean && make -j WOLFSSL_DIR=../../wolfssl && make run @@ -117,3 +127,6 @@ jobs: # Build and test with AUTH=1 and NOCRYPTO=1 (auth on, crypto off) - name: Build and test refactor with AUTH NOCRYPTO run: cd test-refactor/posix && make clean && make -j AUTH=1 NOCRYPTO=1 WOLFSSL_DIR=../../wolfssl && make run + + - name: Show ccache stats + run: ccache -s diff --git a/.github/workflows/build-and-test-stress.yml b/.github/workflows/build-and-test-stress.yml index b14d8ede4..7e7902099 100644 --- a/.github/workflows/build-and-test-stress.yml +++ b/.github/workflows/build-and-test-stress.yml @@ -3,6 +3,9 @@ name: Multi-threaded Stress Test on: push: branches: [ 'master', 'main', 'release/**' ] + schedule: + # Weekly ccache reseed; also keeps caches from 7-day eviction + - cron: '0 6 * * 1' pull_request: branches: [ '*' ] @@ -33,6 +36,13 @@ jobs: repository: wolfssl/wolfssl path: wolfssl + # Compiler cache; PR runs restore without saving + - name: Set up ccache + uses: ./.github/actions/ccache-setup + with: + workflow-id: build-and-test-stress + read-only: ${{ github.event_name == 'pull_request' }} + # Build and run stress tests with TSAN - name: Build and run stress tests with TSAN run: cd test && make clean && make -j THREADSAFE=1 TSAN=1 STRESS=1 DMA=1 SHE=1 WOLFSSL_DIR=../wolfssl && make TSAN=1 run diff --git a/.github/workflows/build-and-test-whnvmtool.yml b/.github/workflows/build-and-test-whnvmtool.yml index 8588cea95..3ecf22424 100644 --- a/.github/workflows/build-and-test-whnvmtool.yml +++ b/.github/workflows/build-and-test-whnvmtool.yml @@ -3,6 +3,9 @@ name: whnvmtool build and test on: push: branches: [ 'master', 'main', 'release/**' ] + schedule: + # Weekly ccache reseed; also keeps caches from 7-day eviction + - cron: '0 6 * * 1' pull_request: branches: [ '*' ] @@ -32,6 +35,13 @@ jobs: repository: wolfssl/wolfssl path: wolfssl + # Compiler cache; PR runs restore without saving + - name: Set up ccache + uses: ./.github/actions/ccache-setup + with: + workflow-id: whnvmtool + read-only: ${{ github.event_name == 'pull_request' }} + # Build and test standard build of whnvmtool - name: Build and test NVM tool run: cd tools/whnvmtool && make clean && make check WOLFSSL_DIR=../../wolfssl diff --git a/.github/workflows/build-and-test-wolfssl-lib.yml b/.github/workflows/build-and-test-wolfssl-lib.yml index 07b849494..56c587bf1 100644 --- a/.github/workflows/build-and-test-wolfssl-lib.yml +++ b/.github/workflows/build-and-test-wolfssl-lib.yml @@ -6,6 +6,9 @@ name: Build with installed wolfSSL (WOLFSSL_LIB) on: push: branches: [ 'master', 'main', 'release/**' ] + schedule: + # Weekly ccache reseed; also keeps caches from 7-day eviction + - cron: '0 6 * * 1' pull_request: branches: [ '*' ] @@ -25,6 +28,13 @@ jobs: repository: wolfssl/wolfssl path: wolfssl + # Compiler cache; PR runs restore without saving + - name: Set up ccache + uses: ./.github/actions/ccache-setup + with: + workflow-id: build-and-test-wolfssl-lib + read-only: ${{ github.event_name == 'pull_request' }} + - name: Build and install wolfssl # HAVE_ANONYMOUS_INLINE_AGGREGATES toggles the layout of wc_CryptoInfo # (anonymous union vs named .u member). wolfHSM requires the anonymous diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index d1af2729b..e0112e367 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -3,6 +3,9 @@ name: Build and Test on: push: branches: [ 'master', 'main', 'release/**' ] + schedule: + # Weekly ccache reseed; also keeps caches from 7-day eviction + - cron: '0 6 * * 1' pull_request: branches: [ '*' ] @@ -37,6 +40,13 @@ jobs: repository: wolfssl/wolfssl path: wolfssl + # Compiler cache; PR runs restore without saving + - name: Set up ccache + uses: ./.github/actions/ccache-setup + with: + workflow-id: build-and-test + read-only: ${{ github.event_name == 'pull_request' }} + # Build and test standard build - name: Build and test run: cd test && make clean && make -j WOLFSSL_DIR=../wolfssl && make run @@ -125,3 +135,6 @@ jobs: # Build and test the global (cross-client) trusted certificate verify cache - name: Build and test with CERT_VERIFY_CACHE_GLOBAL ASAN run: cd test && make clean && make -j CERT_VERIFY_CACHE_GLOBAL=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run + + - name: Show ccache stats + run: ccache -s From 36544236eb9468939082d29c672c19218760ee81 Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Fri, 24 Jul 2026 06:46:31 -0700 Subject: [PATCH 2/2] Temporary commit to test ccache --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e0112e367..98b710636 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -45,7 +45,7 @@ jobs: uses: ./.github/actions/ccache-setup with: workflow-id: build-and-test - read-only: ${{ github.event_name == 'pull_request' }} + read-only: false # Build and test standard build - name: Build and test