A while back, I was toying with the idea of adding gcov support to libgdata, to give metrics on the code coverage of the test suite. I played around with adding it, but my attempts were thwarted by the fact that I couldn't easily get a list of all C files which were to be compiled, in all source directories, to use in the top-level Makefile.
I recently found a little time to work on this some more and, as usually happens, it ballooned into something bigger, and I ended up redoing the entire build system for libgdata. It now uses non-recursive automake, which makes things a lot simpler in many respects. The diffstat was favourable ("21 files changed, 641 insertions(+), 833 deletions(-)"), and it has made the autogen/clean/build cycle about 20 seconds faster (on average, from some very rough tests). Accomplishing this was largely possible due to the (fairly) recent literature on the subject from Murray Cumming and Daniel Elstner, which proved rather useful; so many thanks to them.
As a result of all this, I now know that only just over 50% of libgdata's code was actually being exercised before by the test suite. I've now pushed this up to 60%, and uncovered a few buglets in the process. I dread to think what other problems lie in the remaining 40%; there's certainly lots of work left to be done.
Anyway, since all the snippets of automake magic I could find were subtly incompatible with non-recursive automake, I ended up with a slightly different one (based on gobject-introspection's gcov support). Hopefully it's useful to someone, though it could probably do with some tidying up:
if GCOV_ENABLED gcov-report.txt: gcov-clean all check $(AM_V_GEN)(rm -f $@; \ echo -e "Test coverage for libgdata:\n" >> $@; \ total_covered=0; total_actual=0; \ for file in $(filter %.c,$(gdata_libgdata_la_SOURCES)); do \ file2=\(\){file%/*}; \ gcov -o `find -newer \(\){file2/.c/.gcda}" -print0 | sed -e 's/\.gcda/\.o/'` \(\)file2.gcov; then \ actual=`grep -v ' -:' \(\)file2.gcov | wc -l`; \ covered=\(\)((total_covered + covered)); \ total_actual=\(\)file:\t\(\)actual\t(\(\)covered * 100) / \(\)total_actual\nCovered statements: \(\)(((\(\)total_actual))%" >> $@) gcov: gcov-report.txt @cat gcov-report.txt clean: gcov-clean gcov-clean: @find . -name "*.gcda" -o -name "*.gcov" -delete MAINTAINERCLEANFILES += gcov-report.txt .PHONY: gcov gcov-clean gcov-report.txt else gcov: @echo "Need to reconfigure with --enable-gcov" endif
It will output a code coverage report of the test suite (`make check`
) when `make gcov`
is called, and will also save it as "gcov-report.txt" in the root source directory.
I think it has to be spelled .PHONY: 🙂
Good catch. I'd meant to change that before committing, but forgot. Now I've got the problem of having .PHONY twice in my Makefile though, and I don't want to combine them because that would be messy. Humph.
targets with no body just add dependencys. E.g.
.PHONY: foo
.PHONY: bar
is equivalent to
.PHONY: foo bar
It would be great if automake would generate gcov makefile targets!
Hello Philip,
Thank you for this blog post, very interesting. I've added libgdata to my list of projects using no recursive automake: http://live.gnome.org/JavierJardon/NewBuildSystem . Hope you don't mind
Also, maybe you already know, but you can see the coverage of your code here: http://fixed.gnome.org/coverage/libgdata/lcov/ and the status of the build here: http://build.gnome.org/libgdata
Regards
Ooh, thanks.
I didn't know we had automated lcov testing going on; that's quite useful to know. Unfortunately, the build bots are using libgdata 0.4.0, which is a bit out of date now. I should probably get around to asking for an external dependency version bump for libgdata sometime.