From 3393e7c79f44634aa81ba0c0a97cf5cc65579df8 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Tue, 24 Mar 2026 23:04:38 -0400 Subject: [PATCH 1/7] [vibe] going back to lcov, because codecov example uses it --- .codecov.yml | 6 ------ .github/workflows/ci.yml | 25 ++++++++----------------- Makefile | 6 ++++-- dependencies.sh | 6 +++--- 4 files changed, 15 insertions(+), 28 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index b65be24..0022fcf 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -22,12 +22,6 @@ ignore: - "build/_deps/**/*" - "test/**/*" -# Strip the GitHub Actions container-mount prefix (/__w///) -# from paths emitted by gcovr --root "$GITHUB_WORKSPACE". -# Without this, Codecov cannot match coverage lines to repo files. -fixes: - - "/__w/git-wip/git-wip/::" - comment: layout: "reach,diff,flags,files,footer" behavior: default diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00805cc..0d61283 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -232,28 +232,19 @@ jobs: - name: Collect coverage data run: | - gcovr --cobertura coverage.xml \ - --gcov-executable 'llvm-cov gcov' \ - --gcov-ignore-errors=no_working_dir_found \ - --exclude 'build/_deps/' \ - --exclude 'test/' \ - --root "$GITHUB_WORKSPACE" - - - name: Verify coverage file - run: | - ls -la coverage.xml - head -50 coverage.xml - - - name: Install codecov dependencies - env: - DEBIAN_FRONTEND: noninteractive - run: apt-get update && apt-get install -y curl gpg + lcov --capture --directory build \ + --gcov-tool "llvm-cov gcov" \ + --output-file coverage.info + lcov --remove coverage.info \ + '/usr/*' '*/build/_deps/*' '*/test/*' \ + --output-file coverage.info + lcov --list coverage.info - name: Upload to Codecov uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.xml + files: ./coverage.info flags: unittests name: debian-stable-clang-debug verbose: true diff --git a/Makefile b/Makefile index 82a300e..7fbb435 100644 --- a/Makefile +++ b/Makefile @@ -61,13 +61,15 @@ test: ## run unit tests (with ctest, uses REBUILD={true,false}, COVERAGE={true,f ${Q}cd "${BUILD}"/ && ctest -C "${TYPE}" $(if ${CI},--output-on-failure -VV) ${Q}echo " ✅ Unit tests complete." -coverage: ## check code coverage (with gcovr, uses REBUILD={true,false}) +coverage: ## check code coverage (with lcov), uses REBUILD={true,false}) ${Q}$(if $(filter 1 yes true YES TRUE,${REBUILD}),rm -rf "${BUILD}"/) ${Q}${CMAKE} -G ${GENERATOR} -S. -B${BUILD} -DCMAKE_INSTALL_PREFIX="$(PREFIX)" -DCMAKE_BUILD_TYPE="${TYPE}" -DWIP_COVERAGE=ON ${Q}${CMAKE} --build "${BUILD}" --config "${TYPE}" --parallel "${NPROC}" ${Q}cd "${BUILD}"/ && ctest -C "${TYPE}" -VV + ${Q}lcov --capture --directory "${BUILD}" --gcov-tool "llvm-cov gcov" --output-file coverage.info + ${Q}lcov --remove coverage.info '/usr/*' '*/${BUILD}/_deps/*' '*/test/*' --output-file coverage.info ${Q}mkdir -p coverage-report - ${Q}gcovr --html coverage-report/index.html --root . "${BUILD}" + ${Q}genhtml coverage.info --output-directory coverage-report ${Q}echo " ✅ Coverage report generated in coverage-report/" install: ## install the package (to the `PREFIX`, uses REBUILD={true,false}) diff --git a/dependencies.sh b/dependencies.sh index 795ebc9..d0f95e2 100755 --- a/dependencies.sh +++ b/dependencies.sh @@ -205,7 +205,7 @@ case "$pkg_mgr" in libgmock-dev libgtest-dev libgit2-dev - gcovr + lcov ) ;; dnf) @@ -213,14 +213,14 @@ case "$pkg_mgr" in gtest-devel gmock-devel libgit2-devel - gcovr + lcov ) ;; pacman) # Arch uses different package names packages+=( libgit2 - gcovr + lcov ) # Replace base packages with Arch equivalents packages=( "${packages[@]/ninja-build/ninja}" ) From cc14348f4a3bfa947a94693bdf2ef53e8477a24a Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Tue, 24 Mar 2026 23:26:07 -0400 Subject: [PATCH 2/7] [vibe] make coverage work with gcc/clang --- .github/workflows/ci.yml | 8 +++++++- .gitignore | 1 + Makefile | 20 ++++++++++++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d61283..43db139 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -232,11 +232,17 @@ jobs: - name: Collect coverage data run: | + LLVM_COV=$(which llvm-cov 2>/dev/null || ls /usr/bin/llvm-cov-* | sort -V | tail -1) + echo "Using llvm-cov: $LLVM_COV" + printf '#!/bin/sh\nexec "%s" gcov "$@"\n' "$LLVM_COV" > build/gcov-tool.sh + chmod +x build/gcov-tool.sh lcov --capture --directory build \ - --gcov-tool "llvm-cov gcov" \ + --gcov-tool build/gcov-tool.sh \ + --ignore-errors inconsistent,inconsistent,format,unsupported \ --output-file coverage.info lcov --remove coverage.info \ '/usr/*' '*/build/_deps/*' '*/test/*' \ + --ignore-errors inconsistent,inconsistent,format,unsupported \ --output-file coverage.info lcov --list coverage.info diff --git a/.gitignore b/.gitignore index 0fc9d21..00ab863 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ build/ coverage-report/ coverage.xml +coverage.info ### C++ # Prerequisites diff --git a/Makefile b/Makefile index 7fbb435..08a863a 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,12 @@ NPROC ?= $(shell nproc || echo 1) CC ?= $(shell which clang gcc cc | head -n1) CXX ?= $(shell which clang g++ c++ | head -n1) COVERAGE ?= false +# Locate the right gcov-compatible tool to match the compiler: +# - if CC is clang, use llvm-cov (prefer plain symlink, fall back to versioned) +# - otherwise use plain gcov +# Use = (recursive) not := (immediate) so CC override on the command line is respected. +_IS_CLANG = $(shell $(CC) --version 2>/dev/null | grep -c clang) +GCOV_TOOL = $(if $(filter 1,$(_IS_CLANG)),$(shell which llvm-cov 2>/dev/null || ls /usr/bin/llvm-cov-* 2>/dev/null | sort -V | tail -1) gcov,gcov) $(info ## TYPE=${TYPE} CC=${CC} CXX=${CXX} COVERAGE=${COVERAGE}) # Coverage flag for CMake @@ -63,13 +69,19 @@ test: ## run unit tests (with ctest, uses REBUILD={true,false}, COVERAGE={true,f coverage: ## check code coverage (with lcov), uses REBUILD={true,false}) ${Q}$(if $(filter 1 yes true YES TRUE,${REBUILD}),rm -rf "${BUILD}"/) - ${Q}${CMAKE} -G ${GENERATOR} -S. -B${BUILD} -DCMAKE_INSTALL_PREFIX="$(PREFIX)" -DCMAKE_BUILD_TYPE="${TYPE}" -DWIP_COVERAGE=ON + ${Q}${CMAKE} -G ${GENERATOR} -S. -B${BUILD} -DCMAKE_INSTALL_PREFIX="$(PREFIX)" -DCMAKE_BUILD_TYPE="${TYPE}" -DWIP_COVERAGE=ON -DCMAKE_C_COMPILER="${CC}" -DCMAKE_CXX_COMPILER="${CXX}" ${Q}${CMAKE} --build "${BUILD}" --config "${TYPE}" --parallel "${NPROC}" ${Q}cd "${BUILD}"/ && ctest -C "${TYPE}" -VV - ${Q}lcov --capture --directory "${BUILD}" --gcov-tool "llvm-cov gcov" --output-file coverage.info - ${Q}lcov --remove coverage.info '/usr/*' '*/${BUILD}/_deps/*' '*/test/*' --output-file coverage.info + ${Q}printf '#!/bin/sh\nexec $(GCOV_TOOL) "$$@"\n' > "${BUILD}/gcov-tool.sh" && chmod +x "${BUILD}/gcov-tool.sh" + ${Q}lcov --capture --directory "${BUILD}" --gcov-tool "${BUILD}/gcov-tool.sh" \ + --ignore-errors inconsistent,inconsistent,format,unsupported \ + --output-file coverage.info + ${Q}lcov --remove coverage.info '/usr/*' '*/${BUILD}/_deps/*' '*/test/*' \ + --ignore-errors inconsistent,inconsistent,format,unsupported \ + --output-file coverage.info ${Q}mkdir -p coverage-report - ${Q}genhtml coverage.info --output-directory coverage-report + ${Q}genhtml coverage.info --output-directory coverage-report \ + --ignore-errors inconsistent,inconsistent,corrupt,unsupported,category ${Q}echo " ✅ Coverage report generated in coverage-report/" install: ## install the package (to the `PREFIX`, uses REBUILD={true,false}) From 20c704bbb9e6c310dcca9225ffcdd3706e22ddd2 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Tue, 24 Mar 2026 23:31:56 -0400 Subject: [PATCH 3/7] [vibe] switch CI coverage to use gcc/lcov --- .github/workflows/ci.yml | 19 ++++++------------- Makefile | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43db139..4636f05 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -192,7 +192,7 @@ jobs: retention-days: 7 coverage: - name: Coverage (debian:stable / clang / Debug) + name: Coverage (debian:stable / gcc / Debug) runs-on: ubuntu-latest @@ -206,7 +206,7 @@ jobs: - name: Install dependencies env: DEBIAN_FRONTEND: noninteractive - run: bash dependencies.sh --compiler=clang + run: bash dependencies.sh --compiler=gcc - name: Cache CMake FetchContent (spdlog, clipp, fmt) uses: actions/cache@v5 @@ -224,27 +224,20 @@ jobs: - name: Build with coverage env: - CC: clang - CXX: clang++ + CC: gcc + CXX: g++ CI: "1" COVERAGE: "1" run: make TYPE=Debug test - name: Collect coverage data run: | - LLVM_COV=$(which llvm-cov 2>/dev/null || ls /usr/bin/llvm-cov-* | sort -V | tail -1) - echo "Using llvm-cov: $LLVM_COV" - printf '#!/bin/sh\nexec "%s" gcov "$@"\n' "$LLVM_COV" > build/gcov-tool.sh - chmod +x build/gcov-tool.sh lcov --capture --directory build \ - --gcov-tool build/gcov-tool.sh \ - --ignore-errors inconsistent,inconsistent,format,unsupported \ + --gcov-tool gcov \ --output-file coverage.info lcov --remove coverage.info \ '/usr/*' '*/build/_deps/*' '*/test/*' \ - --ignore-errors inconsistent,inconsistent,format,unsupported \ --output-file coverage.info - lcov --list coverage.info - name: Upload to Codecov uses: codecov/codecov-action@v5 @@ -252,6 +245,6 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.info flags: unittests - name: debian-stable-clang-debug + name: debian-stable-gcc-debug verbose: true fail_ci_if_error: true diff --git a/Makefile b/Makefile index 08a863a..015704b 100644 --- a/Makefile +++ b/Makefile @@ -74,14 +74,25 @@ coverage: ## check code coverage (with lcov), uses REBUILD={true,false}) ${Q}cd "${BUILD}"/ && ctest -C "${TYPE}" -VV ${Q}printf '#!/bin/sh\nexec $(GCOV_TOOL) "$$@"\n' > "${BUILD}/gcov-tool.sh" && chmod +x "${BUILD}/gcov-tool.sh" ${Q}lcov --capture --directory "${BUILD}" --gcov-tool "${BUILD}/gcov-tool.sh" \ - --ignore-errors inconsistent,inconsistent,format,unsupported \ + --ignore-errors inconsistent \ + --ignore-errors inconsistent \ + --ignore-errors format \ + --ignore-errors unsupported \ --output-file coverage.info ${Q}lcov --remove coverage.info '/usr/*' '*/${BUILD}/_deps/*' '*/test/*' \ - --ignore-errors inconsistent,inconsistent,format,unsupported \ + --ignore-errors inconsistent \ + --ignore-errors inconsistent \ + --ignore-errors format \ + --ignore-errors unsupported \ + --ignore-errors corrupt \ --output-file coverage.info ${Q}mkdir -p coverage-report ${Q}genhtml coverage.info --output-directory coverage-report \ - --ignore-errors inconsistent,inconsistent,corrupt,unsupported,category + --ignore-errors inconsistent \ + --ignore-errors inconsistent \ + --ignore-errors corrupt \ + --ignore-errors unsupported \ + --ignore-errors category ${Q}echo " ✅ Coverage report generated in coverage-report/" install: ## install the package (to the `PREFIX`, uses REBUILD={true,false}) From 8d2cc87cd70ed364f02bd3b430a4d87bb643a3f5 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Tue, 24 Mar 2026 23:41:22 -0400 Subject: [PATCH 4/7] [vibe] try w/o coverage on spdlog --- CMakeLists.txt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f913582..6e929db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,12 +8,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) # Enable generation of compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# Code coverage options option(WIP_COVERAGE "Enable code coverage instrumentation" OFF) -if(WIP_COVERAGE) - add_compile_options(-fprofile-arcs -ftest-coverage) - add_link_options(-fprofile-arcs -ftest-coverage) -endif() include(FetchContent) include(CheckCXXSourceCompiles) @@ -26,6 +21,15 @@ FetchContent_Declare( FetchContent_MakeAvailable(spdlog) +# Code coverage options — applied AFTER FetchContent so that third-party +# dependencies (spdlog, fmt, …) are NOT instrumented. Mixing gcov versions +# between the host compiler and a pre-built dep causes "version mismatch" +# errors at runtime that pollute test output. +if(WIP_COVERAGE) + add_compile_options(-fprofile-arcs -ftest-coverage) + add_link_options(-fprofile-arcs -ftest-coverage) +endif() + # Detect whether the compiler's standard library ships (C++23 P2093). # GCC < 14 and some older clangs lack it even with -std=c++23. check_cxx_source_compiles(" From d226935b051aee59b3637012f8aa7329bc79a3bc Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Wed, 25 Mar 2026 07:31:13 -0400 Subject: [PATCH 5/7] [vibe] coverage job runs make coverage --- .github/workflows/ci.yml | 14 ++------------ Makefile | 6 ++++-- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4636f05..891d12e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -222,22 +222,12 @@ jobs: git config --global user.name "GitHub Actions" git config --global init.defaultBranch master - - name: Build with coverage + - name: Build, test, and collect coverage env: CC: gcc CXX: g++ CI: "1" - COVERAGE: "1" - run: make TYPE=Debug test - - - name: Collect coverage data - run: | - lcov --capture --directory build \ - --gcov-tool gcov \ - --output-file coverage.info - lcov --remove coverage.info \ - '/usr/*' '*/build/_deps/*' '*/test/*' \ - --output-file coverage.info + run: make CC=gcc CXX=g++ TYPE=Debug coverage REBUILD=1 - name: Upload to Codecov uses: codecov/codecov-action@v5 diff --git a/Makefile b/Makefile index 015704b..c351a04 100644 --- a/Makefile +++ b/Makefile @@ -73,13 +73,15 @@ coverage: ## check code coverage (with lcov), uses REBUILD={true,false}) ${Q}${CMAKE} --build "${BUILD}" --config "${TYPE}" --parallel "${NPROC}" ${Q}cd "${BUILD}"/ && ctest -C "${TYPE}" -VV ${Q}printf '#!/bin/sh\nexec $(GCOV_TOOL) "$$@"\n' > "${BUILD}/gcov-tool.sh" && chmod +x "${BUILD}/gcov-tool.sh" - ${Q}lcov --capture --directory "${BUILD}" --gcov-tool "${BUILD}/gcov-tool.sh" \ + ${Q}lcov --capture --directory "${BUILD}/src" \ + --directory "${BUILD}/test" \ + --gcov-tool "${BUILD}/gcov-tool.sh" \ --ignore-errors inconsistent \ --ignore-errors inconsistent \ --ignore-errors format \ --ignore-errors unsupported \ --output-file coverage.info - ${Q}lcov --remove coverage.info '/usr/*' '*/${BUILD}/_deps/*' '*/test/*' \ + ${Q}lcov --remove coverage.info '/usr/*' '*/test/*' \ --ignore-errors inconsistent \ --ignore-errors inconsistent \ --ignore-errors format \ From ec9a96ae2b99d4f06d4216ee35a77654bcfe6c51 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Wed, 25 Mar 2026 08:12:33 -0400 Subject: [PATCH 6/7] Update ci.yml --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 891d12e..7092d85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -206,7 +206,9 @@ jobs: - name: Install dependencies env: DEBIAN_FRONTEND: noninteractive - run: bash dependencies.sh --compiler=gcc + run: | + bash dependencies.sh --compiler=gcc + apt install -y curl gpg - name: Cache CMake FetchContent (spdlog, clipp, fmt) uses: actions/cache@v5 From e68117d7a338e3d0e75b625b0c47fa8f9e960dae Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Wed, 25 Mar 2026 08:42:28 -0400 Subject: [PATCH 7/7] [vibe] only install coverage packages when needed --- .codecov.yml | 2 +- .github/workflows/ci.yml | 4 +--- Makefile | 4 ++-- dependencies.sh | 25 +++++++++++++++++++++---- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 0022fcf..d0e6b47 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -25,4 +25,4 @@ ignore: comment: layout: "reach,diff,flags,files,footer" behavior: default - require_changes: no \ No newline at end of file + require_changes: no diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7092d85..01df302 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -206,9 +206,7 @@ jobs: - name: Install dependencies env: DEBIAN_FRONTEND: noninteractive - run: | - bash dependencies.sh --compiler=gcc - apt install -y curl gpg + run: bash dependencies.sh --compiler=gcc --coverage - name: Cache CMake FetchContent (spdlog, clipp, fmt) uses: actions/cache@v5 diff --git a/Makefile b/Makefile index c351a04..03daccc 100644 --- a/Makefile +++ b/Makefile @@ -67,11 +67,11 @@ test: ## run unit tests (with ctest, uses REBUILD={true,false}, COVERAGE={true,f ${Q}cd "${BUILD}"/ && ctest -C "${TYPE}" $(if ${CI},--output-on-failure -VV) ${Q}echo " ✅ Unit tests complete." -coverage: ## check code coverage (with lcov), uses REBUILD={true,false}) +coverage: ## check code coverage (with lcov, uses REBUILD={true,false}) ${Q}$(if $(filter 1 yes true YES TRUE,${REBUILD}),rm -rf "${BUILD}"/) ${Q}${CMAKE} -G ${GENERATOR} -S. -B${BUILD} -DCMAKE_INSTALL_PREFIX="$(PREFIX)" -DCMAKE_BUILD_TYPE="${TYPE}" -DWIP_COVERAGE=ON -DCMAKE_C_COMPILER="${CC}" -DCMAKE_CXX_COMPILER="${CXX}" ${Q}${CMAKE} --build "${BUILD}" --config "${TYPE}" --parallel "${NPROC}" - ${Q}cd "${BUILD}"/ && ctest -C "${TYPE}" -VV + ${Q}cd "${BUILD}"/ && ctest -C "${TYPE}" $(if ${CI},--output-on-failure -VV) ${Q}printf '#!/bin/sh\nexec $(GCOV_TOOL) "$$@"\n' > "${BUILD}/gcov-tool.sh" && chmod +x "${BUILD}/gcov-tool.sh" ${Q}lcov --capture --directory "${BUILD}/src" \ --directory "${BUILD}/test" \ diff --git a/dependencies.sh b/dependencies.sh index d0f95e2..03b1c73 100755 --- a/dependencies.sh +++ b/dependencies.sh @@ -113,6 +113,7 @@ function want_one_of() { # --------------------------------------------------------------------------- compiler="" # empty → auto-select via must_have_one_of +coverage=0 # --coverage → install lcov, curl, gpg for arg in "$@" ; do case "$arg" in @@ -122,9 +123,11 @@ for arg in "$@" ; do --compiler=clang) compiler=clang ;; --compiler=*) die "unknown --compiler value '${arg#--compiler=}' (expected gcc or clang)" ;; + --coverage) + coverage=1 ;; -h|--help) cat <<'EOF' -Usage: dependencies.sh [--compiler=] [-h|--help] +Usage: dependencies.sh [--compiler=] [--coverage] [-h|--help] Install build dependencies for git-wip. @@ -133,6 +136,8 @@ Options: --compiler=clang Install clang (no flag) Install whichever of clang/gcc is available (auto-select) + --coverage Also install coverage tools (lcov, curl, gpg) + -h, --help Show this help and exit EOF exit 0 @@ -205,7 +210,6 @@ case "$pkg_mgr" in libgmock-dev libgtest-dev libgit2-dev - lcov ) ;; dnf) @@ -213,14 +217,12 @@ case "$pkg_mgr" in gtest-devel gmock-devel libgit2-devel - lcov ) ;; pacman) # Arch uses different package names packages+=( libgit2 - lcov ) # Replace base packages with Arch equivalents packages=( "${packages[@]/ninja-build/ninja}" ) @@ -233,6 +235,21 @@ case "$pkg_mgr" in ;; esac +# Coverage tools (only when --coverage is requested) +if [ "$coverage" = 1 ]; then + case "$pkg_mgr" in + apt) + packages+=( lcov curl gpg ) + ;; + dnf) + packages+=( lcov curl gnupg2 ) + ;; + pacman) + packages+=( lcov curl gnupg ) + ;; + esac +fi + set -e -x case "$pkg_mgr" in