From 64ef3fefb4d1ab61ba9dadf69eccb631142016c2 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Fri, 8 Mar 2024 10:04:07 +0100 Subject: [PATCH] Rework the coverage build This reworks it to avoid a need to special build type and adding the flags only to the targets that need it (skipping testing wrappers which break with them). It also updates the CodeCoverage module from the following URL: https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake Signed-off-by: Jakub Jelen Reviewed-by: Andreas Schneider --- .gitlab-ci.yml | 2 +- CMakeLists.txt | 7 ++- cmake/Modules/AddCMockaTest.cmake | 4 ++ cmake/Modules/CodeCoverage.cmake | 76 +++++++++++++++++-------- cmake/Modules/DefineCompilerFlags.cmake | 12 ---- src/CMakeLists.txt | 7 +++ tests/CMakeLists.txt | 12 +++- tests/pkd/CMakeLists.txt | 5 +- tests/server/test_server/CMakeLists.txt | 11 ++-- 9 files changed, 89 insertions(+), 47 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d942ae3e..344a91dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -148,7 +148,7 @@ fedora/coverage: extends: .fedora image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD variables: - CMAKE_ADDITIONAL_OPTIONS: -DCMAKE_BUILD_TYPE=Coverage + CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_BUILD_TYPE=Debug -DWITH_COVERAGE=ON" script: - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. && make -j$(nproc) && diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c7b8c50..acc1e606 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,18 +188,18 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND) endif (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND) # Coverage -if (CMAKE_BUILD_TYPE STREQUAL "Coverage") +if (WITH_COVERAGE) include(CodeCoverage) setup_target_for_coverage_lcov( NAME "coverage" EXECUTABLE make test DEPENDENCIES ssh tests) - set(GCOVR_ADDITIONAL_ARGS --xml-pretty --exclude-unreachable-branches --print-summary --gcov-ignore-parse-errors) + set(GCOVR_ADDITIONAL_ARGS --xml-pretty --exclude-unreachable-branches --print-summary) setup_target_for_coverage_gcovr_xml( NAME "coverage_xml" EXECUTABLE make test DEPENDENCIES ssh tests) -endif (CMAKE_BUILD_TYPE STREQUAL "Coverage") +endif (WITH_COVERAGE) add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source DEPENDS ${_SYMBOL_TARGET} VERBATIM) @@ -215,6 +215,7 @@ message(STATUS "********************************************") message(STATUS "********** ${PROJECT_NAME} build options : **********") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS "Coverage: ${WITH_COVERAGE}") message(STATUS "zlib support: ${WITH_ZLIB}") message(STATUS "libgcrypt support: ${WITH_GCRYPT}") message(STATUS "libmbedTLS support: ${WITH_MBEDTLS}") diff --git a/cmake/Modules/AddCMockaTest.cmake b/cmake/Modules/AddCMockaTest.cmake index 4b0c2dad..79178183 100644 --- a/cmake/Modules/AddCMockaTest.cmake +++ b/cmake/Modules/AddCMockaTest.cmake @@ -116,5 +116,9 @@ function(ADD_CMOCKA_TEST _TARGET_NAME) add_test(${_TARGET_NAME} ${TARGET_SYSTEM_EMULATOR} ${_TARGET_NAME} ) + if (WITH_COVERAGE) + include(CodeCoverage) + append_coverage_compiler_flags_to_target(${_TARGET_NAME}) + endif (WITH_COVERAGE) endfunction (ADD_CMOCKA_TEST) diff --git a/cmake/Modules/CodeCoverage.cmake b/cmake/Modules/CodeCoverage.cmake index 3cf81b98..c500cfa4 100644 --- a/cmake/Modules/CodeCoverage.cmake +++ b/cmake/Modules/CodeCoverage.cmake @@ -83,6 +83,10 @@ # - Change gcovr output from -o for --xml and --html output respectively. # This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt". # +# 2022-09-28, Sebastian Mueller +# - fix append_coverage_compiler_flags_to_target to correctly add flags +# - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent) +# # USAGE: # # 1. Copy this file into your cmake modules path. @@ -147,30 +151,34 @@ if(NOT GCOV_PATH) message(FATAL_ERROR "gcov not found! Aborting...") endif() # NOT GCOV_PATH +# Check supported compiler (Clang, GNU and Flang) get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) -list(GET LANGUAGES 0 LANG) - -if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") +foreach(LANG ${LANGUAGES}) + if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3) - message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") endif() -elseif(NOT CMAKE_COMPILER_IS_GNUCXX) - if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang") - # Do nothing; exit conditional without error if true - elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") - # Do nothing; exit conditional without error if true - else() - message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") - endif() -endif() + elseif(NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU" + AND NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(LLVM)?[Ff]lang") + message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...") + endif() +endforeach() -set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" +set(COVERAGE_COMPILER_FLAGS "-g --coverage" CACHE INTERNAL "") + if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") include(CheckCXXCompilerFlag) - check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path) - if(HAVE_fprofile_abs_path) - set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") + check_cxx_compiler_flag(-fprofile-abs-path HAVE_cxx_fprofile_abs_path) + if(HAVE_cxx_fprofile_abs_path) + set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") + endif() +endif() +if(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)") + include(CheckCCompilerFlag) + check_c_compiler_flag(-fprofile-abs-path HAVE_c_fprofile_abs_path) + if(HAVE_c_fprofile_abs_path) + set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") endif() endif() @@ -202,7 +210,7 @@ mark_as_advanced( CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT (CMAKE_BUILD_TYPE STREQUAL "Coverage" OR CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)) +if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)) message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG) @@ -228,7 +236,7 @@ endif() # ) function(setup_target_for_coverage_lcov) - set(options NO_DEMANGLE) + set(options NO_DEMANGLE SONARQUBE) set(oneValueArgs BASE_DIRECTORY NAME) set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -298,6 +306,18 @@ function(setup_target_for_coverage_lcov) ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info ) + if(${Coverage_SONARQUBE}) + # Generate SonarQube output + set(GCOVR_XML_CMD + ${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + set(GCOVR_XML_CMD_COMMAND + COMMAND ${GCOVR_XML_CMD} + ) + set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml) + set(GCOVR_XML_CMD_COMMENT COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.") + endif() if(CODE_COVERAGE_VERBOSE) @@ -329,6 +349,12 @@ function(setup_target_for_coverage_lcov) message(STATUS "Command to generate lcov HTML output: ") string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}") message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}") + + if(${Coverage_SONARQUBE}) + message(STATUS "Command to generate SonarQube XML output: ") + string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") + message(STATUS "${GCOVR_XML_CMD_SPACED}") + endif() endif() # Setup target @@ -340,6 +366,7 @@ function(setup_target_for_coverage_lcov) COMMAND ${LCOV_BASELINE_COUNT_CMD} COMMAND ${LCOV_FILTER_CMD} COMMAND ${LCOV_GEN_HTML_CMD} + ${GCOVR_XML_CMD_COMMAND} # Set output files as GENERATED (will be removed on 'make clean') BYPRODUCTS @@ -347,6 +374,7 @@ function(setup_target_for_coverage_lcov) ${Coverage_NAME}.capture ${Coverage_NAME}.total ${Coverage_NAME}.info + ${GCOVR_XML_CMD_BYPRODUCTS} ${Coverage_NAME}/index.html WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} @@ -358,6 +386,7 @@ function(setup_target_for_coverage_lcov) add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ; COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ${GCOVR_XML_CMD_COMMENT} ) # Show info where to find the report @@ -621,7 +650,6 @@ function(setup_target_for_coverage_fastcov) --process-gcno --output ${Coverage_NAME}.json --exclude ${FASTCOV_EXCLUDES} - --exclude ${FASTCOV_EXCLUDES} ) set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH} @@ -714,7 +742,9 @@ endfunction() # append_coverage_compiler_flags # Setup coverage for specific library function(append_coverage_compiler_flags_to_target name) - target_compile_options(${name} - PRIVATE ${COVERAGE_COMPILER_FLAGS}) + separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}") + target_compile_options(${name} PRIVATE ${_flag_list}) + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + target_link_libraries(${name} PRIVATE gcov) + endif() endfunction() - diff --git a/cmake/Modules/DefineCompilerFlags.cmake b/cmake/Modules/DefineCompilerFlags.cmake index c6c07ede..39378a10 100644 --- a/cmake/Modules/DefineCompilerFlags.cmake +++ b/cmake/Modules/DefineCompilerFlags.cmake @@ -46,16 +46,4 @@ if (UNIX AND NOT WIN32) CACHE STRING "Flags used by the linker during the creation of shared libraries during UNDEFINEDSANITIZER builds.") set(CMAKE_EXEC_LINKER_FLAGS_UNDEFINEDSANITIZER "-fsanitize=undefined" CACHE STRING "Flags used by the linker during UNDEFINEDSANITIZER builds.") - - # Activate with: -DCMAKE_BUILD_TYPE=Coverage - set(CMAKE_C_FLAGS_COVERAGE "-O0 -g -fprofile-arcs -ftest-coverage" - CACHE STRING "Flags used by the C compiler during Coverage builds.") - set(CMAKE_CXX_FLAGS_COVERAGE "-O0 -g -fprofile-arcs -ftest-coverage" - CACHE STRING "Flags used by the CXX compiler during Coverage builds.") - set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "-fprofile-arcs -ftest-coverage" - CACHE STRING "Flags used by the linker during the creation of shared libraries during Coverage builds.") - set(CMAKE_MODULE_LINKER_FLAGS_COVERAGE "-fprofile-arcs -ftest-coverage" - CACHE STRING "Flags used by the linker during the creation of shared libraries during Coverage builds.") - set(CMAKE_EXEC_LINKER_FLAGS_COVERAGE "-fprofile-arcs -ftest-coverage" - CACHE STRING "Flags used by the linker during Coverage builds.") endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 748bb7a9..93ecb5e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -371,6 +371,10 @@ if (MINGW) target_link_libraries(ssh PRIVATE "-Wl,--enable-stdcall-fixup") target_compile_definitions(ssh PRIVATE "_POSIX_SOURCE") endif () +if (WITH_COVERAGE) + include(CodeCoverage) + append_coverage_compiler_flags_to_target(ssh) +endif (WITH_COVERAGE) install(TARGETS ssh @@ -423,6 +427,9 @@ if (BUILD_STATIC_LIB) if (WIN32) target_compile_definitions(ssh-static PUBLIC "LIBSSH_STATIC") endif (WIN32) + if (WITH_COVERAGE) + append_coverage_compiler_flags_to_target(ssh-static) + endif (WITH_COVERAGE) endif (BUILD_STATIC_LIB) message(STATUS "Threads_FOUND=${Threads_FOUND}") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e8c77883..d795b4f6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,10 +26,13 @@ add_library(${TORTURE_LIBRARY} torture_key.c torture_pki.c torture_cmocka.c) -target_link_libraries(${TORTURE_LIBRARY} ${TORTURE_LINK_LIBRARIES}) +target_link_libraries(${TORTURE_LIBRARY} PRIVATE ${TORTURE_LINK_LIBRARIES}) target_compile_options(${TORTURE_LIBRARY} PRIVATE -DSSH_PING_EXECUTABLE="${CMAKE_CURRENT_BINARY_DIR}/ssh_ping" ) +if (WITH_COVERAGE) + append_coverage_compiler_flags_to_target(${TORTURE_LIBRARY}) +endif (WITH_COVERAGE) # The shared version of the library is only useful when client testing is # enabled @@ -60,7 +63,7 @@ if (CLIENT_TESTING) torture_pki.c torture_cmocka.c ) - target_link_libraries(${TORTURE_SHARED_LIBRARY} + target_link_libraries(${TORTURE_SHARED_LIBRARY} PUBLIC ${CMOCKA_LIBRARY} ssh::static ${WRAP_SYMBOLS} @@ -69,11 +72,14 @@ if (CLIENT_TESTING) -DSSH_PING_EXECUTABLE="${CMAKE_CURRENT_BINARY_DIR}/ssh_ping" -DTORTURE_SHARED ) + if (WITH_COVERAGE) + append_coverage_compiler_flags_to_target(${TORTURE_SHARED_LIBRARY}) + endif (WITH_COVERAGE) endif () if (ARGP_LIBRARIES) target_link_libraries(${TORTURE_LIBRARY} - ${ARGP_LIBRARIES} + PUBLIC ${ARGP_LIBRARIES} ) endif() diff --git a/tests/pkd/CMakeLists.txt b/tests/pkd/CMakeLists.txt index 9a7038cc..f5a62653 100644 --- a/tests/pkd/CMakeLists.txt +++ b/tests/pkd/CMakeLists.txt @@ -25,7 +25,10 @@ set(pkd_libs add_executable(pkd_hello ${pkd_hello_src}) target_compile_options(pkd_hello PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) -target_link_libraries(pkd_hello ${pkd_libs}) +target_link_libraries(pkd_hello PRIVATE ${pkd_libs}) +if (WITH_COVERAGE) + append_coverage_compiler_flags_to_target(pkd_hello) +endif (WITH_COVERAGE) # # pkd_hello_i1 runs only one iteration per algorithm combination for diff --git a/tests/server/test_server/CMakeLists.txt b/tests/server/test_server/CMakeLists.txt index f1453d43..7e0f88c1 100644 --- a/tests/server/test_server/CMakeLists.txt +++ b/tests/server/test_server/CMakeLists.txt @@ -12,6 +12,9 @@ add_library(testserver STATIC test_server.c default_cb.c sftpserver_cb.c) +if (WITH_COVERAGE) + append_coverage_compiler_flags_to_target(testserver) +endif (WITH_COVERAGE) set(LIBSSH_SERVER_TESTS # torture_server_kbdint @@ -29,10 +32,10 @@ if (UNIX AND NOT WIN32) add_executable(test_server ${server_SRCS}) target_compile_options(test_server PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) target_link_libraries(test_server - testserver - ssh::ssh - ${ARGP_LIBRARIES} - util) + PRIVATE testserver ssh::ssh ${ARGP_LIBRARIES} util) + if (WITH_COVERAGE) + append_coverage_compiler_flags_to_target(test_server) + endif (WITH_COVERAGE) endif () endif (WITH_SERVER AND UNIX AND NOT WIN32)