1# ===========================================================================
2#     https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html
3# ===========================================================================
4#
5# SYNOPSIS
6#
7#   AX_CODE_COVERAGE()
8#
9# DESCRIPTION
10#
11#   Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS,
12#   CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included
13#   in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every
14#   build target (program or library) which should be built with code
15#   coverage support. Also defines CODE_COVERAGE_RULES which should be
16#   substituted in your Makefile; and $enable_code_coverage which can be
17#   used in subsequent configure output. CODE_COVERAGE_ENABLED is defined
18#   and substituted, and corresponds to the value of the
19#   --enable-code-coverage option, which defaults to being disabled.
20#
21#   Test also for gcov program and create GCOV variable that could be
22#   substituted.
23#
24#   Note that all optimization flags in CFLAGS must be disabled when code
25#   coverage is enabled.
26#
27#   Usage example:
28#
29#   configure.ac:
30#
31#     AX_CODE_COVERAGE
32#
33#   Makefile.am:
34#
35#     @CODE_COVERAGE_RULES@
36#     my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ...
37#     my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ...
38#     my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ...
39#     my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ...
40#
41#   This results in a "check-code-coverage" rule being added to any
42#   Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module
43#   has been configured with --enable-code-coverage). Running `make
44#   check-code-coverage` in that directory will run the module's test suite
45#   (`make check`) and build a code coverage report detailing the code which
46#   was touched, then print the URI for the report.
47#
48#   In earlier versions of this macro, CODE_COVERAGE_LDFLAGS was defined
49#   instead of CODE_COVERAGE_LIBS. They are both still defined, but use of
50#   CODE_COVERAGE_LIBS is preferred for clarity; CODE_COVERAGE_LDFLAGS is
51#   deprecated. They have the same value.
52#
53#   In order to generate a code coverage report across your entire project,
54#   you MUST at minimum include the line:
55#
56#   @CODE_COVERAGE_RULES@
57#
58#   in every single Makefile.am throughout your project.
59#
60#   NOTE: If mixing non-recursive and recursive automake, then problems
61#   will appear with missing source files, due to one or more LCOV base
62#   directories missing. This mixed-mode will seemingly always have these
63#   errors and is only solved by using recursive make OR non-recursive
64#   make.
65#
66#   This code was derived from Makefile.decl in GLib, originally licenced
67#   under LGPLv2.1+.
68#
69# LICENSE
70#
71#   Copyright (c) 2012, 2016 Philip Withnall
72#   Copyright (c) 2012 Xan Lopez
73#   Copyright (c) 2012 Christian Persch
74#   Copyright (c) 2012 Paolo Borelli
75#   Copyright (c) 2012 Dan Winship
76#   Copyright (c) 2015 Bastien ROUCARIES
77#   Copyright (C) 2017 Joseph Benden <joe@benden.us>
78#
79#   This library is free software; you can redistribute it and/or modify it
80#   under the terms of the GNU Lesser General Public License as published by
81#   the Free Software Foundation; either version 2.1 of the License, or (at
82#   your option) any later version.
83#
84#   This library is distributed in the hope that it will be useful, but
85#   WITHOUT ANY WARRANTY; without even the implied warranty of
86#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
87#   General Public License for more details.
88#
89#   You should have received a copy of the GNU Lesser General Public License
90#   along with this program. If not, see <https://www.gnu.org/licenses/>.
91
92#serial 26
93
94AC_DEFUN([AX_CODE_COVERAGE],[
95	dnl Check for --enable-code-coverage
96	AC_REQUIRE([AC_PROG_SED])
97
98	# allow to override gcov location
99	AC_ARG_WITH([gcov],
100	  [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])],
101	  [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov],
102	  [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov])
103
104	AC_MSG_CHECKING([whether to build with code coverage support])
105	AC_ARG_ENABLE([code-coverage],
106	  AS_HELP_STRING([--enable-code-coverage],
107	  [Whether to enable code coverage support]),,
108	  enable_code_coverage=no)
109
110	AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes])
111	AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage])
112	AC_MSG_RESULT($enable_code_coverage)
113
114	AS_IF([ test "$enable_code_coverage" = "yes" ], [
115		# check for gcov
116		AC_CHECK_TOOL([GCOV],
117		  [$_AX_CODE_COVERAGE_GCOV_PROG_WITH],
118		  [:])
119		AS_IF([test "X$GCOV" = "X:"],
120		  [AC_MSG_ERROR([gcov is needed to do coverage])])
121		AC_SUBST([GCOV])
122
123		dnl Check if gcc is being used
124		AS_IF([ test "$GCC" = "no" ], [
125			AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage])
126		])
127
128		AC_CHECK_PROG([LCOV], [lcov], [lcov])
129		AC_CHECK_PROG([GENHTML], [genhtml], [genhtml])
130
131		AS_IF([ test -z "$LCOV" ], [
132			AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed])
133		])
134
135		AS_IF([ test -z "$GENHTML" ], [
136			AC_MSG_ERROR([Could not find genhtml from the lcov package])
137		])
138
139		dnl Build the code coverage flags
140		dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility
141		CODE_COVERAGE_CPPFLAGS="-DNDEBUG"
142		CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
143		CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
144		CODE_COVERAGE_LIBS="-lgcov"
145		CODE_COVERAGE_LDFLAGS="$CODE_COVERAGE_LIBS"
146
147		AC_SUBST([CODE_COVERAGE_CPPFLAGS])
148		AC_SUBST([CODE_COVERAGE_CFLAGS])
149		AC_SUBST([CODE_COVERAGE_CXXFLAGS])
150		AC_SUBST([CODE_COVERAGE_LIBS])
151		AC_SUBST([CODE_COVERAGE_LDFLAGS])
152
153		[CODE_COVERAGE_RULES_CHECK='
154	$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture TOP_CODE_COVERAGE_OUTPUT_FILE="$(CODE_COVERAGE_OUTPUT_FILE)"
155	$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture TOP_CODE_COVERAGE_OUTPUT_FILE="$(CODE_COVERAGE_OUTPUT_FILE)"
156	$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-join
157	$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-remove
158	$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-genhtml
159']
160		[CODE_COVERAGE_RULES_CAPTURE='
161	-$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) check-am
162	find $(builddir) \( -name "*.o" \) -exec $(GCOV) -i -b -p -r -s $(top_absdir) {} ";"; \
163	$(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --no-recursion --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) --ignore-errors source
164	if test ! -e "$(TOP_CODE_COVERAGE_OUTPUT_FILE).join" -a -s "$(CODE_COVERAGE_OUTPUT_FILE).tmp"; then \
165		cp -v "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "$(TOP_CODE_COVERAGE_OUTPUT_FILE).join"; \
166	fi;
167']
168    [CODE_COVERAGE_RULES_JOIN='
169	find $(builddir) -size +0c -a -name "$(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info.tmp" -exec echo "\"{}\"" ";" | \
170	xargs -L 1 $(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --no-recursion --output-file "$(CODE_COVERAGE_OUTPUT_FILE).join" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) --ignore-errors source -a "$(CODE_COVERAGE_OUTPUT_FILE).join" -a
171']
172	  [CODE_COVERAGE_RULES_REMOVE='
173	$(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --no-recursion --remove "$(CODE_COVERAGE_OUTPUT_FILE).join" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS)
174']
175    [CODE_COVERAGE_RULES_GENHTML='
176	-@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp
177	find $(top_builddir) -size 0c -a -name "*.gcov" -delete
178	$(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) --ignore-errors source
179	@echo "file://$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html"
180']
181		[CODE_COVERAGE_RULES_CLEAN='
182clean: code-coverage-clean
183distclean: code-coverage-clean
184code-coverage-clean:
185	-$(LCOV) --directory $(top_builddir) -z
186	-rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_FILE).join $(CODE_COVERAGE_OUTPUT_DIRECTORY)
187	-find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete
188']
189	], [
190		[CODE_COVERAGE_RULES_CHECK='
191	@echo "Need to reconfigure with --enable-code-coverage"
192']
193		CODE_COVERAGE_RULES_CAPTURE="$CODE_COVERAGE_RULES_CHECK"
194		CODE_COVERAGE_RULES_CLEAN=''
195	])
196
197[CODE_COVERAGE_RULES='
198# Code coverage
199#
200# Optional:
201#  - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting.
202#    Multiple directories may be specified, separated by whitespace.
203#    (Default: $(top_builddir))
204#  - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated
205#    by lcov for code coverage. (Default:
206#    $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info)
207#  - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage
208#    reports to be created. (Default:
209#    $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage)
210#  - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage,
211#    set to 0 to disable it and leave empty to stay with the default.
212#    (Default: empty)
213#  - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov
214#    instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
215#  - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov
216#    instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
217#  - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov
218#  - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the
219#    collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
220#  - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov
221#    instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
222#  - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering
223#    lcov instance. (Default: empty)
224#  - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov
225#    instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
226#  - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the
227#    genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
228#  - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml
229#    instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
230#  - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore
231#
232# The generated report will be titled using the $(PACKAGE_NAME) and
233# $(PACKAGE_VERSION). In order to add the current git hash to the title,
234# use the git-version-gen script, available online.
235
236# Optional variables
237CODE_COVERAGE_DIRECTORY ?= $(builddir)
238CODE_COVERAGE_OUTPUT_FILE ?= $(abs_builddir)/$(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info
239CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(abs_builddir)/$(PACKAGE_NAME)-coverage
240CODE_COVERAGE_BRANCH_COVERAGE ?= 1
241CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= -b $(abs_srcdir) $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
242--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
243CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
244CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)"
245CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
246CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
247CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?=
248CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
249CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\
250$(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
251--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
252CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
253CODE_COVERAGE_IGNORE_PATTERN ?=
254
255GITIGNOREFILES ?=
256GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
257
258code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V))
259code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY))
260code_coverage_v_lcov_cap_0 = echo "  LCOV   --capture"\
261 $(CODE_COVERAGE_OUTPUT_FILE);
262code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V))
263code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY))
264code_coverage_v_lcov_ign_0 = echo "  LCOV   --remove /tmp/*"\
265 $(CODE_COVERAGE_IGNORE_PATTERN);
266code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V))
267code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY))
268code_coverage_v_genhtml_0 = echo "  GEN   " $(CODE_COVERAGE_OUTPUT_DIRECTORY);
269code_coverage_quiet = $(code_coverage_quiet_$(V))
270code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY))
271code_coverage_quiet_0 = --quiet
272
273# sanitizes the test-name: replaces with underscores: dashes and dots
274code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1)))
275
276# Use recursive makes in order to ignore errors during check
277check-code-coverage:'"$CODE_COVERAGE_RULES_CHECK"'
278
279code-coverage-join:'"$CODE_COVERAGE_RULES_JOIN"'
280
281code-coverage-remove:'"$CODE_COVERAGE_RULES_REMOVE"'
282
283code-coverage-genhtml:'"$CODE_COVERAGE_RULES_GENHTML"'
284
285# Capture code coverage data
286#am__extra_recursive_targets = code-coverage-capture
287#am__recursive_targets += code-coverage-capture
288
289code-coverage-capture-am: code-coverage-capture-hook'"$CODE_COVERAGE_RULES_CAPTURE"'
290
291ifneq ($(SUBDIRS),)
292code-coverage-capture: code-coverage-capture-recursive
293else
294code-coverage-capture: code-coverage-capture-am
295endif
296
297# Hook rule executed before code-coverage-capture, overridable by the user
298code-coverage-capture-hook:
299
300'"$CODE_COVERAGE_RULES_CLEAN"'
301
302A''M_DISTCHECK_CONFIGURE_FLAGS ?=
303A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
304
305# .PHONY: check-code-coverage code-coverage-capture code-coverage-capture-recursive code-coverage-capture-am code-coverage-join code-coverage-remove code-coverage-genhtml code-coverage-capture-hook code-coverage-clean
306.MAKE: code-coverage-capture-am
307
308.PHONY: code-coverage-capture-am code-coverage-capture  \
309        check-code-coverage code-coverage-join code-coverage-remove code-coverage-genhtml code-coverage-capture-hook code-coverage-clean
310
311']
312
313  am__extra_recursive_targets="code-coverage-capture-recursive"
314  AC_SUBST([am__extra_recursive_targets])
315	AC_SUBST([CODE_COVERAGE_RULES])
316	m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])])
317])
318