# Copyright (c) Microsoft Corporation. All rights reserved.
# SPDX-License-Identifier: MIT
#
# Defines utility functions to create code coverage targets with gcov.
# gcov html and xml report.
#

## SET UP only when option is set. 
if(BUILD_CODE_COVERAGE)
    if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
        message(FATAL_ERROR "Code coverage requires GNU compiler (gcc) and debug build type. ${CMAKE_CXX_COMPILER_ID}")
    endif()

    ## Check if gcov and gcovr are available (gcovr generates html and xml from gcov files)
    find_program(GCOV gcov)
    find_program(GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
    if(NOT GCOV)
        message(FATAL_ERROR "gcov is required for code coverage reports.")
    endif()
    link_libraries(gcov)
endif()

# Set the compiler flags for the CMake project from where this is called (PARENT_SCOPE)
function(append_code_coverage_for_current_project)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fprofile-arcs -ftest-coverage" PARENT_SCOPE)
endfunction()


# gcovr - html
function(add_gcovr_html)

    set(options NONE)
    set(oneValueArgs TARGET_NAME EXECUTABLE_NAME)
    set(multiValueArgs EXCLUDE)
    cmake_parse_arguments(args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

    set(BASEDIR ${PROJECT_SOURCE_DIR})

    # Enable excluding path from code coverage generation 
    set(EXCLUDE_ARGS "")
    foreach(PATH ${args_EXCLUDE})
        list(APPEND EXCLUDE_ARGS -e)
        list(APPEND EXCLUDE_ARGS "${BASEDIR}/${PATH}")
    endforeach()

    add_custom_target(${args_TARGET_NAME}
        # Run tests
        ${args_EXECUTABLE_NAME}

        # Create folder
        COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${args_TARGET_NAME}

        # Running gcovr
        COMMAND ${GCOVR_PATH} --html --html-details
            -r ${BASEDIR} ${EXCLUDE_ARGS}
            --object-directory=${PROJECT_BINARY_DIR}
            -o ${args_TARGET_NAME}/index.html

        BYPRODUCTS ${PROJECT_BINARY_DIR}/${args_TARGET_NAME}  # report directory
        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
        DEPENDS
        VERBATIM
        COMMAND_EXPAND_LISTS
        COMMENT "Running gcovr to produce HTML code coverage report."
    )

    # Show info where to find the report
    add_custom_command(TARGET ${args_TARGET_NAME} POST_BUILD
        COMMAND ;
        COMMENT "Open ./${args_TARGET_NAME}/index.html in your browser to view the coverage report."
    )

endfunction()

# gcovr - xml
function(add_gcovr_xml)

    set(options NONE)
    set(oneValueArgs TARGET_NAME EXECUTABLE_NAME)
    set(multiValueArgs EXCLUDE)
    cmake_parse_arguments(args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

    set(BASEDIR ${PROJECT_SOURCE_DIR})

    if (NOT DEFINED ENV{CODE_COVERAGE_COLLECT_ONLY})
        set(RUN_EXE ${args_EXECUTABLE_NAME})
    endif()

    # Enable excluding path from code coverage generation 
    set(EXCLUDE_ARGS "")
    foreach(PATH ${args_EXCLUDE})
        list(APPEND EXCLUDE_ARGS -e)
        list(APPEND EXCLUDE_ARGS "${BASEDIR}/${PATH}")
    endforeach()
    
    add_custom_target(${args_TARGET_NAME}
        # Running on CI won't require to run tests exe since it was run on previous step
        ${RUN_EXE}

        # Running gcovr
        COMMAND ${GCOVR_PATH} --xml
            -r ${BASEDIR} ${EXCLUDE_ARGS}
            --object-directory=${PROJECT_BINARY_DIR}
            -o ${args_TARGET_NAME}.xml
        BYPRODUCTS ${args_TARGET_NAME}.xml
        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
        DEPENDS
        VERBATIM
        COMMAND_EXPAND_LISTS
        COMMENT "Running gcovr to produce Cobertura code coverage report."
    )

    # Show info where to find the report
    add_custom_command(TARGET ${args_TARGET_NAME} POST_BUILD
        COMMAND ;
        COMMENT "Cobertura code coverage report saved in ${args_TARGET_NAME}.xml."
    )

endfunction()

# codeCoverage macro to be used from CMake lib definition
# exclude files list can be pass as extra parameters
macro(create_code_coverage service target_prefix exe_name)
    if(BUILD_CODE_COVERAGE)
        APPEND_CODE_COVERAGE_FOR_CURRENT_PROJECT()

        # Get exclude files if any
        set (EXCLUDE_LIST ${ARGN})

        # HTML and XML - Coverage using gcovr
        add_gcovr_html(TARGET_NAME ${target_prefix}_cov_html EXECUTABLE_NAME ${exe_name} EXCLUDE ${EXCLUDE_LIST})
        # xml is used on CI
        add_gcovr_xml(TARGET_NAME ${target_prefix}_cov_xml EXECUTABLE_NAME ${exe_name} EXCLUDE ${EXCLUDE_LIST})

        # add xml target to `coverage_targets.txt` which is used by CI to generate coverage reports
        file(APPEND ${CMAKE_BINARY_DIR}/${service}-targets-coverage.txt " ${target_prefix}_cov_xml")
    endif()
endmacro()
