Skip to content

Instantly share code, notes, and snippets.

@aengelke
Created January 10, 2026 13:29
Show Gist options
  • Select an option

  • Save aengelke/82b9919222dbc2c43fcfc5bb79dceee6 to your computer and use it in GitHub Desktop.

Select an option

Save aengelke/82b9919222dbc2c43fcfc5bb79dceee6 to your computer and use it in GitHub Desktop.
#!/bin/sh
# SPDX-FileCopyrightText: 2025 Alexis Engelke
# SPDX-License-Identifier: Apache-2.0
USE_GCC=OFF
DISABLE_PCH=OFF
set -euo pipefail
cat >CMakeLists.txt <<EOF
cmake_minimum_required(VERSION 3.31)
project(test)
add_compile_definitions(M_CMD=\${M_CMD})
add_executable(test test.c)
target_precompile_headers(test PRIVATE test.h)
EOF
cat >test.c <<EOF
#include <stdio.h>
#include "test.h"
#if M_HDR == 2
#define M_SRC 20
#else
#define M_SRC 10
#endif
int main(void) {
printf("%d %d %d\n", M_HDR, M_SRC, hdrfn());
return 0;
}
EOF
cat >test.h <<EOF
#ifndef TEST_H
#define TEST_H
#define M_HDR 2
static inline int hdrfn() {
return M_CMD;
}
#endif
EOF
if [ $USE_GCC == "ON" ]; then
export CC=gcc
export CFLAGS="-fpch-preprocess"
else
export CC=clang
export CFLAGS="-Xclang -fno-pch-timestamp"
fi
rm -rf build cache
export CCACHE_DIR=$PWD/cache
cmake -G Ninja -S . -B build -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DM_CMD=5 -DCMAKE_DISABLE_PRECOMPILE_HEADERS=$DISABLE_PCH
build_test() { # build_test <expected output> <expected cache stats>
CCACHE_SLOPPINESS=pch_defines,time_macros CCACHE_DEBUG=1 ninja -v -C build
output=`./build/test`
ninja -C build -t clean
stats=`ccache --print-stats | awk '/^direct_cache_hit/{dch=$2}/^preprocessed_cache_hit/{pch=$2}/cache_miss/{cm=$2}END{print dch,pch,cm}'`
if [ $DISABLE_PCH == "OFF" ] && [ "x$stats" != "x$2" ]; then
echo "wrong stats: $stats != $2"
ccache -svv
fi
if [ "x$output" != "x$1" ]; then echo "wrong result: $output != $1"; exit 1; fi
ccache -z >/dev/null
}
# Test 1: initial build, no hits
build_test "2 20 5" "0 0 2"
# Test 2: change header, no hits
sed -i "s/M_HDR 2/M_HDR 1/" test.h
build_test "1 10 5" "0 0 2"
# Test 3: change header back, hit for pch and test.c
sed -i "s/M_HDR 1/M_HDR 2/" test.h
build_test "2 20 5" "2 0 0"
# Test 4: change header forth again, hit for pch and test.c
sed -i "s/M_HDR 2/M_HDR 1/" test.h
build_test "1 10 5" "2 0 0"
# Test 5: change cmdline macro definition, no hits
cmake -B build -DM_CMD=32
build_test "1 10 32" "0 0 2"
# Test 6: change cmdline macro definition back, hit for pch and test.c
cmake -B build -DM_CMD=5
build_test "1 10 5" "2 0 0"
# Test 7: change cmdline macro definition forth and change test.h at the same time, no hits
cmake -B build -DM_CMD=32
sed -i "s/M_HDR 1/M_HDR 2/" test.h
build_test "2 20 32" "0 0 2"
# Test 8: whitespace change to source file. PCH is hit test.c is direct miss but preprocessed hit
echo "/* new comment*/" >>test.c
build_test "2 20 32" "1 1 0"
# Test 9: whitespace change to header. PCH will be missed, hence source file will be recompiled
echo "/* new comment*/" >>test.h
build_test "2 20 32" "0 0 2"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment