Warning, /sdk/codevis/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 # USAGE:
0083 #
0084 # 1. Copy this file into your cmake modules path.
0085 #
0086 # 2. Add the following line to your CMakeLists.txt (best inside an if-condition
0087 #    using a CMake option() to enable it just optionally):
0088 #      include(CodeCoverage)
0089 #
0090 # 3. Append necessary compiler flags:
0091 #      append_coverage_compiler_flags()
0092 #
0093 # 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
0094 #
0095 # 4. If you need to exclude additional directories from the report, specify them
0096 #    using full paths in the COVERAGE_EXCLUDES variable before calling
0097 #    setup_target_for_coverage_*().
0098 #    Example:
0099 #      set(COVERAGE_EXCLUDES
0100 #          '${PROJECT_SOURCE_DIR}/src/dir1/*'
0101 #          '/path/to/my/src/dir2/*')
0102 #    Or, use the EXCLUDE argument to setup_target_for_coverage_*().
0103 #    Example:
0104 #      setup_target_for_coverage_lcov(
0105 #          NAME coverage
0106 #          EXECUTABLE testrunner
0107 #          EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
0108 #
0109 # 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
0110 #     relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
0111 #     Example:
0112 #       set(COVERAGE_EXCLUDES "dir1/*")
0113 #       setup_target_for_coverage_gcovr_html(
0114 #           NAME coverage
0115 #           EXECUTABLE testrunner
0116 #           BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
0117 #           EXCLUDE "dir2/*")
0118 #
0119 # 5. Use the functions described below to create a custom make target which
0120 #    runs your test executable and produces a code coverage report.
0121 #
0122 # 6. Build a Debug build:
0123 #      cmake -DCMAKE_BUILD_TYPE=Debug ..
0124 #      make
0125 #      make my_coverage_target
0126 #
0127 
0128 include(CMakeParseArguments)
0129 
0130 option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
0131 
0132 # Check prereqs
0133 find_program( GCOV_PATH gcov )
0134 find_program( LCOV_PATH  NAMES lcov lcov.bat lcov.exe lcov.perl)
0135 find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
0136 find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
0137 find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
0138 find_program( CPPFILT_PATH NAMES c++filt )
0139 
0140 if(NOT GCOV_PATH)
0141     message(FATAL_ERROR "gcov not found! Aborting...")
0142 endif() # NOT GCOV_PATH
0143 
0144 get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
0145 list(GET LANGUAGES 0 LANG)
0146 
0147 if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
0148     if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
0149         message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
0150     endif()
0151 elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
0152     if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang")
0153         # Do nothing; exit conditional without error if true
0154     elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
0155         # Do nothing; exit conditional without error if true
0156     else()
0157         message("Current Compiler: ${CMAKE_CXX_COMPILER_ID}")
0158         message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
0159     endif()
0160 endif()
0161 
0162 set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
0163     CACHE INTERNAL "")
0164 if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
0165     include(CheckCXXCompilerFlag)
0166     check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
0167     if(HAVE_fprofile_abs_path)
0168         set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
0169     endif()
0170 endif()
0171 
0172 set(CMAKE_Fortran_FLAGS_COVERAGE
0173     ${COVERAGE_COMPILER_FLAGS}
0174     CACHE STRING "Flags used by the Fortran compiler during coverage builds."
0175     FORCE )
0176 set(CMAKE_CXX_FLAGS_COVERAGE
0177     ${COVERAGE_COMPILER_FLAGS}
0178     CACHE STRING "Flags used by the C++ compiler during coverage builds."
0179     FORCE )
0180 set(CMAKE_C_FLAGS_COVERAGE
0181     ${COVERAGE_COMPILER_FLAGS}
0182     CACHE STRING "Flags used by the C compiler during coverage builds."
0183     FORCE )
0184 set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
0185     ""
0186     CACHE STRING "Flags used for linking binaries during coverage builds."
0187     FORCE )
0188 set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
0189     ""
0190     CACHE STRING "Flags used by the shared libraries linker during coverage builds."
0191     FORCE )
0192 mark_as_advanced(
0193     CMAKE_Fortran_FLAGS_COVERAGE
0194     CMAKE_CXX_FLAGS_COVERAGE
0195     CMAKE_C_FLAGS_COVERAGE
0196     CMAKE_EXE_LINKER_FLAGS_COVERAGE
0197     CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
0198 
0199 get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
0200 if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
0201     message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
0202 endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
0203 
0204 if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
0205     link_libraries(gcov)
0206 endif()
0207 
0208 # Defines a target for running and collection code coverage information
0209 # Builds dependencies, runs the given executable and outputs reports.
0210 # NOTE! The executable should always have a ZERO as exit code otherwise
0211 # the coverage generation will not complete.
0212 #
0213 # setup_target_for_coverage_lcov(
0214 #     NAME testrunner_coverage                    # New target name
0215 #     EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
0216 #     DEPENDENCIES testrunner                     # Dependencies to build first
0217 #     BASE_DIRECTORY "../"                        # Base directory for report
0218 #                                                 #  (defaults to PROJECT_SOURCE_DIR)
0219 #     EXCLUDE "src/dir1/*" "src/dir2/*"           # Patterns to exclude (can be relative
0220 #                                                 #  to BASE_DIRECTORY, with CMake 3.4+)
0221 #     NO_DEMANGLE                                 # Don't demangle C++ symbols
0222 #                                                 #  even if c++filt is found
0223 # )
0224 function(setup_target_for_coverage_lcov)
0225 
0226     set(options NO_DEMANGLE)
0227     set(oneValueArgs BASE_DIRECTORY NAME)
0228     set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
0229     cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
0230 
0231     if(NOT LCOV_PATH)
0232         message(FATAL_ERROR "lcov not found! Aborting...")
0233     endif() # NOT LCOV_PATH
0234 
0235     if(NOT GENHTML_PATH)
0236         message(FATAL_ERROR "genhtml not found! Aborting...")
0237     endif() # NOT GENHTML_PATH
0238 
0239     # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
0240     if(DEFINED Coverage_BASE_DIRECTORY)
0241         get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
0242     else()
0243         set(BASEDIR ${PROJECT_SOURCE_DIR})
0244     endif()
0245 
0246     # Collect excludes (CMake 3.4+: Also compute absolute paths)
0247     set(LCOV_EXCLUDES "")
0248     foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
0249         if(CMAKE_VERSION VERSION_GREATER 3.4)
0250             get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
0251         endif()
0252         list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
0253     endforeach()
0254     list(REMOVE_DUPLICATES LCOV_EXCLUDES)
0255 
0256     # Conditional arguments
0257     if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
0258       set(GENHTML_EXTRA_ARGS "--demangle-cpp")
0259     endif()
0260      
0261     # Setting up commands which will be run to generate coverage data.
0262     # Cleanup lcov
0263     set(LCOV_CLEAN_CMD 
0264         ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . 
0265         -b ${BASEDIR} --zerocounters
0266     )
0267     # Create baseline to make sure untouched files show up in the report
0268     set(LCOV_BASELINE_CMD 
0269         ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b 
0270         ${BASEDIR} -o ${Coverage_NAME}.base
0271     )
0272     # Run tests
0273     set(LCOV_EXEC_TESTS_CMD 
0274         ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
0275     )    
0276     # Capturing lcov counters and generating report
0277     set(LCOV_CAPTURE_CMD 
0278         ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b 
0279         ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
0280     )
0281     # add baseline counters
0282     set(LCOV_BASELINE_COUNT_CMD
0283         ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base 
0284         -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
0285     ) 
0286     # filter collected data to final coverage report
0287     set(LCOV_FILTER_CMD 
0288         ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove 
0289         ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
0290     )    
0291     # Generate HTML output
0292     set(LCOV_GEN_HTML_CMD
0293         ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o 
0294         ${Coverage_NAME} ${Coverage_NAME}.info
0295     )
0296     
0297 
0298     if(CODE_COVERAGE_VERBOSE)
0299         message(STATUS "Executed command report")
0300         message(STATUS "Command to clean up lcov: ")
0301         string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
0302         message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
0303 
0304         message(STATUS "Command to create baseline: ")
0305         string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
0306         message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
0307 
0308         message(STATUS "Command to run the tests: ")
0309         string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
0310         message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
0311 
0312         message(STATUS "Command to capture counters and generate report: ")
0313         string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
0314         message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
0315 
0316         message(STATUS "Command to add baseline counters: ")
0317         string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
0318         message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
0319 
0320         message(STATUS "Command to filter collected data: ")
0321         string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
0322         message(STATUS "${LCOV_FILTER_CMD_SPACED}")
0323 
0324         message(STATUS "Command to generate lcov HTML output: ")
0325         string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
0326         message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
0327     endif()
0328 
0329     # Setup target
0330     add_custom_target(${Coverage_NAME}
0331         COMMAND ${LCOV_CLEAN_CMD}
0332         COMMAND ${LCOV_BASELINE_CMD} 
0333         COMMAND ${LCOV_EXEC_TESTS_CMD}
0334         COMMAND ${LCOV_CAPTURE_CMD}
0335         COMMAND ${LCOV_BASELINE_COUNT_CMD}
0336         COMMAND ${LCOV_FILTER_CMD} 
0337         COMMAND ${LCOV_GEN_HTML_CMD}
0338 
0339         # Set output files as GENERATED (will be removed on 'make clean')
0340         BYPRODUCTS
0341             ${Coverage_NAME}.base
0342             ${Coverage_NAME}.capture
0343             ${Coverage_NAME}.total
0344             ${Coverage_NAME}.info
0345             ${Coverage_NAME}/index.html
0346         WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
0347         DEPENDS ${Coverage_DEPENDENCIES}
0348         VERBATIM # Protect arguments to commands
0349         COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
0350     )
0351 
0352     # Show where to find the lcov info report
0353     add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
0354         COMMAND ;
0355         COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
0356     )
0357 
0358     # Show info where to find the report
0359     add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
0360         COMMAND ;
0361         COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
0362     )
0363 
0364 endfunction() # setup_target_for_coverage_lcov
0365 
0366 # Defines a target for running and collection code coverage information
0367 # Builds dependencies, runs the given executable and outputs reports.
0368 # NOTE! The executable should always have a ZERO as exit code otherwise
0369 # the coverage generation will not complete.
0370 #
0371 # setup_target_for_coverage_gcovr_xml(
0372 #     NAME ctest_coverage                    # New target name
0373 #     EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
0374 #     DEPENDENCIES executable_target         # Dependencies to build first
0375 #     BASE_DIRECTORY "../"                   # Base directory for report
0376 #                                            #  (defaults to PROJECT_SOURCE_DIR)
0377 #     EXCLUDE "src/dir1/*" "src/dir2/*"      # Patterns to exclude (can be relative
0378 #                                            #  to BASE_DIRECTORY, with CMake 3.4+)
0379 # )
0380 # The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
0381 # GCVOR command.
0382 function(setup_target_for_coverage_gcovr_xml)
0383 
0384     set(options NONE)
0385     set(oneValueArgs BASE_DIRECTORY NAME)
0386     set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
0387     cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
0388 
0389     if(NOT GCOVR_PATH)
0390         message(FATAL_ERROR "gcovr not found! Aborting...")
0391     endif() # NOT GCOVR_PATH
0392 
0393     # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
0394     if(DEFINED Coverage_BASE_DIRECTORY)
0395         get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
0396     else()
0397         set(BASEDIR ${PROJECT_SOURCE_DIR})
0398     endif()
0399 
0400     # Collect excludes (CMake 3.4+: Also compute absolute paths)
0401     set(GCOVR_EXCLUDES "")
0402     foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
0403         if(CMAKE_VERSION VERSION_GREATER 3.4)
0404             get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
0405         endif()
0406         list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
0407     endforeach()
0408     list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
0409 
0410     # Combine excludes to several -e arguments
0411     set(GCOVR_EXCLUDE_ARGS "")
0412     foreach(EXCLUDE ${GCOVR_EXCLUDES})
0413         list(APPEND GCOVR_EXCLUDE_ARGS "-e")
0414         list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
0415     endforeach()
0416     
0417     # Set up commands which will be run to generate coverage data
0418     # Run tests
0419     set(GCOVR_XML_EXEC_TESTS_CMD
0420         ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
0421     )
0422     # Running gcovr
0423     set(GCOVR_XML_CMD
0424         ${GCOVR_PATH} --xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} 
0425         --object-directory=${PROJECT_BINARY_DIR} -o ${Coverage_NAME}.xml
0426     )
0427     
0428     if(CODE_COVERAGE_VERBOSE)
0429         message(STATUS "Executed command report")
0430 
0431         message(STATUS "Command to run tests: ")
0432         string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
0433         message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
0434 
0435         message(STATUS "Command to generate gcovr XML coverage data: ")
0436         string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
0437         message(STATUS "${GCOVR_XML_CMD_SPACED}")
0438     endif()
0439 
0440     add_custom_target(${Coverage_NAME}
0441         COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
0442         COMMAND ${GCOVR_XML_CMD}
0443         
0444         BYPRODUCTS ${Coverage_NAME}.xml
0445         WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
0446         DEPENDS ${Coverage_DEPENDENCIES}
0447         VERBATIM # Protect arguments to commands
0448         COMMENT "Running gcovr to produce Cobertura code coverage report."
0449     )
0450 
0451     # Show info where to find the report
0452     add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
0453         COMMAND ;
0454         COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
0455     )
0456 endfunction() # setup_target_for_coverage_gcovr_xml
0457 
0458 # Defines a target for running and collection code coverage information
0459 # Builds dependencies, runs the given executable and outputs reports.
0460 # NOTE! The executable should always have a ZERO as exit code otherwise
0461 # the coverage generation will not complete.
0462 #
0463 # setup_target_for_coverage_gcovr_html(
0464 #     NAME ctest_coverage                    # New target name
0465 #     EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
0466 #     DEPENDENCIES executable_target         # Dependencies to build first
0467 #     BASE_DIRECTORY "../"                   # Base directory for report
0468 #                                            #  (defaults to PROJECT_SOURCE_DIR)
0469 #     EXCLUDE "src/dir1/*" "src/dir2/*"      # Patterns to exclude (can be relative
0470 #                                            #  to BASE_DIRECTORY, with CMake 3.4+)
0471 # )
0472 # The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
0473 # GCVOR command.
0474 function(setup_target_for_coverage_gcovr_html)
0475 
0476     set(options NONE)
0477     set(oneValueArgs BASE_DIRECTORY NAME)
0478     set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
0479     cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
0480 
0481     if(NOT GCOVR_PATH)
0482         message(FATAL_ERROR "gcovr not found! Aborting...")
0483     endif() # NOT GCOVR_PATH
0484 
0485     # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
0486     if(DEFINED Coverage_BASE_DIRECTORY)
0487         get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
0488     else()
0489         set(BASEDIR ${PROJECT_SOURCE_DIR})
0490     endif()
0491 
0492     # Collect excludes (CMake 3.4+: Also compute absolute paths)
0493     set(GCOVR_EXCLUDES "")
0494     foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
0495         if(CMAKE_VERSION VERSION_GREATER 3.4)
0496             get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
0497         endif()
0498         list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
0499     endforeach()
0500     list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
0501 
0502     # Combine excludes to several -e arguments
0503     set(GCOVR_EXCLUDE_ARGS "")
0504     foreach(EXCLUDE ${GCOVR_EXCLUDES})
0505         list(APPEND GCOVR_EXCLUDE_ARGS "-e")
0506         list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
0507     endforeach()
0508 
0509     # Set up commands which will be run to generate coverage data
0510     # Run tests
0511     set(GCOVR_HTML_EXEC_TESTS_CMD
0512         ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
0513     )
0514     # Create folder
0515     set(GCOVR_HTML_FOLDER_CMD
0516         ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
0517     )
0518     # Running gcovr
0519     set(GCOVR_HTML_CMD
0520         ${GCOVR_PATH} --html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
0521         ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} 
0522         -o ${Coverage_NAME}/index.html
0523     )
0524 
0525     if(CODE_COVERAGE_VERBOSE)
0526         message(STATUS "Executed command report")
0527 
0528         message(STATUS "Command to run tests: ")
0529         string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
0530         message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
0531 
0532         message(STATUS "Command to create a folder: ")
0533         string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
0534         message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
0535 
0536         message(STATUS "Command to generate gcovr HTML coverage data: ")
0537         string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
0538         message(STATUS "${GCOVR_HTML_CMD_SPACED}")
0539     endif()
0540 
0541     add_custom_target(${Coverage_NAME}
0542         COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
0543         COMMAND ${GCOVR_HTML_FOLDER_CMD}
0544         COMMAND ${GCOVR_HTML_CMD}
0545 
0546         BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html  # report directory
0547         WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
0548         DEPENDS ${Coverage_DEPENDENCIES}
0549         VERBATIM # Protect arguments to commands
0550         COMMENT "Running gcovr to produce HTML code coverage report."
0551     )
0552 
0553     # Show info where to find the report
0554     add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
0555         COMMAND ;
0556         COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
0557     )
0558 
0559 endfunction() # setup_target_for_coverage_gcovr_html
0560 
0561 # Defines a target for running and collection code coverage information
0562 # Builds dependencies, runs the given executable and outputs reports.
0563 # NOTE! The executable should always have a ZERO as exit code otherwise
0564 # the coverage generation will not complete.
0565 #
0566 # setup_target_for_coverage_fastcov(
0567 #     NAME testrunner_coverage                    # New target name
0568 #     EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
0569 #     DEPENDENCIES testrunner                     # Dependencies to build first
0570 #     BASE_DIRECTORY "../"                        # Base directory for report
0571 #                                                 #  (defaults to PROJECT_SOURCE_DIR)
0572 #     EXCLUDE "src/dir1/" "src/dir2/"             # Patterns to exclude.
0573 #     NO_DEMANGLE                                 # Don't demangle C++ symbols
0574 #                                                 #  even if c++filt is found
0575 #     SKIP_HTML                                   # Don't create html report
0576 # )
0577 function(setup_target_for_coverage_fastcov)
0578 
0579     set(options NO_DEMANGLE SKIP_HTML)
0580     set(oneValueArgs BASE_DIRECTORY NAME)
0581     set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS)
0582     cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
0583 
0584     if(NOT FASTCOV_PATH)
0585         message(FATAL_ERROR "fastcov not found! Aborting...")
0586     endif()
0587 
0588     if(NOT GENHTML_PATH)
0589         message(FATAL_ERROR "genhtml not found! Aborting...")
0590     endif()
0591 
0592     # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
0593     if(Coverage_BASE_DIRECTORY)
0594         get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
0595     else()
0596         set(BASEDIR ${PROJECT_SOURCE_DIR})
0597     endif()
0598 
0599     # Collect excludes (Patterns, not paths, for fastcov)
0600     set(FASTCOV_EXCLUDES "")
0601     foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
0602         list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
0603     endforeach()
0604     list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
0605 
0606     # Conditional arguments
0607     if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
0608         set(GENHTML_EXTRA_ARGS "--demangle-cpp")
0609     endif()
0610 
0611     # Set up commands which will be run to generate coverage data
0612     set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
0613 
0614     set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
0615         --search-directory ${BASEDIR}
0616         --process-gcno
0617         --lcov
0618         --output ${Coverage_NAME}.info
0619         --exclude ${FASTCOV_EXCLUDES}
0620         --exclude ${FASTCOV_EXCLUDES}
0621     )
0622 
0623     if(Coverage_SKIP_HTML)
0624         set(FASTCOV_HTML_CMD ";")
0625     else()
0626         set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
0627             -o ${Coverage_NAME} ${Coverage_NAME}.info
0628         )
0629     endif()
0630 
0631     if(CODE_COVERAGE_VERBOSE)
0632         message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
0633 
0634         message("   Running tests:")
0635         string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
0636         message("     ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
0637 
0638         message("   Capturing fastcov counters and generating report:")
0639         string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
0640         message("     ${FASTCOV_CAPTURE_CMD_SPACED}")
0641 
0642         if(NOT Coverage_SKIP_HTML)
0643             message("   Generating HTML report: ")
0644             string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
0645             message("     ${FASTCOV_HTML_CMD_SPACED}")
0646         endif()
0647     endif()
0648 
0649     # Setup target
0650     add_custom_target(${Coverage_NAME}
0651 
0652         # Cleanup fastcov
0653         COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
0654             --search-directory ${BASEDIR}
0655             --zerocounters
0656 
0657         COMMAND ${FASTCOV_EXEC_TESTS_CMD}
0658         COMMAND ${FASTCOV_CAPTURE_CMD}
0659         COMMAND ${FASTCOV_HTML_CMD}
0660 
0661         # Set output files as GENERATED (will be removed on 'make clean')
0662         BYPRODUCTS
0663              ${Coverage_NAME}.info
0664              ${Coverage_NAME}/index.html  # report directory
0665 
0666         WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
0667         DEPENDS ${Coverage_DEPENDENCIES}
0668         VERBATIM # Protect arguments to commands
0669         COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
0670     )
0671 
0672     set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info.")
0673     if(NOT Coverage_SKIP_HTML)
0674         string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
0675     endif()
0676     # Show where to find the fastcov info report
0677     add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
0678         COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
0679     )
0680 
0681 endfunction() # setup_target_for_coverage_fastcov
0682 
0683 function(append_coverage_compiler_flags)
0684     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
0685     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
0686     set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
0687     message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
0688 endfunction() # append_coverage_compiler_flags