Skip to content

Add support for generating coverage reports w/ gcov - #4489

Open
d-torrance wants to merge 9 commits into
Macaulay2:developmentfrom
d-torrance:gcov
Open

Add support for generating coverage reports w/ gcov#4489
d-torrance wants to merge 9 commits into
Macaulay2:developmentfrom
d-torrance:gcov

Conversation

@d-torrance

Copy link
Copy Markdown
Member

This replaces MichaelABurr#65

As discussed in the C++ refactoring group Zoom meeting yesterday: If we build M2 using ./configure --enable-gcov, then we can run make coverage-report to get a gcovr-generated coverage report of the engine and interpreter. In particular, everything that was covered since we built M2 (or ran make coverage-reset).

This will be useful to figuring out which features still need unit testing (e.g., by running make coverage-reset && make -C Macaulay2/e check && make coverage-report, we can see what all the unit tests hit.) There's also CMake support that I haven't tested.

AI Disclosure

🤖 This was Claude through and through 🤖

d-torrance and others added 9 commits August 1, 2026 09:13
Adds a --enable-gcov option (modeled on --enable-profile) that builds with
--coverage -O0, plus -fprofile-abs-path under gcc so gcov/gcovr can resolve
sources from the out-of-tree build; it also disables stripping.

Coverage data (.gcda) is generated automatically whenever any coverage-built
binary exits, so no target runs the tests for you: just run the unit tests,
make check, or the M2 binary on your own code.  Two top-level targets clear
and report on the accumulated data:
  coverage-reset   delete accumulated .gcda (tree-wide)
  coverage-report  produce an HTML report via gcovr (engine-scoped by default;
                   override COVERAGE_ROOT/COVERAGE_OBJDIR for the whole tree)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a -DGCOV=ON option that builds with --coverage -O0, plus
-fprofile-abs-path under gcc, mirroring the PROFILING flag block.  New
cmake/gcov.cmake provides coverage-reset and coverage-report (gcovr) targets,
paralleling the autotools setup; coverage data is generated by running any
coverage-built binary (unit tests, ctest, or the M2 binary).

Also fixes a pre-existing typo in the dormant Clang coverage.cmake, which
searched for clang-format instead of llvm-cov.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The coverage-report target now prints the report path as an OSC 8 terminal
hyperlink (file://host/path) via printf, so terminals that support it render
it as a clickable link.  Done for both the autotools (Makefile.in) and cmake
(gcov.cmake) targets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gcov can report the same function on more than one line (e.g. inlines compiled
at -O0), which gcovr's default --merge-mode-functions=strict treats as a fatal
error.  Pass merge-use-line-min so those entries are merged and attributed to
the function's first line.  Applied to both the autotools and cmake targets;
the autotools value is overridable via COVERAGE_MERGE_MODE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-fprofile-abs-path is gcc-only (clang rejects it), and it was only a
convenience for source resolution -- which gcovr already handles via its
--root/--object-directory options.  Removing it keeps --coverage portable
across gcc and clang and avoids a compiler-specific guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consolidate the declaration and flag-setting logic into one block, quote
macro arguments with [...], use AS_IF instead of raw test/if, and keep lines
within 80 columns.  No functional change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On macOS, applying --coverage before AC_FC_LIBRARY_LDFLAGS/AX_BLAS/AX_LAPACK
let gfortran expand --coverage to -lgcov and bake it into FCLIBS/BLAS_LIBS.
The final clang link then pulled in both gcc's libgcov and clang's own
libclang_rt.profile, producing duplicate __gcov_reset/__gcov_fork symbols.
Move the gcov flag block after that detection so only the compiler's own
coverage runtime ends up on the link line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pass the object tree to gcovr as a positional search path instead of via
--object-directory.  --object-directory forces gcov to run in a single
directory, which cannot resolve the relative source paths of objects built in
deeper VPATH subdirectories (e.g. Macaulay2/e/unit-tests), causing "could not
infer a working directory" errors.  As a search path, gcovr runs gcov in each
data file's own directory, so every source resolves.

Also default COVERAGE_ROOT/COVERAGE_OBJDIR to the whole tree, drop the
hardcoded unit-tests exclude, and add a GCOVR_OPTIONS variable for passing
extra gcovr options (filters, excludes, formats).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Report over the whole tree by default (root = source tree, build tree passed
as a positional search path rather than --object-directory), drop the
hardcoded unit-tests exclude, and add a GCOVR_OPTIONS cache variable for
passing extra gcovr options (split with separate_arguments).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mikestillman

Copy link
Copy Markdown
Member

I suppose we need to install gcovr first? Where should we put that info? I don't want (or can't) install using pip globally...

@mikestillman

Copy link
Copy Markdown
Member

Here is one set of instructions (from chatgpt). Sound reasonable? (I've never used pipx before).

Do the following once (using homebrew, on mac)

brew install pipx
pipx ensurepath
pipx install gcovr

Restart a terminal, do

gcovr --version

to upgrade or remove:

pipx upgrade gcovr
pipx uninstall gcovr

@d-torrance

Copy link
Copy Markdown
Member Author

It's available from most package managers (apt install gcovr, brew install gcovr, etc.) Maybe we should add a wiki page or something? Since it isn't a build dependency and we only need it for generating the reports, I think it might be overkill to check for it

@d-torrance

Copy link
Copy Markdown
Member Author

Yeah pipx is cool for scripts written in Python. But gcovr itself is in Homebrew, so I don't think that's necessary -- just brew install gcovr.

@mikestillman

Copy link
Copy Markdown
Member

The info about pipx suggested by chatGPT is too complicated and not needed on a mac: I believe just doing brew install gcovr does the trick.

@d-torrance d-torrance added the Infrastructure GitHub workflows, etc. label Aug 3, 2026
Comment thread M2/cmake/gcov.cmake

if(GCOV)
find_program(GCOVR NAMES gcovr)
set(_coverage_dir ${CMAKE_BINARY_DIR}/coverage)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a small CI smoke test with coverage enabled, especially for the CMake path? The current checks all use the default GCOV=OFF, so they don't exercise the instrumentation flags or either coverage target; the PR also notes that the CMake support hasn't been tested. Even a minimal configure/build/run/coverage-report job would catch toolchain and gcovr invocation problems.

AI-assisted review comment made by GPT 4.6-Sol under Andrew's direction in order to observe deeper code reaches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Infrastructure GitHub workflows, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants