Warning, /rolisteam/rolisteam/cmake/CodeCoverage.cmake is written in an unsupported language. File is not indexed.
0001 # Copyright (c) 2012 - 2017, Lars Bilke 0002 # All rights reserved. 0003 # 0004 # Redistribution and use in source and binary forms, with or without modification, 0005 # are permitted provided that the following conditions are met: 0006 # 0007 # 1. Redistributions of source code must retain the above copyright notice, this 0008 # list of conditions and the following disclaimer. 0009 # 0010 # 2. Redistributions in binary form must reproduce the above copyright notice, 0011 # this list of conditions and the following disclaimer in the documentation 0012 # and/or other materials provided with the distribution. 0013 # 0014 # 3. Neither the name of the copyright holder nor the names of its contributors 0015 # may be used to endorse or promote products derived from this software without 0016 # specific prior written permission. 0017 # 0018 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 0019 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 0020 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 0021 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 0022 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 0023 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 0024 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 0025 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 0026 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 0027 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 0028 # 0029 # CHANGES: 0030 # 0031 # 2012-01-31, Lars Bilke 0032 # - Enable Code Coverage 0033 # 0034 # 2013-09-17, Joakim Söderberg 0035 # - Added support for Clang. 0036 # - Some additional usage instructions. 0037 # 0038 # 2016-02-03, Lars Bilke 0039 # - Refactored functions to use named parameters 0040 # 0041 # 2017-06-02, Lars Bilke 0042 # - Merged with modified version from github.com/ufz/ogs 0043 # 0044 # 2019-05-06, Anatolii Kurotych 0045 # - Remove unnecessary --coverage flag 0046 # 0047 # 2019-12-13, FeRD (Frank Dana) 0048 # - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor 0049 # of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. 0050 # - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY 0051 # - All setup functions: accept BASE_DIRECTORY, EXCLUDE list 0052 # - Set lcov basedir with -b argument 0053 # - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be 0054 # overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) 0055 # - Delete output dir, .info file on 'make clean' 0056 # - Remove Python detection, since version mismatches will break gcovr 0057 # - Minor cleanup (lowercase function names, update examples...) 0058 # 0059 # 2019-12-19, FeRD (Frank Dana) 0060 # - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets 0061 # 0062 # 2020-01-19, Bob Apthorpe 0063 # - Added gfortran support 0064 # 0065 # 2020-02-17, FeRD (Frank Dana) 0066 # - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters 0067 # in EXCLUDEs, and remove manual escaping from gcovr targets 0068 # 0069 # 2021-01-19, Robin Mueller 0070 # - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run 0071 # - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional 0072 # flags to the gcovr command 0073 # 0074 # 2020-05-04, Mihchael Davis 0075 # - Add -fprofile-abs-path to make gcno files contain absolute paths 0076 # - Fix BASE_DIRECTORY not working when defined 0077 # - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines 0078 # 0079 # 2021-05-10, Martin Stump 0080 # - Check if the generator is multi-config before warning about non-Debug builds 0081 # 0082 # 2022-02-22, Marko Wehle 0083 # - Change gcovr output from -o <filename> for --xml <filename> and --html <filename> output respectively. 0084 # This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt". 0085 # 0086 # USAGE: 0087 # 0088 # 1. Copy this file into your cmake modules path. 0089 # 0090 # 2. Add the following line to your CMakeLists.txt (best inside an if-condition 0091 # using a CMake option() to enable it just optionally): 0092 # include(CodeCoverage) 0093 # 0094 # 3. Append necessary compiler flags for all supported source files: 0095 # append_coverage_compiler_flags() 0096 # Or for specific target: 0097 # append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME) 0098 # 0099 # 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og 0100 # 0101 # 4. If you need to exclude additional directories from the report, specify them 0102 # using full paths in the COVERAGE_EXCLUDES variable before calling 0103 # setup_target_for_coverage_*(). 0104 # Example: 0105 # set(COVERAGE_EXCLUDES 0106 # '${PROJECT_SOURCE_DIR}/src/dir1/*' 0107 # '/path/to/my/src/dir2/*') 0108 # Or, use the EXCLUDE argument to setup_target_for_coverage_*(). 0109 # Example: 0110 # setup_target_for_coverage_lcov( 0111 # NAME coverage 0112 # EXECUTABLE testrunner 0113 # EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") 0114 # 0115 # 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set 0116 # relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) 0117 # Example: 0118 # set(COVERAGE_EXCLUDES "dir1/*") 0119 # setup_target_for_coverage_gcovr_html( 0120 # NAME coverage 0121 # EXECUTABLE testrunner 0122 # BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" 0123 # EXCLUDE "dir2/*") 0124 # 0125 # 5. Use the functions described below to create a custom make target which 0126 # runs your test executable and produces a code coverage report. 0127 # 0128 # 6. Build a Debug build: 0129 # cmake -DCMAKE_BUILD_TYPE=Debug .. 0130 # make 0131 # make my_coverage_target 0132 # 0133 0134 include(CMakeParseArguments) 0135 0136 option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE) 0137 0138 # Check prereqs 0139 find_program( GCOV_PATH gcov ) 0140 find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) 0141 find_program( FASTCOV_PATH NAMES fastcov fastcov.py ) 0142 find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) 0143 find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) 0144 find_program( CPPFILT_PATH NAMES c++filt ) 0145 0146 if(NOT GCOV_PATH) 0147 message(FATAL_ERROR "gcov not found! Aborting...") 0148 endif() # NOT GCOV_PATH 0149 0150 get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) 0151 list(GET LANGUAGES 0 LANG) 0152 0153 if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") 0154 if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3) 0155 message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") 0156 endif() 0157 elseif(NOT CMAKE_COMPILER_IS_GNUCXX) 0158 if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang") 0159 # Do nothing; exit conditional without error if true 0160 elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") 0161 # Do nothing; exit conditional without error if true 0162 else() 0163 message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") 0164 endif() 0165 endif() 0166 0167 set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" 0168 CACHE INTERNAL "") 0169 if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") 0170 include(CheckCXXCompilerFlag) 0171 check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path) 0172 if(HAVE_fprofile_abs_path) 0173 set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") 0174 endif() 0175 endif() 0176 0177 set(CMAKE_Fortran_FLAGS_COVERAGE 0178 ${COVERAGE_COMPILER_FLAGS} 0179 CACHE STRING "Flags used by the Fortran compiler during coverage builds." 0180 FORCE ) 0181 set(CMAKE_CXX_FLAGS_COVERAGE 0182 ${COVERAGE_COMPILER_FLAGS} 0183 CACHE STRING "Flags used by the C++ compiler during coverage builds." 0184 FORCE ) 0185 set(CMAKE_C_FLAGS_COVERAGE 0186 ${COVERAGE_COMPILER_FLAGS} 0187 CACHE STRING "Flags used by the C compiler during coverage builds." 0188 FORCE ) 0189 set(CMAKE_EXE_LINKER_FLAGS_COVERAGE 0190 "" 0191 CACHE STRING "Flags used for linking binaries during coverage builds." 0192 FORCE ) 0193 set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE 0194 "" 0195 CACHE STRING "Flags used by the shared libraries linker during coverage builds." 0196 FORCE ) 0197 mark_as_advanced( 0198 CMAKE_Fortran_FLAGS_COVERAGE 0199 CMAKE_CXX_FLAGS_COVERAGE 0200 CMAKE_C_FLAGS_COVERAGE 0201 CMAKE_EXE_LINKER_FLAGS_COVERAGE 0202 CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) 0203 0204 get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 0205 if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)) 0206 message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") 0207 endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG) 0208 0209 if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") 0210 link_libraries(gcov) 0211 endif() 0212 0213 # Defines a target for running and collection code coverage information 0214 # Builds dependencies, runs the given executable and outputs reports. 0215 # NOTE! The executable should always have a ZERO as exit code otherwise 0216 # the coverage generation will not complete. 0217 # 0218 # setup_target_for_coverage_lcov( 0219 # NAME testrunner_coverage # New target name 0220 # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR 0221 # DEPENDENCIES testrunner # Dependencies to build first 0222 # BASE_DIRECTORY "../" # Base directory for report 0223 # # (defaults to PROJECT_SOURCE_DIR) 0224 # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative 0225 # # to BASE_DIRECTORY, with CMake 3.4+) 0226 # NO_DEMANGLE # Don't demangle C++ symbols 0227 # # even if c++filt is found 0228 # ) 0229 function(setup_target_for_coverage_lcov) 0230 0231 set(options NO_DEMANGLE) 0232 set(oneValueArgs BASE_DIRECTORY NAME) 0233 set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) 0234 cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 0235 0236 if(NOT LCOV_PATH) 0237 message(FATAL_ERROR "lcov not found! Aborting...") 0238 endif() # NOT LCOV_PATH 0239 0240 if(NOT GENHTML_PATH) 0241 message(FATAL_ERROR "genhtml not found! Aborting...") 0242 endif() # NOT GENHTML_PATH 0243 0244 # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR 0245 if(DEFINED Coverage_BASE_DIRECTORY) 0246 get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) 0247 else() 0248 set(BASEDIR ${PROJECT_SOURCE_DIR}) 0249 endif() 0250 0251 # Collect excludes (CMake 3.4+: Also compute absolute paths) 0252 set(LCOV_EXCLUDES "") 0253 foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) 0254 if(CMAKE_VERSION VERSION_GREATER 3.4) 0255 get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) 0256 endif() 0257 list(APPEND LCOV_EXCLUDES "${EXCLUDE}") 0258 endforeach() 0259 list(REMOVE_DUPLICATES LCOV_EXCLUDES) 0260 0261 # Conditional arguments 0262 if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) 0263 set(GENHTML_EXTRA_ARGS "--demangle-cpp") 0264 endif() 0265 0266 # Setting up commands which will be run to generate coverage data. 0267 # Cleanup lcov 0268 set(LCOV_CLEAN_CMD 0269 ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . 0270 -b ${BASEDIR} --zerocounters 0271 ) 0272 # Create baseline to make sure untouched files show up in the report 0273 set(LCOV_BASELINE_CMD 0274 ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b 0275 ${BASEDIR} -o ${Coverage_NAME}.base 0276 ) 0277 # Run tests 0278 set(LCOV_EXEC_TESTS_CMD 0279 ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} 0280 ) 0281 # Capturing lcov counters and generating report 0282 set(LCOV_CAPTURE_CMD 0283 ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b 0284 ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture 0285 ) 0286 # add baseline counters 0287 set(LCOV_BASELINE_COUNT_CMD 0288 ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base 0289 -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total 0290 ) 0291 # filter collected data to final coverage report 0292 set(LCOV_FILTER_CMD 0293 ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove 0294 ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info 0295 ) 0296 # Generate HTML output 0297 set(LCOV_GEN_HTML_CMD 0298 ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o 0299 ${Coverage_NAME} ${Coverage_NAME}.info 0300 ) 0301 0302 0303 if(CODE_COVERAGE_VERBOSE) 0304 message(STATUS "Executed command report") 0305 message(STATUS "Command to clean up lcov: ") 0306 string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}") 0307 message(STATUS "${LCOV_CLEAN_CMD_SPACED}") 0308 0309 message(STATUS "Command to create baseline: ") 0310 string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}") 0311 message(STATUS "${LCOV_BASELINE_CMD_SPACED}") 0312 0313 message(STATUS "Command to run the tests: ") 0314 string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}") 0315 message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}") 0316 0317 message(STATUS "Command to capture counters and generate report: ") 0318 string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}") 0319 message(STATUS "${LCOV_CAPTURE_CMD_SPACED}") 0320 0321 message(STATUS "Command to add baseline counters: ") 0322 string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}") 0323 message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}") 0324 0325 message(STATUS "Command to filter collected data: ") 0326 string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}") 0327 message(STATUS "${LCOV_FILTER_CMD_SPACED}") 0328 0329 message(STATUS "Command to generate lcov HTML output: ") 0330 string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}") 0331 message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}") 0332 endif() 0333 0334 # Setup target 0335 add_custom_target(${Coverage_NAME} 0336 COMMAND ${LCOV_CLEAN_CMD} 0337 COMMAND ${LCOV_BASELINE_CMD} 0338 COMMAND ${LCOV_EXEC_TESTS_CMD} 0339 COMMAND ${LCOV_CAPTURE_CMD} 0340 COMMAND ${LCOV_BASELINE_COUNT_CMD} 0341 COMMAND ${LCOV_FILTER_CMD} 0342 COMMAND ${LCOV_GEN_HTML_CMD} 0343 0344 # Set output files as GENERATED (will be removed on 'make clean') 0345 BYPRODUCTS 0346 ${Coverage_NAME}.base 0347 ${Coverage_NAME}.capture 0348 ${Coverage_NAME}.total 0349 ${Coverage_NAME}.info 0350 ${Coverage_NAME}/index.html 0351 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 0352 DEPENDS ${Coverage_DEPENDENCIES} 0353 VERBATIM # Protect arguments to commands 0354 COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." 0355 ) 0356 0357 # Show where to find the lcov info report 0358 add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 0359 COMMAND ; 0360 COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." 0361 ) 0362 0363 # Show info where to find the report 0364 add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 0365 COMMAND ; 0366 COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." 0367 ) 0368 0369 endfunction() # setup_target_for_coverage_lcov 0370 0371 # Defines a target for running and collection code coverage information 0372 # Builds dependencies, runs the given executable and outputs reports. 0373 # NOTE! The executable should always have a ZERO as exit code otherwise 0374 # the coverage generation will not complete. 0375 # 0376 # setup_target_for_coverage_gcovr_xml( 0377 # NAME ctest_coverage # New target name 0378 # EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR 0379 # DEPENDENCIES executable_target # Dependencies to build first 0380 # BASE_DIRECTORY "../" # Base directory for report 0381 # # (defaults to PROJECT_SOURCE_DIR) 0382 # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative 0383 # # to BASE_DIRECTORY, with CMake 3.4+) 0384 # ) 0385 # The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the 0386 # GCVOR command. 0387 function(setup_target_for_coverage_gcovr_xml) 0388 0389 set(options NONE) 0390 set(oneValueArgs BASE_DIRECTORY NAME) 0391 set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) 0392 cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 0393 0394 if(NOT GCOVR_PATH) 0395 message(FATAL_ERROR "gcovr not found! Aborting...") 0396 endif() # NOT GCOVR_PATH 0397 0398 # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR 0399 if(DEFINED Coverage_BASE_DIRECTORY) 0400 get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) 0401 else() 0402 set(BASEDIR ${PROJECT_SOURCE_DIR}) 0403 endif() 0404 0405 # Collect excludes (CMake 3.4+: Also compute absolute paths) 0406 set(GCOVR_EXCLUDES "") 0407 foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) 0408 if(CMAKE_VERSION VERSION_GREATER 3.4) 0409 get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) 0410 endif() 0411 list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") 0412 endforeach() 0413 list(REMOVE_DUPLICATES GCOVR_EXCLUDES) 0414 0415 # Combine excludes to several -e arguments 0416 set(GCOVR_EXCLUDE_ARGS "") 0417 foreach(EXCLUDE ${GCOVR_EXCLUDES}) 0418 list(APPEND GCOVR_EXCLUDE_ARGS "-e") 0419 list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") 0420 endforeach() 0421 0422 # Set up commands which will be run to generate coverage data 0423 # Run tests 0424 set(GCOVR_XML_EXEC_TESTS_CMD 0425 ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} 0426 ) 0427 # Running gcovr 0428 set(GCOVR_XML_CMD 0429 ${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} 0430 ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} 0431 ) 0432 0433 if(CODE_COVERAGE_VERBOSE) 0434 message(STATUS "Executed command report") 0435 0436 message(STATUS "Command to run tests: ") 0437 string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}") 0438 message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}") 0439 0440 message(STATUS "Command to generate gcovr XML coverage data: ") 0441 string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") 0442 message(STATUS "${GCOVR_XML_CMD_SPACED}") 0443 endif() 0444 0445 add_custom_target(${Coverage_NAME} 0446 COMMAND ${GCOVR_XML_EXEC_TESTS_CMD} 0447 COMMAND ${GCOVR_XML_CMD} 0448 0449 BYPRODUCTS ${Coverage_NAME}.xml 0450 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 0451 DEPENDS ${Coverage_DEPENDENCIES} 0452 VERBATIM # Protect arguments to commands 0453 COMMENT "Running gcovr to produce Cobertura code coverage report." 0454 ) 0455 0456 # Show info where to find the report 0457 add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 0458 COMMAND ; 0459 COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." 0460 ) 0461 endfunction() # setup_target_for_coverage_gcovr_xml 0462 0463 # Defines a target for running and collection code coverage information 0464 # Builds dependencies, runs the given executable and outputs reports. 0465 # NOTE! The executable should always have a ZERO as exit code otherwise 0466 # the coverage generation will not complete. 0467 # 0468 # setup_target_for_coverage_gcovr_html( 0469 # NAME ctest_coverage # New target name 0470 # EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR 0471 # DEPENDENCIES executable_target # Dependencies to build first 0472 # BASE_DIRECTORY "../" # Base directory for report 0473 # # (defaults to PROJECT_SOURCE_DIR) 0474 # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative 0475 # # to BASE_DIRECTORY, with CMake 3.4+) 0476 # ) 0477 # The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the 0478 # GCVOR command. 0479 function(setup_target_for_coverage_gcovr_html) 0480 0481 set(options NONE) 0482 set(oneValueArgs BASE_DIRECTORY NAME) 0483 set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) 0484 cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 0485 0486 if(NOT GCOVR_PATH) 0487 message(FATAL_ERROR "gcovr not found! Aborting...") 0488 endif() # NOT GCOVR_PATH 0489 0490 # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR 0491 if(DEFINED Coverage_BASE_DIRECTORY) 0492 get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) 0493 else() 0494 set(BASEDIR ${PROJECT_SOURCE_DIR}) 0495 endif() 0496 0497 # Collect excludes (CMake 3.4+: Also compute absolute paths) 0498 set(GCOVR_EXCLUDES "") 0499 foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) 0500 if(CMAKE_VERSION VERSION_GREATER 3.4) 0501 get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) 0502 endif() 0503 list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") 0504 endforeach() 0505 list(REMOVE_DUPLICATES GCOVR_EXCLUDES) 0506 0507 # Combine excludes to several -e arguments 0508 set(GCOVR_EXCLUDE_ARGS "") 0509 foreach(EXCLUDE ${GCOVR_EXCLUDES}) 0510 list(APPEND GCOVR_EXCLUDE_ARGS "-e") 0511 list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") 0512 endforeach() 0513 0514 # Set up commands which will be run to generate coverage data 0515 # Run tests 0516 set(GCOVR_HTML_EXEC_TESTS_CMD 0517 ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} 0518 ) 0519 # Create folder 0520 set(GCOVR_HTML_FOLDER_CMD 0521 ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} 0522 ) 0523 # Running gcovr 0524 set(GCOVR_HTML_CMD 0525 ${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} 0526 ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} 0527 ) 0528 0529 if(CODE_COVERAGE_VERBOSE) 0530 message(STATUS "Executed command report") 0531 0532 message(STATUS "Command to run tests: ") 0533 string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}") 0534 message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}") 0535 0536 message(STATUS "Command to create a folder: ") 0537 string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}") 0538 message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}") 0539 0540 message(STATUS "Command to generate gcovr HTML coverage data: ") 0541 string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}") 0542 message(STATUS "${GCOVR_HTML_CMD_SPACED}") 0543 endif() 0544 0545 add_custom_target(${Coverage_NAME} 0546 COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD} 0547 COMMAND ${GCOVR_HTML_FOLDER_CMD} 0548 COMMAND ${GCOVR_HTML_CMD} 0549 0550 BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory 0551 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 0552 DEPENDS ${Coverage_DEPENDENCIES} 0553 VERBATIM # Protect arguments to commands 0554 COMMENT "Running gcovr to produce HTML code coverage report." 0555 ) 0556 0557 # Show info where to find the report 0558 add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 0559 COMMAND ; 0560 COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." 0561 ) 0562 0563 endfunction() # setup_target_for_coverage_gcovr_html 0564 0565 # Defines a target for running and collection code coverage information 0566 # Builds dependencies, runs the given executable and outputs reports. 0567 # NOTE! The executable should always have a ZERO as exit code otherwise 0568 # the coverage generation will not complete. 0569 # 0570 # setup_target_for_coverage_fastcov( 0571 # NAME testrunner_coverage # New target name 0572 # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR 0573 # DEPENDENCIES testrunner # Dependencies to build first 0574 # BASE_DIRECTORY "../" # Base directory for report 0575 # # (defaults to PROJECT_SOURCE_DIR) 0576 # EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude. 0577 # NO_DEMANGLE # Don't demangle C++ symbols 0578 # # even if c++filt is found 0579 # SKIP_HTML # Don't create html report 0580 # POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths 0581 # ) 0582 function(setup_target_for_coverage_fastcov) 0583 0584 set(options NO_DEMANGLE SKIP_HTML) 0585 set(oneValueArgs BASE_DIRECTORY NAME) 0586 set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD) 0587 cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 0588 0589 if(NOT FASTCOV_PATH) 0590 message(FATAL_ERROR "fastcov not found! Aborting...") 0591 endif() 0592 0593 if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH) 0594 message(FATAL_ERROR "genhtml not found! Aborting...") 0595 endif() 0596 0597 # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR 0598 if(Coverage_BASE_DIRECTORY) 0599 get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) 0600 else() 0601 set(BASEDIR ${PROJECT_SOURCE_DIR}) 0602 endif() 0603 0604 # Collect excludes (Patterns, not paths, for fastcov) 0605 set(FASTCOV_EXCLUDES "") 0606 foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES}) 0607 list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}") 0608 endforeach() 0609 list(REMOVE_DUPLICATES FASTCOV_EXCLUDES) 0610 0611 # Conditional arguments 0612 if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) 0613 set(GENHTML_EXTRA_ARGS "--demangle-cpp") 0614 endif() 0615 0616 # Set up commands which will be run to generate coverage data 0617 set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) 0618 0619 set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} 0620 --search-directory ${BASEDIR} 0621 --process-gcno 0622 --output ${Coverage_NAME}.json 0623 --exclude ${FASTCOV_EXCLUDES} 0624 --exclude ${FASTCOV_EXCLUDES} 0625 ) 0626 0627 set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH} 0628 -C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info 0629 ) 0630 0631 if(Coverage_SKIP_HTML) 0632 set(FASTCOV_HTML_CMD ";") 0633 else() 0634 set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} 0635 -o ${Coverage_NAME} ${Coverage_NAME}.info 0636 ) 0637 endif() 0638 0639 set(FASTCOV_POST_CMD ";") 0640 if(Coverage_POST_CMD) 0641 set(FASTCOV_POST_CMD ${Coverage_POST_CMD}) 0642 endif() 0643 0644 if(CODE_COVERAGE_VERBOSE) 0645 message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):") 0646 0647 message(" Running tests:") 0648 string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}") 0649 message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}") 0650 0651 message(" Capturing fastcov counters and generating report:") 0652 string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}") 0653 message(" ${FASTCOV_CAPTURE_CMD_SPACED}") 0654 0655 message(" Converting fastcov .json to lcov .info:") 0656 string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}") 0657 message(" ${FASTCOV_CONVERT_CMD_SPACED}") 0658 0659 if(NOT Coverage_SKIP_HTML) 0660 message(" Generating HTML report: ") 0661 string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}") 0662 message(" ${FASTCOV_HTML_CMD_SPACED}") 0663 endif() 0664 if(Coverage_POST_CMD) 0665 message(" Running post command: ") 0666 string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}") 0667 message(" ${FASTCOV_POST_CMD_SPACED}") 0668 endif() 0669 endif() 0670 0671 # Setup target 0672 add_custom_target(${Coverage_NAME} 0673 0674 # Cleanup fastcov 0675 COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} 0676 --search-directory ${BASEDIR} 0677 --zerocounters 0678 0679 COMMAND ${FASTCOV_EXEC_TESTS_CMD} 0680 COMMAND ${FASTCOV_CAPTURE_CMD} 0681 COMMAND ${FASTCOV_CONVERT_CMD} 0682 COMMAND ${FASTCOV_HTML_CMD} 0683 COMMAND ${FASTCOV_POST_CMD} 0684 0685 # Set output files as GENERATED (will be removed on 'make clean') 0686 BYPRODUCTS 0687 ${Coverage_NAME}.info 0688 ${Coverage_NAME}.json 0689 ${Coverage_NAME}/index.html # report directory 0690 0691 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 0692 DEPENDS ${Coverage_DEPENDENCIES} 0693 VERBATIM # Protect arguments to commands 0694 COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report." 0695 ) 0696 0697 set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.") 0698 if(NOT Coverage_SKIP_HTML) 0699 string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.") 0700 endif() 0701 # Show where to find the fastcov info report 0702 add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 0703 COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG} 0704 ) 0705 0706 endfunction() # setup_target_for_coverage_fastcov 0707 0708 function(append_coverage_compiler_flags) 0709 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 0710 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 0711 set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 0712 message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") 0713 endfunction() # append_coverage_compiler_flags 0714 0715 # Setup coverage for specific library 0716 function(append_coverage_compiler_flags_to_target name) 0717 separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}") 0718 target_compile_options(${name} 0719 PRIVATE ${_flag_list}) 0720 target_link_libraries(${name} PRIVATE gcov) 0721 endfunction()