Computer Assisted Medical Intervention Tool Kit  version 5.2
CodeCoverage.h
Go to the documentation of this file.
1 # This is an additional CMake modules published on github by Lars Bilke.
2 # see https://github.com/bilke/cmake-modules for more scripts.
3 #
4 # TODO include this to debian/copyright:
5 # ---
6 # Files: sdk/cmake/modules/macros/CodeCoverage.cmake
7 # Copyright: © 2012 - 2017, Lars Bilke
8 # License: BSD-3-clause
9 # Comment: see original sources at https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake
10 # ---
11 #
12 # Copyright (c) 2012 - 2017, Lars Bilke
13 # All rights reserved.
14 #
15 # Redistribution and use in source and binary forms, with or without modification,
16 # are permitted provided that the following conditions are met:
17 #
18 # 1. Redistributions of source code must retain the above copyright notice, this
19 # list of conditions and the following disclaimer.
20 #
21 # 2. Redistributions in binary form must reproduce the above copyright notice,
22 # this list of conditions and the following disclaimer in the documentation
23 # and/or other materials provided with the distribution.
24 #
25 # 3. Neither the name of the copyright holder nor the names of its contributors
26 # may be used to endorse or promote products derived from this software without
27 # specific prior written permission.
28 #
29 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
30 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
32 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
33 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES
35 # LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
36 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 #
40 # CHANGES:
41 #
42 # 2012-01-31, Lars Bilke
43 # - Enable Code Coverage
44 #
45 # 2013-09-17, Joakim Söderberg
46 # - Added support for Clang.
47 # - Some additional usage instructions.
48 #
49 # 2016-02-03, Lars Bilke
50 # - Refactored functions to use named parameters
51 #
52 # 2017-06-02, Lars Bilke
53 # - Merged with modified version from github.com/ufz/ogs
54 #
55 #
56 # USAGE:
57 #
58 # 1. Copy this file into your cmake modules path.
59 #
60 # 2. Add the following line to your CMakeLists.txt:
61 # include(CodeCoverage)
62 #
63 # 3. Append necessary compiler flags:
64 # APPEND_COVERAGE_COMPILER_FLAGS()
65 #
66 # 4. If you need to exclude additional directories from the report, specify them
67 # using the COVERAGE_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE.
68 # Example:
69 # set(COVERAGE_EXCLUDES 'dir1/*' 'dir2/*')
70 #
71 # 5. Use the functions described below to create a custom make target which
72 # runs your test executable and produces a code coverage report.
73 #
74 # 6. Build a Debug build:
75 # cmake -DCMAKE_BUILD_TYPE=Debug ..
76 # make
77 # make my_coverage_target
78 #
79 
80 include(CMakeParseArguments)
81 
82 # Check prereqs
83 find_program( GCOV_PATH gcov )
84 find_program( LCOV_PATH lcov )
85 find_program( GENHTML_PATH genhtml )
86 find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
87 find_program( SIMPLE_PYTHON_EXECUTABLE python )
88 
89 if(NOT GCOV_PATH)
90  message(FATAL_ERROR "gcov not found! Aborting...")
91 endif() # NOT GCOV_PATH
92 
93 if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
94  if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
95  message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
96  endif()
97 elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
98  message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
99 endif()
100 
101 set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage"
102  CACHE INTERNAL "")
103 
104 set(CMAKE_CXX_FLAGS_COVERAGE
105  ${COVERAGE_COMPILER_FLAGS}
106  CACHE STRING "Flags used by the C++ compiler during coverage builds."
107  FORCE )
108 set(CMAKE_C_FLAGS_COVERAGE
109  ${COVERAGE_COMPILER_FLAGS}
110  CACHE STRING "Flags used by the C compiler during coverage builds."
111  FORCE )
112 set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
113  ""
114  CACHE STRING "Flags used for linking binaries during coverage builds."
115  FORCE )
116 set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
117  ""
118  CACHE STRING "Flags used by the shared libraries linker during coverage builds."
119  FORCE )
120 mark_as_advanced(
121  CMAKE_CXX_FLAGS_COVERAGE
122  CMAKE_C_FLAGS_COVERAGE
123  CMAKE_EXE_LINKER_FLAGS_COVERAGE
124  CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
125 
126 if(NOT CMAKE_BUILD_TYPE)
127  # Assume the developer that is running the communityedition test suite compiled everything in Debug
128  message(WARNING "Code coverage: no CMAKE_BUILD_TYPE specified... results with an optimised (non-Debug) build may be misleading")
129 else()
130  # support multiplaform (sometimes the "Debug" type is all uppercase, as on Win32, sometimes it is Camelcase)
131  string(TOUPPER ${CMAKE_BUILD_TYPE} CAMITK_BUILD_TYPE_UPPER)
132  if(NOT CAMITK_BUILD_TYPE_UPPER STREQUAL "DEBUG")
133  message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
134  endif()
135 endif()
136 
137 if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
138  link_libraries(gcov)
139 else()
140  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
141 endif()
142 
143 # Defines a target for running and collection code coverage information
144 # Builds dependencies, runs the given executable and outputs reports.
145 # NOTE! The executable should always have a ZERO as exit code otherwise
146 # the coverage generation will not complete.
147 #
148 # SETUP_TARGET_FOR_COVERAGE(
149 # NAME testrunner_coverage # New target name
150 # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
151 # DEPENDENCIES testrunner # Dependencies to build first
152 # )
153 function(SETUP_TARGET_FOR_COVERAGE)
154 
155  set(options NONE)
156  set(oneValueArgs NAME)
157  set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
158  cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
159 
160  if(NOT LCOV_PATH)
161  message(FATAL_ERROR "lcov not found! Aborting...")
162  endif() # NOT LCOV_PATH
163 
164  if(NOT GENHTML_PATH)
165  message(FATAL_ERROR "genhtml not found! Aborting...")
166  endif() # NOT GENHTML_PATH
167 
168  # Setup target
169  add_custom_target(${Coverage_NAME}
170 
171  # Cleanup lcov
172  COMMAND ${LCOV_PATH} --directory . --zerocounters
173  # Create baseline to make sure untouched files show up in the report
174  COMMAND ${LCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base
175 
176  # Run tests
177  COMMAND ${Coverage_EXECUTABLE}
178 
179  # Capturing lcov counters and generating report
180  COMMAND ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info
181  # add baseline counters
182  COMMAND ${LCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file ${Coverage_NAME}.total
183  COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_EXCLUDES} --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
184  COMMAND ${GENHTML_PATH} --legend -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
185  COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.info ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
186 
187  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
188  DEPENDS ${Coverage_DEPENDENCIES}
189  COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
190  )
191 
192  # Show info where to find the report
193  add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
194  COMMAND
195  COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
196  )
197 
198 endfunction() # SETUP_TARGET_FOR_COVERAGE
199 
200 # Defines a target for running and collection code coverage information
201 # Builds dependencies, runs the given executable and outputs reports.
202 # NOTE! The executable should always have a ZERO as exit code otherwise
203 # the coverage generation will not complete.
204 #
205 # SETUP_TARGET_FOR_COVERAGE_COBERTURA(
206 # NAME ctest_coverage # New target name
207 # EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
208 # DEPENDENCIES executable_target # Dependencies to build first
209 # )
210 function(SETUP_TARGET_FOR_COVERAGE_COBERTURA)
211 
212  set(options NONE)
213  set(oneValueArgs NAME)
214  set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
215  cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
216 
217  if(NOT SIMPLE_PYTHON_EXECUTABLE)
218  message(FATAL_ERROR "python not found! Aborting...")
219  endif() # NOT SIMPLE_PYTHON_EXECUTABLE
220 
221  if(NOT GCOVR_PATH)
222  message(FATAL_ERROR "gcovr not found! Aborting...")
223  endif() # NOT GCOVR_PATH
224 
225  # Combine excludes to several -e arguments
226  set(COBERTURA_EXCLUDES "")
227  foreach(EXCLUDE ${COVERAGE_EXCLUDES})
228  set(COBERTURA_EXCLUDES "-e ${EXCLUDE} ${COBERTURA_EXCLUDES}")
229  endforeach()
230 
231  add_custom_target(${Coverage_NAME}
232 
233  # Run tests
234  ${Coverage_EXECUTABLE}
235 
236  # Running gcovr
237  COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} ${COBERTURA_EXCLUDES}
238  -o ${Coverage_NAME}.xml
239  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
240  DEPENDS ${Coverage_DEPENDENCIES}
241  COMMENT "Running gcovr to produce Cobertura code coverage report."
242  )
243 
244  # Show info where to find the report
245  add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
246  COMMAND
247  COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
248  )
249 
250 endfunction() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
251 
252 function(APPEND_COVERAGE_COMPILER_FLAGS)
253  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
254  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
255 endfunction() # APPEND_COVERAGE_COMPILER_FLAGS
256