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()