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)