summaryrefslogtreecommitdiff
path: root/m4
diff options
context:
space:
mode:
Diffstat (limited to 'm4')
-rw-r--r--m4/eos-coverage.m4361
1 files changed, 361 insertions, 0 deletions
diff --git a/m4/eos-coverage.m4 b/m4/eos-coverage.m4
new file mode 100644
index 0000000..e179dc2
--- /dev/null
+++ b/m4/eos-coverage.m4
@@ -0,0 +1,361 @@
+dnl Copyright 2014 Endless Mobile, Inc.
+dnl
+dnl Macros to check for code coverage support
+dnl
+dnl If you wish to use the JavaScript coverage function, Makefile-jasmine.am.inc
+dnl should be included before calling EOS_COVERAGE_RULES.
+dnl
+dnl Add clean-coverage to the clean-local target in your Makefile to get the clean
+dnl rules for the coverage data. Or, if clean-local is not defined in your
+dnl Makefile, you can just use EOS_COVERAGE_CLEAN_RULES.
+dnl
+dnl Variables that affect the operation of the inserted make rules:
+dnl
+dnl - EOS_JS_COVERAGE_FILES: The list of JavaScript files to be included
+dnl in the JavaScript coverage report. This must be set before including
+dnl this Makefile if EOS_ENABLE_JS_COVERAGE was enabled. Absolute, relative
+dnl and resource paths are all fine.
+dnl
+dnl If EOS_ENABLE_C_COVERAGE was enabled and coverage reporting for the
+dnl project has been enabled on the commandline, then these make rules will
+dnl add -coverage to CFLAGS and LDFLAGS and also create a "eos-c-coverage"
+dnl target which collects line and function hit counter data and places
+dnl it in $coverage_directory/output/c/coverage.lcov
+dnl
+dnl If EOS_ENABLE_JS_COVERAGE was enabled and coverage reporting for this
+dnl project has been enabled on the commandline, then these make rules will
+dnl add the coverage generating switch to EOS_JS_COVERAGE_LOG_FLAGS for all
+dnl files specified in EOS_JS_COVERAGE_FILES. Coverage output from gjs will
+dnl go in $coverage_directory/output/js/coverage.lcov
+
+EOS_HAVE_C_COVERAGE=no
+EOS_HAVE_JS_COVERAGE=no
+EOS_HAVE_COBERTURA=no
+EOS_HAVE_GENTHML=no
+EOS_HAVE_COVERAGE_REPORT=no
+
+LCOV=notfound
+GENHTML=notfound
+LCOV_RESULT_MERGER=notfound
+
+AC_DEFUN_ONCE([EOS_COVERAGE_REPORT], [
+ # Enable the --enable-coverage switch, although it'll only be effective
+ # if we can actually do coverage reports
+ AC_ARG_ENABLE([coverage],
+ [AS_HELP_STRING([--enable-coverage],
+ [Generate code coverage statistics when running tests @<:@default=no@:>@])
+ ])
+
+ # This needs to be defined here so that AC_MSG_RESULT sees it
+ EOS_COVERAGE_REQUESTED=no
+ AC_MSG_CHECKING(whether code coverage support was requested)
+ AS_IF([test "x$enable_coverage" = "xyes"], [
+ EOS_COVERAGE_REQUESTED=yes
+ ])
+ AC_MSG_RESULT([$EOS_COVERAGE_REQUESTED])
+
+ AS_IF([test "x$EOS_COVERAGE_REQUESTED" = "xyes"], [
+
+ # Need LCOV to do coverage report filtering. If we don't have it
+ # then we have to disable coverage reporting wholesale.
+ AC_PATH_PROG([LCOV], [lcov], [notfound])
+ AC_ARG_VAR([LCOV], [Path to lcov])
+
+ AS_IF([test "x$LCOV" != "xnotfound"], [
+ m4_foreach_w([opt], [$1], [
+ AS_CASE(opt,
+ [c], [
+ # Turn on the coverage generating flags in GCC. This
+ # does not work with clang
+ # (see llvm.org/bugs/show_bug.cgi?id=16568).
+ EOS_COVERAGE_COMPILER_FLAGS="-coverage"
+ EOS_COVERAGE_NODEBUG_COMPILER_FLAGS="-g -O0"
+ EOS_C_COVERAGE_LDFLAGS="-lgcov"
+ AS_COMPILER_FLAGS([EOS_SUPPORTED_COVERAGE_CFLAGS], [$EOS_COVERAGE_COMPILER_FLAGS])
+
+ # If this is empty then coverage reporting is not
+ # supported by the compiler.
+ AS_IF([test x"$EOS_SUPPORTED_COVERAGE_CFLAGS" != "x"], [
+ EOS_HAVE_C_COVERAGE=yes
+
+ # These flags aren't mandatory, but they make
+ # coverage reports more accurate
+ AS_COMPILER_FLAGS([EOS_SUPPORTED_NODEBUG_CFLAGS], [$EOS_COVERAGE_NODEBUG_COMPILER_FLAGS])
+ EOS_C_COVERAGE_CFLAGS="$EOS_SUPPORTED_COVERAGE_CFLAGS $EOS_SUPPORTED_NODEBUG_CFLAGS"
+ ])
+ ],
+ [js], [
+ PKG_CHECK_MODULES([GJS_WITH_COVERAGE], [gjs-1.0 >= 1.40.0], [
+ EOS_HAVE_JS_COVERAGE=yes
+ ])
+ ])
+ ])
+
+ EOS_COVERAGE_SUBDIR='_coverage'
+
+ AS_IF([test "x$EOS_HAVE_C_COVERAGE" = "xyes" || test "x$EOS_HAVE_JS_COVERAGE" = "xyes"], [
+ AC_PATH_PROG([GENHTML], [genhtml], [notfound])
+ AC_ARG_VAR([GENHTML], [Path to genhtml])
+ AS_IF([test "x$GENHTML" = "xnotfound"], [
+ ], [
+ EOS_HAVE_GENHTML=yes
+ ])
+
+ AC_PATH_PROG([LCOV_RESULT_MERGER], [node-lcov-result-merger], [notfound])
+ AC_ARG_VAR([LCOV_RESULT_MERGER], [Path to lcov-result-merger])
+
+ AC_MSG_CHECKING(for lcov_cobertura)
+ python -c "import lcov_cobertura" > /dev/null 2>&1
+ AS_IF([test "$?" = "0"], [
+ EOS_COVERAGE_HAVE_LCOV_COBERTURA=yes
+ ], [
+ EOS_COVERAGE_HAVE_LCOV_COBERTURA=no
+ ])
+ AC_MSG_RESULT([$EOS_COVERAGE_HAVE_LCOV_COBERTURA])
+
+ AS_IF([test "x$LCOV_RESULT_MERGER" != "xnotfound" && test "x$EOS_COVERAGE_HAVE_LCOV_COBERTURA" = "xyes"], [
+ EOS_HAVE_COBERTURA=yes
+ ])
+
+ # We have coverage reporting as long as we have either cobertura or
+ # genhtml support
+ AS_IF([test "x$EOS_HAVE_COBERTURA" = "xyes" || test "x$EOS_HAVE_GENHTML" = "xyes" ], [
+ EOS_HAVE_COVERAGE_REPORT=yes
+ ])
+ ])
+
+ # EOS_ENABLE_COVERAGE is set to "no" unless coverage
+ # in at least one language is enabled and a coverage reporter
+ # is available.
+ EOS_ENABLE_COVERAGE=no
+ AS_IF([test "x$EOS_HAVE_COVERAGE_REPORT" = "xyes"], [
+ AS_IF([test "x$EOS_HAVE_C_COVERAGE" = "xyes"], [
+ dnl Strip leading spaces
+ EOS_C_COVERAGE_CFLAGS=${EOS_C_COVERAGE_CFLAGS#* }
+ EOS_ENABLE_C_COVERAGE=yes
+ ])
+ EOS_ENABLE_JS_COVERAGE=$EOS_HAVE_JS_COVERAGE
+ EOS_ENABLE_COVERAGE=yes
+ ])
+ AC_MSG_CHECKING(whether code coverage support can be enabled)
+ AC_MSG_RESULT([$EOS_ENABLE_COVERAGE])
+ ])
+ ])
+
+ # Now that we've figured out if we have coverage reports, build the rules
+ AS_IF([test "x$EOS_ENABLE_COVERAGE" = "xyes"], [
+ EOS_COVERAGE_RULES_HEADER='
+# Copyright (c) 2015 Endless Mobile, Inc, all rights reserved.
+#
+# Internal variable to track the coverage accumulated counter files.
+_eos_coverage_outputs =
+_eos_collect_coverage_targets =
+_eos_clean_coverage_targets =
+
+# Full path to coverage report folder
+_eos_coverage_report_path := $(abs_top_builddir)/$(EOS_COVERAGE_SUBDIR)/report
+
+# Full path to coverage tracefile output
+_eos_coverage_trace_path := $(abs_top_builddir)/$(EOS_COVERAGE_SUBDIR)/output
+
+'
+
+ EOS_COVERAGE_RULES_TARGETS='
+# First check that all the required variables have been set. This includes:
+# - LCOV
+
+$(if $(LCOV),,$(error LCov not found, ensure that eos-coverage.m4 was included in configure.ac))
+
+# Internal variable for the genhtml coverage report path
+_eos_genhtml_coverage_report_path = $(_eos_coverage_report_path)/genhtml
+
+# Internal variable for the cobertura coverage report path
+_eos_cobertura_coverage_report_path = $(_eos_coverage_report_path)/cobertura
+
+# Set up an intermediate eos-collect-coverage target
+# which just runs the language specific coverage collection
+# targets
+#
+# We then compile all of the language specific coverage collection
+# tracefiles into a single tracefile and apply the filters
+# set in EOS_COVERAGE_BLACKLIST_PATTERNS if there are any
+_eos_lcov_add_files_opts = $(addprefix -a ,$(_eos_coverage_outputs))
+eos-collect-coverage: $(_eos_collect_coverage_targets)
+ $(LCOV) -o $(_eos_coverage_trace_path)/coverage.lcov $(_eos_lcov_add_files_opts)
+ if test x"$(EOS_COVERAGE_BLACKLIST_PATTERNS)" != "x" ; then echo $(EOS_COVERAGE_BLACKLIST_PATTERNS) | xargs -L1 $(LCOV) -o $(_eos_coverage_trace_path)/coverage.lcov -r $(_eos_coverage_trace_path)/coverage.lcov ; fi
+
+# The clean-coverage target runs the language specific
+# clean rules and also cleans the generated html reports
+clean-coverage: $(_eos_clean_coverage_targets)
+ rm -rf $(EOS_COVERAGE_SUBDIR)
+'
+ ], [
+ EOS_COVERAGE_RULES_TARGETS='
+# Define the targets just to print an error if coverage reports are not enabled
+eos-collect-coverage:
+ @echo "--enable-coverage must be passed to ./configure"
+ @exit 1
+
+clean-coverage:
+ @echo "no coverage data generated, so none to clean"
+ '])
+
+ AS_IF([test "x$EOS_HAVE_GENHTML" = "xyes"], [
+ EOS_GENHTML_COVERAGE_RULES='
+# Check that required variable GENHTML is set
+$(if $(GENHTML),,$(error GenHTML not found, ensure that eos-coverage.m4 was included in configure.ac))
+
+# The "coverage-genhtml" target depends on eos-collect-coverage
+# and then runs genhtml on the coverage counters. This is useful
+# if you are just looking at coverage data locally.
+coverage-genhtml: eos-collect-coverage
+ $(GENHTML) --legend -o $(_eos_genhtml_coverage_report_path) $(_eos_coverage_trace_path)/coverage.lcov
+'
+ ], [
+ EOS_GENHTML_COVERAGE_RULES='
+coverage-genhtml:
+ @echo "Cannot generate GenHTML coverage report as genhtml was not found in PATH"
+ @exit 1
+'])
+
+ AS_IF([test "x$EOS_HAVE_COBERTURA" = "xyes"], [
+ EOS_COBERTURA_COVERAGE_RULES='
+# Paths to each stage of the cobertura coverage report
+# 1. Merged path
+# 2. XML path
+_eos_cobertura_merged_path = $(_eos_cobertura_coverage_report_path)/merged.lcov
+_eos_cobertura_xml_path = $(_eos_cobertura_coverage_report_path)/cobertura.xml
+
+# The "coverage-cobertura" target depends on eos-collect-coverage
+# and then runs lcov_cobertura.py to convert it to a cobertura compatible
+# XML file format
+coverage-cobertura: eos-collect-coverage
+ mkdir -p $(_eos_cobertura_coverage_report_path)
+ $(LCOV_RESULT_MERGER) $(_eos_coverage_trace_path)/coverage.lcov $(_eos_cobertura_merged_path)
+ python -c "from lcov_cobertura import LcovCobertura; open(\"$(_eos_cobertura_xml_path)\", \"w\").write(LcovCobertura(open(\"$(_eos_cobertura_merged_path)\", \"r\").read()).convert())"
+'
+], [
+ EOS_COBERTURA_COVERAGE_RULES='
+coverage-cobertura:
+ @echo "Cannot generate Cobertura coverage report as lcov-result-merger or lcov_cobertura was not found in PATH"
+ @exit 1
+'])
+
+ AS_IF([test "x$EOS_ENABLE_JS_COVERAGE" = "xyes"], [
+ EOS_JS_COVERAGE_RULES='
+# First check that all the required variables have been set. This includes:
+# - EOS_JS_COVERAGE_FILES
+
+$(if $(EOS_JS_COVERAGE_FILES),,$(warning Need to define EOS_JS_COVERAGE_FILES))
+
+# Internal variables for the coverage data output path and file
+_eos_js_coverage_trace_path := $(_eos_coverage_trace_path)/js
+_eos_js_coverage_data_output_file := $(_eos_js_coverage_trace_path)/coverage.lcov
+
+# Add _eos_js_coverage_data_output_file to _eos_coverage_outputs
+_eos_coverage_outputs += $(_eos_js_coverage_data_output_file)
+'
+
+ # This small fragment collects all the paths and add the --coverage-path
+ # prefix to each one, finally adding --coverage-output. This makes the list
+ # of flags we will pass to gjs to enable coverage reports.
+ #
+ # This line might make maintainers say "gesundheit!" so here is a
+ # short explainer.
+ #
+ # We're not able to define any functions in the makefile here unless
+ # the user explicitly sets AM_JS_LOG_FLAGS using := (because this
+ # variable will appear at the top of the makefile otherwise). Because
+ # that imposes a very subtle implementation detail on users, the
+ # complexity is hidden here instead.
+ #
+ # The pseudocode for this line looks something like this:
+ # paths = []
+ # foreach (p in EOS_JS_COVERAGE_FILES) {
+ # if (path.replace(':', ' ').split(' ')[0] == 'resource') {
+ # paths.push(p) # resource:// style path, unmodified
+ # } else {
+ # paths.push(absolute_path_to(p)) # Absolute path
+ # }
+ # }
+ #
+ # flags = []
+ # foreach (p in paths) {
+ # flags.push('--coverage-path=' + p)
+ # }
+ #
+ # $(filter resource,$(firstword $(subst :, ,$(p)))) is a Makefile
+ # idiom to implement "strings starts with". It converts the first
+ # argument to subst to a space and then splits the string using
+ # spaces as a delimiter, fetching the first word. Obviously, it
+ # doesn't work very well if your string already has spaces in it
+ # but in this case, we are just looking for the very first
+ # resource:// and nothing else. Once the substring is fetched, it
+ # "filters" it for "resource" (what we are looking for) and if
+ # the substring is exactly "resource" then "resource" is returned,
+ # else an emtpy string.
+ #
+ # Note that $(if cond,consequent,alternative) substitutes consequent
+ # cond evalutes to a non-empty string. The documentation on this
+ # point suggests that conditional operators can be used. This is
+ # misleading.
+ EOS_JS_COVERAGE_LOG_FLAGS='$(addprefix --coverage-path=,$(foreach p,$(EOS_JS_COVERAGE_FILES),$(if $(filter resource,$(firstword $(subst :, ,$(p)))),$(p),$(abspath $(p))))) --coverage-output=$(_eos_js_coverage_trace_path)'
+], [
+ EOS_JS_COVERAGE_RULES=''
+])
+
+ AS_IF([test "x$EOS_ENABLE_C_COVERAGE" = "xyes"], [
+ EOS_C_COVERAGE_RULES='
+# Define internal variables to keep the C coverage counters in
+_eos_c_coverage_trace_path := $(_eos_coverage_trace_path)/c
+_eos_c_coverage_data_output_file := $(_eos_c_coverage_trace_path)/coverage.lcov
+_eos_c_coverage_data_output_tmp_file := $(_eos_c_coverage_data_output_file).tmp
+
+# Add final coverage output file to list of coverage data files
+_eos_coverage_outputs += $(_eos_c_coverage_data_output_file)
+
+# Define an eos-c-coverage target to generate the coverage counters
+eos-c-coverage:
+ mkdir -p $(_eos_c_coverage_trace_path)
+ $(LCOV) --compat-libtool --capture --directory $(abs_top_builddir) -o $(_eos_c_coverage_data_output_tmp_file)
+ $(LCOV) --extract $(_eos_c_coverage_data_output_tmp_file) "$(abs_top_srcdir)/*" -o $(_eos_c_coverage_data_output_file)
+ rm -rf $(_eos_c_coverage_data_output_tmp_file)
+
+eos-clean-c-coverage:
+ find $(abs_top_builddir) -name "*.gcda" -delete
+ find $(abs_top_builddir) -name "*.gcno" -delete
+
+_eos_collect_coverage_targets += eos-c-coverage
+_eos_clean_coverage_targets += eos-clean-c-coverage
+'
+], [
+ EOS_C_COVERAGE_RULES='
+eos-c-coverage:
+ @echo "C coverage reporting not enabled"
+ @exit 1
+
+eos-clean-c-coverage: eos-c-coverage
+'
+])
+
+ EOS_COVERAGE_RULES_FOOTER='
+.PHONY: eos-clean-c-coverage eos-c-coverage clean-coverage eos-collect-coverage coverage-cobertura coverage-genhtml
+ '
+
+ EOS_COVERAGE_CLEAN_RULES='
+clean-local: clean-coverage
+'
+
+ EOS_COVERAGE_RULES="$EOS_COVERAGE_RULES_HEADER $EOS_GENHTML_COVERAGE_RULES $EOS_COBERTURA_COVERAGE_RULES $EOS_C_COVERAGE_RULES $EOS_JS_COVERAGE_RULES $EOS_COVERAGE_RULES_TARGETS $EOS_COVERAGE_RULES_FOOTER"
+
+ # Substitute at the top first
+ AC_SUBST([EOS_COVERAGE_SUBDIR])
+
+ # We only want to define this to use it for full substitution, not as a variable
+ AC_SUBST([EOS_COVERAGE_RULES])
+ AM_SUBST_NOTMAKE([EOS_COVERAGE_RULES])
+
+ AC_SUBST([EOS_JS_COVERAGE_LOG_FLAGS])
+ AC_SUBST(EOS_C_COVERAGE_CFLAGS)
+ AC_SUBST(EOS_C_COVERAGE_LDFLAGS)
+])