From d31802ed95ec4f56621784fc1fc5fda20644fa31 Mon Sep 17 00:00:00 2001 From: Andrey Astafyev Date: Thu, 6 Oct 2022 07:57:47 +0300 Subject: [PATCH] 2.0.2 --- MyxxCMake/MyxxCMakeConfig.cmake | 15 +- MyxxCMake/lib/AnalyzeApplyReplacements.cmake | 14 + MyxxCMake/lib/AnalyzeClangCheck.cmake | 52 ++ ...ClangTidy.cmake => AnalyzeClangTidy.cmake} | 0 MyxxCMake/lib/AnalyzeClazy.cmake | 56 ++ MyxxCMake/lib/AnalyzePvsStudio.cmake | 54 ++ MyxxCMake/lib/Coverage.cmake | 46 ++ MyxxCMake/pvs-studio/PVS-Studio.cmake | 607 ++++++++++++++++++ MyxxCMake/sanitizers/FindASan.cmake | 45 ++ MyxxCMake/sanitizers/FindCFI.cmake | 42 ++ MyxxCMake/sanitizers/FindLeakSan.cmake | 37 ++ MyxxCMake/sanitizers/FindMSan.cmake | 49 ++ MyxxCMake/sanitizers/FindSS.cmake | 37 ++ MyxxCMake/sanitizers/FindSanitizers.cmake | 109 ++++ MyxxCMake/sanitizers/FindTSan.cmake | 53 ++ MyxxCMake/sanitizers/FindUBSan.cmake | 37 ++ MyxxCMake/sanitizers/asan-wrapper | 55 ++ MyxxCMake/sanitizers/sanitize-helpers.cmake | 199 ++++++ 18 files changed, 1506 insertions(+), 1 deletion(-) create mode 100644 MyxxCMake/lib/AnalyzeApplyReplacements.cmake create mode 100644 MyxxCMake/lib/AnalyzeClangCheck.cmake rename MyxxCMake/lib/{CodeAnalyzeClangTidy.cmake => AnalyzeClangTidy.cmake} (100%) create mode 100644 MyxxCMake/lib/AnalyzeClazy.cmake create mode 100644 MyxxCMake/lib/AnalyzePvsStudio.cmake create mode 100644 MyxxCMake/lib/Coverage.cmake create mode 100644 MyxxCMake/pvs-studio/PVS-Studio.cmake create mode 100644 MyxxCMake/sanitizers/FindASan.cmake create mode 100644 MyxxCMake/sanitizers/FindCFI.cmake create mode 100644 MyxxCMake/sanitizers/FindLeakSan.cmake create mode 100644 MyxxCMake/sanitizers/FindMSan.cmake create mode 100644 MyxxCMake/sanitizers/FindSS.cmake create mode 100644 MyxxCMake/sanitizers/FindSanitizers.cmake create mode 100644 MyxxCMake/sanitizers/FindTSan.cmake create mode 100644 MyxxCMake/sanitizers/FindUBSan.cmake create mode 100755 MyxxCMake/sanitizers/asan-wrapper create mode 100644 MyxxCMake/sanitizers/sanitize-helpers.cmake diff --git a/MyxxCMake/MyxxCMakeConfig.cmake b/MyxxCMake/MyxxCMakeConfig.cmake index d4b455b..6ac8c91 100644 --- a/MyxxCMake/MyxxCMakeConfig.cmake +++ b/MyxxCMake/MyxxCMakeConfig.cmake @@ -7,9 +7,18 @@ if(NOT MyxCMake_DIR) message(FATAL_ERROR "MyxxCMake library required MyxCMake to work") endif() +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(MYXX_CMAKE_LIB_DIR "${MYXX_CMAKE_SOURCE_DIR}/lib" CACHE PATH "") -include(${MYXX_CMAKE_LIB_DIR}/CodeAnalyzeClangTidy.cmake) +include(${MYXX_CMAKE_LIB_DIR}/Coverage.cmake) +include(${MYXX_CMAKE_LIB_DIR}/AnalyzeApplyReplacements.cmake) +include(${MYXX_CMAKE_LIB_DIR}/AnalyzeClangTidy.cmake) +include(${MYXX_CMAKE_LIB_DIR}/AnalyzeClangCheck.cmake) +include(${MYXX_CMAKE_LIB_DIR}/AnalyzeClazy.cmake) + +include(${MYXX_CMAKE_SOURCE_DIR}/sanitizers/FindSanitizers.cmake) +include(${MYXX_CMAKE_SOURCE_DIR}/pvs-studio/PVS-Studio.cmake) +include(${MYXX_CMAKE_LIB_DIR}/AnalyzePvsStudio.cmake) function(myxx) get_property(targets DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} PROPERTY BUILDSYSTEM_TARGETS) @@ -17,7 +26,11 @@ function(myxx) get_target_property(target_type ${iter} TYPE) if(NOT ${target_type} STREQUAL "UTILITY") + myxx_code_coverage(${iter}) myxx_analyze_clang_tidy(${iter}) + myxx_analyze_clang_check(${iter}) + myxx_analyze_clazy(${iter}) + myxx_analyze_pvs_studio(${iter}) endif() endforeach() endfunction(myxx) diff --git a/MyxxCMake/lib/AnalyzeApplyReplacements.cmake b/MyxxCMake/lib/AnalyzeApplyReplacements.cmake new file mode 100644 index 0000000..2b8a72f --- /dev/null +++ b/MyxxCMake/lib/AnalyzeApplyReplacements.cmake @@ -0,0 +1,14 @@ +include_guard(GLOBAL) + +if(NOT CLANG_APPLY_REPLACEMENTS_NAME) + set(CLANG_APPLY_REPLACEMENTS_NAMES clang-apply-replacements) + foreach(V RANGE 9 18) + list(INSERT CLANG_APPLY_REPLACEMENTS_NAMES 0 "clang-apply-replacements-${V}") + endforeach() + unset(V) +else() + set(CLANG_APPLY_REPLACEMENTS_NAMES ${CLANG_APPLY_REPLACEMENTS_NAME}) +endif() + +find_program(CLANG_APPLY_REPLACEMENTS_EXE NAMES ${CLANG_APPLY_REPLACEMENTS_NAMES}) +unset(CLANG_APPLY_REPLACEMENTS_NAMES) diff --git a/MyxxCMake/lib/AnalyzeClangCheck.cmake b/MyxxCMake/lib/AnalyzeClangCheck.cmake new file mode 100644 index 0000000..9b310d7 --- /dev/null +++ b/MyxxCMake/lib/AnalyzeClangCheck.cmake @@ -0,0 +1,52 @@ +include_guard(GLOBAL) + +if(NOT CLANG_CHECK_NAME) + set(CLANG_CHECK_NAMES clang-check) + foreach(V RANGE 9 18) + list(INSERT CLANG_CHECK_NAMES 0 "clang-check-${V}") + endforeach() + unset(V) +else() + set(CLANG_CHECK_NAMES ${CLANG_CHECK_NAME}) +endif() + +find_program(CLANG_CHECK_EXE NAMES ${CLANG_CHECK_NAMES}) +unset(CLANG_CHECK_NAMES) + +function(myxx_analyze_clang_check target) + if(NOT CLANG_CHECK_EXE) + message(STATUS "MyxCMake: Clang Check analyzer is not found") + return() + endif() + + if(TARGET ${target}-analyze-clang-check) + message(STATUS "MyxxCMake: target ${target}-analyze-clang-check already exists") + return() + endif() + + set(options FIX) + set(oneValueArgs) + set(multiValueArgs EXTRA_ARGS) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(args --analyze --extra-arg=-Wno-unknown-warning-option ${EXTRA_ARGS}) + get_target_property(sources ${target} SOURCES) + foreach(iter ${sources}) + string(FIND ${iter} ${CMAKE_BINARY_DIR} pos) + if(pos EQUAL -1) + list(APPEND srcs ${iter}) + endif() + endforeach() + + if(ARG_FIX) + list(APPEND args "--fixit") + endif() + if(NOT TARGET myxx-analyze-clang-check) + add_custom_target(myxx-analyze-clang-check) + endif() + add_custom_target(${target}-analyze-clang-check + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${CLANG_CHECK_EXE} ${args} -p ${CMAKE_BINARY_DIR} ${srcs}) + add_dependencies(${target}-analyze-clang-check ${target}) + add_dependencies(myxx-analyze-clang-check ${target}-analyze-clang-check) +endfunction() diff --git a/MyxxCMake/lib/CodeAnalyzeClangTidy.cmake b/MyxxCMake/lib/AnalyzeClangTidy.cmake similarity index 100% rename from MyxxCMake/lib/CodeAnalyzeClangTidy.cmake rename to MyxxCMake/lib/AnalyzeClangTidy.cmake diff --git a/MyxxCMake/lib/AnalyzeClazy.cmake b/MyxxCMake/lib/AnalyzeClazy.cmake new file mode 100644 index 0000000..d6fcfa9 --- /dev/null +++ b/MyxxCMake/lib/AnalyzeClazy.cmake @@ -0,0 +1,56 @@ +include_guard(GLOBAL) + +find_program(CLAZY_EXE NAMES clazy-standalone) + +if(CLAZY_EXE AND CLANG_APPLY_REPLACEMENTS_EXE) + option(MYX_CMAKE_CLAZY_FIX "MyxCMake: perform fixes for Clazy" OFF) +endif() + +function(myxx_analyze_clazy target) + if(NOT CLAZY_EXE) + message(STATUS "MyxCMake: Clazy standalone analyzer is not found") + return() + endif() + + if(TARGET ${target}-analyze-clazy) + message(STATUS "MyxxCMake: target ${target}-analyze-clazy already exists") + return() + endif() + + set(options FIX) + set(oneValueArgs CHECKS) + set(multiValueArgs) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT ARG_CHECKS) + set(ARG_CHECKS "level2,container-inside-loop,heap-allocated-small-trivial-type,inefficient-qlist,isempty-vs-count,qt-keywords,unneeded-cast") + endif() + + set(args -checks=${ARG_CHECKS} + -extra-arg=-Wno-unknown-warning-option + -export-fixes=clazy-fixes-file.yaml) + get_target_property(sources ${target} SOURCES) + foreach(iter ${sources}) + string(FIND ${iter} ${CMAKE_BINARY_DIR} pos) + if(pos EQUAL -1) + list(APPEND srcs ${iter}) + endif() + endforeach() + + if(NOT TARGET myxx-analyze-clazy) + add_custom_target(myxx-analyze-clazy) + endif() + + if(ARG_FIX) + add_custom_target(${target}-analyze-clazy + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${CLAZY_EXE} ${args} -p ${CMAKE_BINARY_DIR} ${srcs} + COMMAND ${CLANG_APPLY_REPLACEMENTS_EXE} ${CMAKE_BINARY_DIR}) + else() + add_custom_target(${target}-analyze-clazy + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${CLAZY_EXE} ${args} -p ${CMAKE_BINARY_DIR} ${srcs}) + endif() + add_dependencies(${target}-analyze-clazy ${target}) + add_dependencies(myxx-analyze-clazy ${target}-analyze-clazy) +endfunction() diff --git a/MyxxCMake/lib/AnalyzePvsStudio.cmake b/MyxxCMake/lib/AnalyzePvsStudio.cmake new file mode 100644 index 0000000..335680e --- /dev/null +++ b/MyxxCMake/lib/AnalyzePvsStudio.cmake @@ -0,0 +1,54 @@ +find_program(PVS_STUDIO_ANALYZER_EXE pvs-studio-analyzer) +find_program(PVS_STUDIO_CONVERTER_EXE plog-converter) + +function(myxx_analyze_pvs_studio target) + if(PVS_STUDIO_ANALYZER_EXE) + message(STATUS "MyxxCMake: pvs-studio-analyzer not found.") + return() + endif() + if(PVS_STUDIO_CONVERTER_EXE) + message(STATUS "MyxxCMake: pvs-studio-converter not found.") + return() + endif() + + if(TARGET myx-cmake-analyze-pvs-studio) + return() + endif() + + set(options) + set(oneValueArgs CHECKS) + set(multiValueArgs) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(PVS_STUDIO_HEADER "${CMAKE_BINARY_DIR}/include/myx_cmake_pvs_studio_header_p.hpp") + file(WRITE ${PVS_STUDIO_HEADER} + "#ifndef MYX_CMAKE_PVS_STUDIO_HEADER_HPP_\n" + "#define MYX_CMAKE_PVS_STUDIO_HEADER_HPP_\n" + "#pragma once\n" + "//-V813_MINSIZE=33\n" + "#endif // MYX_CMAKE_PVS_STUDIO_HEADER_HPP_\n") + + pvs_studio_add_target(TARGET myx-cmake-analyze-pvs-studio + LOG + COMPILE_COMMANDS + HIDE_HELP + OUTPUT FORMAT errorfile + ARGS --exclude-path ${CMAKE_CURRENT_BINARY_DIR}/${target}_autogen + MODE GA:1,2,3;64:1;OP:1,2;CS:1,2) + + get_target_property(__target_type ${target} TYPE) + if(${__target_type} STREQUAL INTERFACE_LIBRARY) + set(__target_type INTERFACE) + else() + set(__target_type PRIVATE) + endif() + if(MSVC) + target_compile_options(${target} BEFORE ${__target_type} /FI ${PVS_STUDIO_HEADER}) + else() # GCC/Clang + target_compile_options(${target} BEFORE ${__target_type} -include ${PVS_STUDIO_HEADER}) + endif() + + add_dependencies(myx-cmake-analyze-pvs-studio ${target}) + get_target_property(__target_source_dir "${target}" SOURCE_DIR) + pvs_studio_analyze_target("${target}" "${__target_source_dir}") +endfunction() diff --git a/MyxxCMake/lib/Coverage.cmake b/MyxxCMake/lib/Coverage.cmake new file mode 100644 index 0000000..9c40f71 --- /dev/null +++ b/MyxxCMake/lib/Coverage.cmake @@ -0,0 +1,46 @@ +option(MYXX_CODE_COVERAGE "MyxxCMake: enable code coverage" OFF) + +if(MYXX_CODE_COVERAGE) + find_program(LCOV_EXE NAMES lcov) + find_program(GENHTML_EXE NAMES genhtml) +endif() + +function(myxx_code_coverage target) + if(NOT MYXX_CODE_COVERAGE) + return() + endif() + + if(CMAKE_CXX_COMPILER_IS_GCC) + target_compile_options(${target} PUBLIC "--coverage") + set_property( + TARGET ${target} + APPEND_STRING + PROPERTY LINK_FLAGS " --coverage") + endif() + if(CMAKE_CXX_COMPILER_IS_CLANG) + target_compile_options(${target} PUBLIC "-fprofile-instr-generate -fcoverage-mapping") + set_property( + TARGET ${target} + APPEND_STRING + PROPERTY LINK_FLAGS " --coverage") + endif() + + if(LCOV_EXE) + add_custom_target(${target}-coverage + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${LCOV_EXE} --test-name ${target} --output "${target}.lcov" --capture + --no-external + --base-directory ${CMAKE_SOURCE_DIR} + --directory ${CMAKE_BINARY_DIR}) + --exclude "/usr/\\\*" + --exclude "${CMAKE_BINARY_DIR}/\\\*" + add_dependencies(${target}-coverage ${target}) + + if(GENHTML_EXE) + add_custom_target(${target}-coverage-report + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${GENHTML_EXE} --output-directory "${target}-coverage-html" "${target}.lcov") + add_dependencies(${target}-coverage-report ${target}-coverage) + endif() + endif() +endfunction() diff --git a/MyxxCMake/pvs-studio/PVS-Studio.cmake b/MyxxCMake/pvs-studio/PVS-Studio.cmake new file mode 100644 index 0000000..6ca32b0 --- /dev/null +++ b/MyxxCMake/pvs-studio/PVS-Studio.cmake @@ -0,0 +1,607 @@ +# 2006-2008 (c) Viva64.com Team +# 2008-2020 (c) OOO "Program Verification Systems" +# 2020-2022 (c) PVS-Studio LLC +# Version 12 + +cmake_minimum_required(VERSION 3.0.0) +cmake_policy(SET CMP0054 NEW) + +if (PVS_STUDIO_AS_SCRIPT) + # This code runs at build time. + # It executes pvs-studio-analyzer and propagates its return value. + + set(in_cl_params FALSE) + set(additional_args) + + foreach (arg ${PVS_STUDIO_COMMAND}) + if (NOT in_cl_params) + if ("${arg}" STREQUAL "--cl-params") + set(in_cl_params TRUE) + endif() + else() + # A workaround for macOS frameworks (e.g. QtWidgets.framework) + # You can test this workaround on this project: https://github.com/easyaspi314/MidiEditor/tree/gba + if (APPLE AND "${arg}" MATCHES "^-I(.*)\\.framework$") + STRING(REGEX REPLACE "^-I(.*)\\.framework$" "\\1.framework" framework "${arg}") + if (IS_ABSOLUTE "${framework}") + get_filename_component(framework "${framework}" DIRECTORY) + list(APPEND additional_args "-iframework") + list(APPEND additional_args "${framework}") + endif() + endif() + endif() + endforeach() + + file(REMOVE "${PVS_STUDIO_LOG_FILE}") + execute_process(COMMAND ${PVS_STUDIO_COMMAND} ${additional_args} + RESULT_VARIABLE result + OUTPUT_VARIABLE output + ERROR_VARIABLE error) + + if (result AND NOT output MATCHES "^No compilation units were found\\.") + message(FATAL_ERROR "PVS-Studio exited with non-zero code.\nStdout:\n${output}\nStderr:\n${error}\n") + endif() + + return() +endif() + +if(__PVS_STUDIO_INCLUDED) + return() +endif() +set(__PVS_STUDIO_INCLUDED TRUE) + +set(PVS_STUDIO_SCRIPT "${CMAKE_CURRENT_LIST_FILE}") + +function (pvs_studio_log TEXT) + if (PVS_STUDIO_DEBUG) + message("PVS-Studio: ${TEXT}") + endif() +endfunction() + +function (pvs_studio_relative_path VAR ROOT FILEPATH) + if (WIN32) + STRING(REGEX REPLACE "\\\\" "/" ROOT ${ROOT}) + STRING(REGEX REPLACE "\\\\" "/" FILEPATH ${FILEPATH}) + endif() + set("${VAR}" "${FILEPATH}" PARENT_SCOPE) + if (IS_ABSOLUTE "${FILEPATH}") + file(RELATIVE_PATH RPATH "${ROOT}" "${FILEPATH}") + if (NOT IS_ABSOLUTE "${RPATH}") + set("${VAR}" "${RPATH}" PARENT_SCOPE) + endif() + endif() +endfunction() + +function (pvs_studio_join_path VAR DIR1 DIR2) + if ("${DIR2}" MATCHES "^(/|~|.:/).*$" OR "${DIR1}" STREQUAL "") + set("${VAR}" "${DIR2}" PARENT_SCOPE) + else() + set("${VAR}" "${DIR1}/${DIR2}" PARENT_SCOPE) + endif() +endfunction() + +macro (pvs_studio_append_flags_from_property CXX C DIR PREFIX) + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (PROP ${PROPERTY}) + pvs_studio_join_path(PROP "${DIR}" "${PROP}") + + if (APPLE AND "${PREFIX}" STREQUAL "-I" AND IS_ABSOLUTE "${PROP}" AND "${PROP}" MATCHES "\\.framework$") + get_filename_component(FRAMEWORK "${PROP}" DIRECTORY) + list(APPEND "${CXX}" "-iframework") + list(APPEND "${CXX}" "${FRAMEWORK}") + list(APPEND "${C}" "-iframework") + list(APPEND "${C}" "${FRAMEWORK}") + pvs_studio_log("framework: ${FRAMEWORK}") + elseif (NOT "${PROP}" STREQUAL "") + list(APPEND "${CXX}" "${PREFIX}${PROP}") + list(APPEND "${C}" "${PREFIX}${PROP}") + endif() + endforeach() + endif() +endmacro() + +macro (pvs_studio_append_standard_flag FLAGS STANDARD) + if ("${STANDARD}" MATCHES "^(99|11|14|17|20)$") + if ("${PVS_STUDIO_PREPROCESSOR}" MATCHES "gcc|clang") + list(APPEND "${FLAGS}" "-std=c++${STANDARD}") + endif() + endif() +endmacro() + +function (pvs_studio_set_directory_flags DIRECTORY CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" INCLUDE_DIRECTORIES) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" COMPILE_DEFINITIONS) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction() + +function (pvs_studio_set_target_flags TARGET CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + if (NOT MSVC) + list(APPEND CXX_FLAGS "$<$:--sysroot=${CMAKE_SYSROOT}>") + list(APPEND C_FLAGS "$<$:--sysroot=${CMAKE_SYSROOT}>") + endif() + + set(prop_incdirs "$") + list(APPEND CXX_FLAGS "$<$:-I$-I>>") + list(APPEND C_FLAGS "$<$:-I$-I>>") + + set(prop_compdefs "$") + list(APPEND CXX_FLAGS "$<$:-D$-D>>") + list(APPEND C_FLAGS "$<$:-D$-D>>") + + set(prop_compopt "$") + list(APPEND CXX_FLAGS "$<$:$>>") + list(APPEND C_FLAGS "$<$:$>>") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction() + +function (pvs_studio_set_source_file_flags SOURCE) + set(LANGUAGE "") + + string(TOLOWER "${SOURCE}" SOURCE_LOWER) + if ("${LANGUAGE}" STREQUAL "" AND "${SOURCE_LOWER}" MATCHES "^.*\\.(c|cpp|cc|cx|cxx|cp|c\\+\\+)$") + if ("${SOURCE}" MATCHES "^.*\\.c$") + set(LANGUAGE C) + else() + set(LANGUAGE CXX) + endif() + endif() + + if ("${LANGUAGE}" STREQUAL "C") + set(CL_PARAMS ${PVS_STUDIO_C_FLAGS} ${PVS_STUDIO_TARGET_C_FLAGS} -DPVS_STUDIO) + elseif ("${LANGUAGE}" STREQUAL "CXX") + set(CL_PARAMS ${PVS_STUDIO_CXX_FLAGS} ${PVS_STUDIO_TARGET_CXX_FLAGS} -DPVS_STUDIO) + endif() + + set(PVS_STUDIO_LANGUAGE "${LANGUAGE}" PARENT_SCOPE) + set(PVS_STUDIO_CL_PARAMS "${CL_PARAMS}" PARENT_SCOPE) +endfunction() + +function (pvs_studio_analyze_file SOURCE SOURCE_DIR BINARY_DIR) + set(PLOGS ${PVS_STUDIO_PLOGS}) + pvs_studio_set_source_file_flags("${SOURCE}") + + get_filename_component(SOURCE "${SOURCE}" REALPATH) + + get_source_file_property(PROPERTY "${SOURCE}" HEADER_FILE_ONLY) + if (PROPERTY) + return() + endif() + + pvs_studio_relative_path(SOURCE_RELATIVE "${SOURCE_DIR}" "${SOURCE}") + pvs_studio_join_path(SOURCE "${SOURCE_DIR}" "${SOURCE}") + + set(LOG "${BINARY_DIR}/PVS-Studio/${SOURCE_RELATIVE}.plog") + get_filename_component(LOG "${LOG}" REALPATH) + get_filename_component(PARENT_DIR "${LOG}" DIRECTORY) + + if (EXISTS "${SOURCE}" AND NOT TARGET "${LOG}" AND NOT "${PVS_STUDIO_LANGUAGE}" STREQUAL "") + # A workaround to support implicit dependencies for ninja generators. + set(depPvsArg) + set(depCommandArg) + if (CMAKE_VERSION VERSION_GREATER 3.6 AND "${CMAKE_GENERATOR}" STREQUAL "Ninja") + pvs_studio_relative_path(relLog "${CMAKE_BINARY_DIR}" "${LOG}") + set(depPvsArg --dep-file "${LOG}.d" --dep-file-target "${relLog}") + set(depCommandArg DEPFILE "${LOG}.d") + endif() + + # https://public.kitware.com/Bug/print_bug_page.php?bug_id=14353 + # https://public.kitware.com/Bug/file/5436/expand_command.cmake + # + # It is a workaround to expand generator expressions. + set(cmdline "${PVS_STUDIO_BIN}" analyze + --output-file "${LOG}" + --source-file "${SOURCE}" + ${depPvsArg} + ${PVS_STUDIO_ARGS} + --cl-params "${PVS_STUDIO_CL_PARAMS}" "${SOURCE}") + + string(REPLACE ";" "$" cmdline "${cmdline}") + set(pvscmd "${CMAKE_COMMAND}" + -D "PVS_STUDIO_AS_SCRIPT=TRUE" + -D "PVS_STUDIO_COMMAND=${cmdline}" + -D "PVS_STUDIO_LOG_FILE=${LOG}" + -P "${PVS_STUDIO_SCRIPT}") + + add_custom_command(OUTPUT "${LOG}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${PARENT_DIR}" + COMMAND "${CMAKE_COMMAND}" -E remove_directory "${LOG}" + COMMAND ${pvscmd} + WORKING_DIRECTORY "${BINARY_DIR}" + DEPENDS "${SOURCE}" "${PVS_STUDIO_SUPPRESS_BASE}" "${PVS_STUDIO_DEPENDS}" + IMPLICIT_DEPENDS "${PVS_STUDIO_LANGUAGE}" "${SOURCE}" + ${depCommandArg} + VERBATIM + COMMENT "Analyzing ${PVS_STUDIO_LANGUAGE} file ${SOURCE_RELATIVE}") + list(APPEND PLOGS "${LOG}") + endif() + set(PVS_STUDIO_PLOGS "${PLOGS}" PARENT_SCOPE) +endfunction() + +function (pvs_studio_analyze_target TARGET DIR) + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}") + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + + get_target_property(PROPERTY "${TARGET}" SOURCES) + pvs_studio_relative_path(BINARY_DIR "${CMAKE_SOURCE_DIR}" "${DIR}") + if ("${BINARY_DIR}" MATCHES "^/.*$") + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "PVS-Studio/__${BINARY_DIR}") + else() + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "${BINARY_DIR}") + endif() + + file(MAKE_DIRECTORY "${BINARY_DIR}") + + pvs_studio_set_directory_flags("${DIR}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + pvs_studio_set_target_flags("${TARGET}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (SOURCE ${PROPERTY}) + pvs_studio_join_path(SOURCE "${DIR}" "${SOURCE}") + pvs_studio_analyze_file("${SOURCE}" "${DIR}" "${BINARY_DIR}") + endforeach() + endif() + + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}" PARENT_SCOPE) +endfunction() + +set(PVS_STUDIO_RECURSIVE_TARGETS) +set(PVS_STUDIO_RECURSIVE_TARGETS_NEW) + +macro(pvs_studio_get_recursive_targets TARGET) + get_target_property(libs "${TARGET}" LINK_LIBRARIES) + foreach (lib IN LISTS libs) + list(FIND PVS_STUDIO_RECURSIVE_TARGETS "${lib}" index) + if (TARGET "${lib}" AND "${index}" STREQUAL -1) + get_target_property(target_type "${lib}" TYPE) + if (NOT "${target_type}" STREQUAL "INTERFACE_LIBRARY") + list(APPEND PVS_STUDIO_RECURSIVE_TARGETS "${lib}") + list(APPEND PVS_STUDIO_RECURSIVE_TARGETS_NEW "${lib}") + pvs_studio_get_recursive_targets("${lib}") + endif() + endif() + endforeach() +endmacro() + +option(PVS_STUDIO_DISABLE OFF "Disable PVS-Studio targets") +option(PVS_STUDIO_DEBUG OFF "Add debug info") + +# pvs_studio_add_target +# Target options: +# ALL add PVS-Studio target to default build (default: off) +# TARGET target name of analysis target (default: pvs) +# ANALYZE targets... targets to analyze +# RECURSIVE analyze target's dependencies (requires CMake 3.5+) +# COMPILE_COMMANDS use compile_commands.json instead of targets (specified by the 'ANALYZE' option) to determine files for analysis +# (set CMAKE_EXPORT_COMPILE_COMMANDS, available only for Makefile and Ninja generators) +# +# Output options: +# OUTPUT prints report to stdout +# LOG path path to report (default: ${CMAKE_CURRENT_BINARY_DIR}/PVS-Studio.log) +# FORMAT format format of report +# MODE mode analyzers/levels filter (default: GA:1,2) +# HIDE_HELP do not print help message +# +# Analyzer options: +# PLATFORM name linux32/linux64 (default: linux64) +# PREPROCESSOR name preprocessor type: gcc/clang (default: auto detected) +# LICENSE path path to PVS-Studio.lic (default: ~/.config/PVS-Studio/PVS-Studio.lic) +# CONFIG path path to PVS-Studio.cfg +# CFG_TEXT text embedded PVS-Studio.cfg +# SUPPRESS_BASE path to suppress base file +# KEEP_COMBINED_PLOG do not delete combined plog file *.pvs.raw for further processing with plog-converter +# +# Misc options: +# DEPENDS targets.. additional target dependencies +# SOURCES path... list of source files to analyze +# BIN path path to pvs-studio-analyzer (Unix) or CompilerCommandsAnalyzer.exe (Windows) +# CONVERTER path path to plog-converter (Unix) or HtmlGenerator.exe (Windows) +# C_FLAGS flags... additional C_FLAGS +# CXX_FLAGS flags... additional CXX_FLAGS +# ARGS args... additional pvs-studio-analyzer/CompilerCommandsAnalyzer.exe flags +# CONVERTER_ARGS args... additional plog-converter/HtmlGenerator.exe flags +function (pvs_studio_add_target) + macro (default VAR VALUE) + if ("${${VAR}}" STREQUAL "") + set("${VAR}" "${VALUE}") + endif() + endmacro() + + set(PVS_STUDIO_SUPPORTED_PREPROCESSORS "gcc|clang|visualcpp") + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(DEFAULT_PREPROCESSOR "clang") + elseif (MSVC) + set(DEFAULT_PREPROCESSOR "visualcpp") + else() + set(DEFAULT_PREPROCESSOR "gcc") + endif() + + set(OPTIONAL OUTPUT ALL RECURSIVE HIDE_HELP KEEP_COMBINED_PLOG COMPILE_COMMANDS KEEP_INTERMEDIATE_FILES) + set(SINGLE LICENSE CONFIG TARGET LOG FORMAT BIN CONVERTER PLATFORM PREPROCESSOR CFG_TEXT SUPPRESS_BASE) + set(MULTI SOURCES C_FLAGS CXX_FLAGS ARGS DEPENDS ANALYZE MODE CONVERTER_ARGS) + cmake_parse_arguments(PVS_STUDIO "${OPTIONAL}" "${SINGLE}" "${MULTI}" ${ARGN}) + + + default(PVS_STUDIO_C_FLAGS "") + default(PVS_STUDIO_CXX_FLAGS "") + default(PVS_STUDIO_TARGET "pvs") + default(PVS_STUDIO_LOG "PVS-Studio.log") + + set(PATHS) + + if (WIN32) + # The registry value is only read when you do some cache operation on it. + # https://stackoverflow.com/questions/1762201/reading-registry-values-with-cmake + GET_FILENAME_COMPONENT(ROOT "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\ProgramVerificationSystems\\PVS-Studio;installDir]" ABSOLUTE CACHE) + + if (EXISTS "${ROOT}") + set(PATHS "${ROOT}") + else() + set(ROOT "PROGRAMFILES(X86)") + set(ROOT "$ENV{${ROOT}}/PVS-Studio") + string(REPLACE \\ / ROOT "${ROOT}") + + if (EXISTS "${ROOT}") + set(PATHS "${ROOT}") + else() + set(ROOT "PATH") + set(ROOT "$ENV{${ROOT}}") + set(PATHS "${ROOT}") + endif() + endif() + + + + default(PVS_STUDIO_BIN "CompilerCommandsAnalyzer.exe") + default(PVS_STUDIO_CONVERTER "HtmlGenerator.exe") + else() + default(PVS_STUDIO_BIN "pvs-studio-analyzer") + default(PVS_STUDIO_CONVERTER "plog-converter") + endif() + + find_program(PVS_STUDIO_BIN_PATH "${PVS_STUDIO_BIN}" ${PATHS}) + set(PVS_STUDIO_BIN "${PVS_STUDIO_BIN_PATH}") + + if (NOT EXISTS "${PVS_STUDIO_BIN}") + message(FATAL_ERROR "pvs-studio-analyzer is not found") + endif() + + find_program(PVS_STUDIO_CONVERTER_PATH "${PVS_STUDIO_CONVERTER}" ${PATHS}) + set(PVS_STUDIO_CONVERTER "${PVS_STUDIO_CONVERTER_PATH}") + + if (NOT EXISTS "${PVS_STUDIO_CONVERTER}") + message(FATAL_ERROR "plog-converter is not found") + endif() + + default(PVS_STUDIO_MODE "GA:1,2") + default(PVS_STUDIO_PREPROCESSOR "${DEFAULT_PREPROCESSOR}") + if (WIN32) + default(PVS_STUDIO_PLATFORM "x64") + else() + default(PVS_STUDIO_PLATFORM "linux64") + endif() + + string(REPLACE ";" "+" PVS_STUDIO_MODE "${PVS_STUDIO_MODE}") + + if ("${PVS_STUDIO_CONFIG}" STREQUAL "" AND NOT "${PVS_STUDIO_CFG_TEXT}" STREQUAL "") + set(PVS_STUDIO_CONFIG "${CMAKE_BINARY_DIR}/PVS-Studio.cfg") + + set(PVS_STUDIO_CONFIG_COMMAND "${CMAKE_COMMAND}" -E echo "${PVS_STUDIO_CFG_TEXT}" > "${PVS_STUDIO_CONFIG}") + + add_custom_command(OUTPUT "${PVS_STUDIO_CONFIG}" + COMMAND ${PVS_STUDIO_CONFIG_COMMAND} + WORKING_DIRECTORY "${BINARY_DIR}" + COMMENT "Generating PVS-Studio.cfg") + + list(APPEND PVS_STUDIO_DEPENDS "${PVS_STUDIO_CONFIG}") + endif() + if (NOT "${PVS_STUDIO_PREPROCESSOR}" MATCHES "^${PVS_STUDIO_SUPPORTED_PREPROCESSORS}$") + message(FATAL_ERROR "Preprocessor ${PVS_STUDIO_PREPROCESSOR} isn't supported. Available options: ${PVS_STUDIO_SUPPORTED_PREPROCESSORS}.") + endif() + + pvs_studio_append_standard_flag(PVS_STUDIO_CXX_FLAGS "${CMAKE_CXX_STANDARD}") + pvs_studio_set_directory_flags("${CMAKE_CURRENT_SOURCE_DIR}" PVS_STUDIO_CXX_FLAGS PVS_STUDIO_C_FLAGS) + + if (NOT "${PVS_STUDIO_LICENSE}" STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --lic-file "${PVS_STUDIO_LICENSE}") + endif() + + if (NOT ${PVS_STUDIO_CONFIG} STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --cfg "${PVS_STUDIO_CONFIG}") + endif() + + list(APPEND PVS_STUDIO_ARGS --platform "${PVS_STUDIO_PLATFORM}" + --preprocessor "${PVS_STUDIO_PREPROCESSOR}") + + if (NOT "${PVS_STUDIO_SUPPRESS_BASE}" STREQUAL "") + pvs_studio_join_path(PVS_STUDIO_SUPPRESS_BASE "${CMAKE_CURRENT_SOURCE_DIR}" "${PVS_STUDIO_SUPPRESS_BASE}") + list(APPEND PVS_STUDIO_ARGS --suppress-file "${PVS_STUDIO_SUPPRESS_BASE}") + endif() + + if (NOT "${CMAKE_CXX_COMPILER}" STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --cxx "${CMAKE_CXX_COMPILER}") + endif() + + if (NOT "${CMAKE_C_COMPILER}" STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --cc "${CMAKE_C_COMPILER}") + endif() + + if (PVS_STUDIO_KEEP_INTERMEDIATE_FILES) + list(APPEND PVS_STUDIO_ARGS --dump-files) + endif() + + string(REGEX REPLACE [123,:] "" ANALYZER_MODE ${PVS_STUDIO_MODE}) + if (NOT "$ANALYZER_MODE" STREQUAL "GA") + list (APPEND PVS_STUDIO_ARGS -a "${ANALYZER_MODE}") + endif() + + set(PVS_STUDIO_PLOGS "") + + set(PVS_STUDIO_RECURSIVE_TARGETS_NEW) + if (${PVS_STUDIO_RECURSIVE}) + foreach (TARGET IN LISTS PVS_STUDIO_ANALYZE) + list(APPEND PVS_STUDIO_RECURSIVE_TARGETS_NEW "${TARGET}") + pvs_studio_get_recursive_targets("${TARGET}") + endforeach() + endif() + + set(inc_path) + + foreach (TARGET ${PVS_STUDIO_ANALYZE}) + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}") + string(FIND "${TARGET}" ":" DELIM) + if ("${DELIM}" GREATER "-1") + math(EXPR DELIMI "${DELIM}+1") + string(SUBSTRING "${TARGET}" "${DELIMI}" "-1" DIR) + string(SUBSTRING "${TARGET}" "0" "${DELIM}" TARGET) + pvs_studio_join_path(DIR "${CMAKE_CURRENT_SOURCE_DIR}" "${DIR}") + else() + get_target_property(TARGET_SOURCE_DIR "${TARGET}" SOURCE_DIR) + if (EXISTS "${TARGET_SOURCE_DIR}") + set(DIR "${TARGET_SOURCE_DIR}") + endif() + endif() + pvs_studio_analyze_target("${TARGET}" "${DIR}") + list(APPEND PVS_STUDIO_DEPENDS "${TARGET}") + + if ("${inc_path}" STREQUAL "") + set(inc_path "$") + else() + set(inc_path "${inc_path}$$") + endif() + endforeach() + + foreach (TARGET ${PVS_STUDIO_RECURSIVE_TARGETS_NEW}) + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}") + get_target_property(TARGET_SOURCE_DIR "${TARGET}" SOURCE_DIR) + if (EXISTS "${TARGET_SOURCE_DIR}") + set(DIR "${TARGET_SOURCE_DIR}") + endif() + pvs_studio_analyze_target("${TARGET}" "${DIR}") + list(APPEND PVS_STUDIO_DEPENDS "${TARGET}") + endforeach() + + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + foreach (SOURCE ${PVS_STUDIO_SOURCES}) + pvs_studio_analyze_file("${SOURCE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") + endforeach() + + if (PVS_STUDIO_COMPILE_COMMANDS) + set(COMPILE_COMMANDS_LOG "${PVS_STUDIO_LOG}.pvs.analyzer.raw") + if (NOT CMAKE_EXPORT_COMPILE_COMMANDS) + message(FATAL_ERROR "You should set CMAKE_EXPORT_COMPILE_COMMANDS to TRUE") + endif() + add_custom_command( + OUTPUT "${COMPILE_COMMANDS_LOG}" + COMMAND "${PVS_STUDIO_BIN}" analyze -i + --output-file "${COMPILE_COMMANDS_LOG}.always" + ${PVS_STUDIO_ARGS} + COMMENT "Analyzing with PVS-Studio" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + DEPENDS "${PVS_STUDIO_SUPPRESS_BASE}" "${PVS_STUDIO_DEPENDS}" + ) + list(APPEND PVS_STUDIO_PLOGS_LOGS "${COMPILE_COMMANDS_LOG}.always") + list(APPEND PVS_STUDIO_PLOGS_DEPENDENCIES "${COMPILE_COMMANDS_LOG}") + endif() + + pvs_studio_relative_path(LOG_RELATIVE "${CMAKE_BINARY_DIR}" "${PVS_STUDIO_LOG}") + if (PVS_STUDIO_PLOGS OR PVS_STUDIO_COMPILE_COMMANDS) + if (WIN32) + string(REPLACE / \\ PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}") + endif() + if (WIN32) + set(COMMANDS COMMAND type ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_LOGS} > "${PVS_STUDIO_LOG}" 2>nul || cd .) + else() + set(COMMANDS COMMAND cat ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_LOGS} > "${PVS_STUDIO_LOG}" 2>/dev/null || true) + endif() + set(COMMENT "Generating ${LOG_RELATIVE}") + if (NOT "${PVS_STUDIO_FORMAT}" STREQUAL "" OR PVS_STUDIO_OUTPUT) + if ("${PVS_STUDIO_FORMAT}" STREQUAL "") + set(PVS_STUDIO_FORMAT "errorfile") + endif() + if (PVS_STUDIO_HIDE_HELP) + set(PVS_STUDIO_CONVERTER_ARGS ${PVS_STUDIO_CONVERTER_ARGS} --noHelpMessages) + endif() + if (PVS_STUDIO_OUTPUT) + set(PVS_STUDIO_CONVERTER_ARGS ${PVS_STUDIO_CONVERTER_ARGS} --stdout) + endif() + list(APPEND COMMANDS + COMMAND "${CMAKE_COMMAND}" -E remove -f "${PVS_STUDIO_LOG}.pvs.raw" + COMMAND "${CMAKE_COMMAND}" -E rename "${PVS_STUDIO_LOG}" "${PVS_STUDIO_LOG}.pvs.raw" + COMMAND "${PVS_STUDIO_CONVERTER}" "${PVS_STUDIO_CONVERTER_ARGS}" -t "${PVS_STUDIO_FORMAT}" "${PVS_STUDIO_LOG}.pvs.raw" -o "${PVS_STUDIO_LOG}" -a "${PVS_STUDIO_MODE}") + if (NOT PVS_STUDIO_KEEP_COMBINED_PLOG) + list(APPEND COMMANDS COMMAND "${CMAKE_COMMAND}" -E remove -f "${PVS_STUDIO_LOG}.pvs.raw") + endif() + endif() + else() + set(COMMANDS COMMAND "${CMAKE_COMMAND}" -E touch "${PVS_STUDIO_LOG}") + set(COMMENT "Generating ${LOG_RELATIVE}: no sources found") + endif() + + if (WIN32) + string(REPLACE / \\ PVS_STUDIO_LOG "${PVS_STUDIO_LOG}") + endif() + + if (CMAKE_GENERATOR STREQUAL "Unix Makefiles") + get_filename_component(LOG_NAME ${LOG_RELATIVE} NAME) + set(LOG_TARGET "${PVS_STUDIO_TARGET}-${LOG_NAME}-log") + add_custom_target("${LOG_TARGET}" + BYPRODUCTS "${PVS_STUDIO_LOG}" + ${COMMANDS} + COMMENT "${COMMENT}" + DEPENDS ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_DEPENDENCIES} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + else() + set(LOG_TARGET "${PVS_STUDIO_LOG}") + add_custom_command(OUTPUT "${LOG_TARGET}" + ${COMMANDS} + COMMENT "${COMMENT}" + DEPENDS ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_DEPENDENCIES} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + endif() + + if (PVS_STUDIO_ALL) + set(ALL "ALL") + else() + set(ALL "") + endif() + + add_custom_target("${PVS_STUDIO_TARGET}" ${ALL} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + DEPENDS ${PVS_STUDIO_DEPENDS} "${LOG_TARGET}") + + # A workaround to add implicit dependencies of source files from include directories + set_target_properties("${PVS_STUDIO_TARGET}" PROPERTIES INCLUDE_DIRECTORIES "${inc_path}") + + if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") + set(props_file "${CMAKE_BINARY_DIR}/${PVS_STUDIO_TARGET}.user.props") + file(WRITE "${props_file}" [=[ + + + + + + + + true + + + +]=]) + set_target_properties("${PVS_STUDIO_TARGET}" PROPERTIES VS_USER_PROPS "${props_file}") + endif() +endfunction() diff --git a/MyxxCMake/sanitizers/FindASan.cmake b/MyxxCMake/sanitizers/FindASan.cmake new file mode 100644 index 0000000..9bdd10a --- /dev/null +++ b/MyxxCMake/sanitizers/FindASan.cmake @@ -0,0 +1,45 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault 2015-2016 RWTH Aachen University, Federal Republic of Germany +# 2021 Markus Eggenbauer +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +# associated documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, distribute, +# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or +# substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +# NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +# OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. + "-g -fsanitize=address -fno-omit-frame-pointer" "-g -fsanitize=address" + # Older deprecated flag for ASan + "-g -faddress-sanitizer") + +include(sanitize-helpers) + +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" "ASan") + +find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) +mark_as_advanced(ASan_WRAPPER) + +function(add_sanitize_address TARGET) + # TODO: check conditions for target: + # if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) + # message(FATAL_ERROR "AddressSanitizer is not compatible with ThreadSanitizer or MemorySanitizer.") + # endif () + + sanitizer_check_target(${TARGET}) + sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") +endfunction() diff --git a/MyxxCMake/sanitizers/FindCFI.cmake b/MyxxCMake/sanitizers/FindCFI.cmake new file mode 100644 index 0000000..d216cb6 --- /dev/null +++ b/MyxxCMake/sanitizers/FindCFI.cmake @@ -0,0 +1,42 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault 2015-2016 RWTH Aachen University, Federal Republic of Germany +# 2021 Markus Eggenbauer +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +# associated documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, distribute, +# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or +# substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +# NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +# OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +option(SANITIZE_CFI "Enable Control Flow Integrity (CFI) for sanitized targets." OFF) + +set(FLAG_CANDIDATES + # FIXME: Brief comment on why the additional flags + # In this case you need a + # linker that does optimization at + # linking time such as LLVM lld or GNU gold. + "-g -fsanitize=cfi -fvisibility=hidden -flto -fuse-ld=lld") + +# There might be some conflict with the other sanitizer +# hence it might need an if statement here. + +# add some handy functions +include(sanitize-helpers) + +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ControlFlowIntegrity" "CFI") + +function(add_sanitize_cfi TARGET) + sanitizer_check_target(${TARGET}) + sanitizer_add_flags(${TARGET} "ControlFlowIntegrity" "CFI") +endfunction() diff --git a/MyxxCMake/sanitizers/FindLeakSan.cmake b/MyxxCMake/sanitizers/FindLeakSan.cmake new file mode 100644 index 0000000..b52ca57 --- /dev/null +++ b/MyxxCMake/sanitizers/FindLeakSan.cmake @@ -0,0 +1,37 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# 2019 Barcelona Supercomputing Center +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +option(SANITIZE_LEAK "Enable LeakSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES "-g -fsanitize=leak") + +include(sanitize-helpers) + +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "LeakSanitizer" "LeakSan") + +function(add_sanitize_leak TARGET) + sanitizer_check_target(${TARGET}) + sanitizer_add_flags(${TARGET} "LeakSanitizer" "LeakSan") +endfunction() diff --git a/MyxxCMake/sanitizers/FindMSan.cmake b/MyxxCMake/sanitizers/FindMSan.cmake new file mode 100644 index 0000000..38c98a5 --- /dev/null +++ b/MyxxCMake/sanitizers/FindMSan.cmake @@ -0,0 +1,49 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# 2021 Markus Eggenbauer +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES "-g -fsanitize=memory") + +include(sanitize-helpers) + +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(NOTICE "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for Linux systems only.") +elseif(NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(NOTICE "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for 64bit systems only.") +else() + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" "MSan") +endif() + +function(add_sanitize_memory TARGET) + # TODO: check conditions for target: + # if (SANITIZE_MEMORY AND (SANITIZE_THREAD OR SANITIZE_ADDRESS)) + # message(FATAL_ERROR "AddressSanitizer is not compatible with ThreadSanitizer or MemorySanitizer.") + # endif () + sanitizer_check_target(${TARGET}) + sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") +endfunction() diff --git a/MyxxCMake/sanitizers/FindSS.cmake b/MyxxCMake/sanitizers/FindSS.cmake new file mode 100644 index 0000000..8b5ee84 --- /dev/null +++ b/MyxxCMake/sanitizers/FindSS.cmake @@ -0,0 +1,37 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault 2015-2016 RWTH Aachen University, Federal Republic of Germany +# 2021 Markus Eggenbauer +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +# associated documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, distribute, +# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or +# substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +# NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +# OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +option(SANITIZE_SS "Enable SafeStack for sanitized targets." OFF) + +set(FLAG_CANDIDATES "-g -fsanitize=safe-stack") + +# There might be some conflict with the other sanitizer +# hence it might need an if statement here. + +# add some handy functions +include(sanitize-helpers) + +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "SafeStack" "SS") + +function(add_sanitize_ss TARGET) + sanitizer_check_target(${TARGET}) + sanitizer_add_flags(${TARGET} "SafeStack" "SS") +endfunction() diff --git a/MyxxCMake/sanitizers/FindSanitizers.cmake b/MyxxCMake/sanitizers/FindSanitizers.cmake new file mode 100644 index 0000000..53d1b27 --- /dev/null +++ b/MyxxCMake/sanitizers/FindSanitizers.cmake @@ -0,0 +1,109 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# 2021 Markus Eggenbauer +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# If any of the used compiler is a GNU compiler, add a second option to static +# link against the sanitizers. +option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) + +set(FIND_QUIETLY_FLAG "") +if(DEFINED Sanitizers_FIND_QUIETLY) + set(FIND_QUIETLY_FLAG "QUIET") +endif() + +find_package(ASan ${FIND_QUIETLY_FLAG}) +find_package(TSan ${FIND_QUIETLY_FLAG}) +find_package(MSan ${FIND_QUIETLY_FLAG}) +find_package(UBSan ${FIND_QUIETLY_FLAG}) +find_package(LeakSan ${FIND_QUIETLY_FLAG}) +find_package(CFI ${FIND_QUIETLY_FLAG}) +find_package(SS ${FIND_QUIETLY_FLAG}) + +function(sanitizer_add_blacklist_file FILE) + if(NOT IS_ABSOLUTE ${FILE}) + set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") + endif() + get_filename_component(FILE "${FILE}" REALPATH) + + sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" "SanitizerBlacklist" "SanBlist") +endfunction() + +function(add_sanitizers ...) + # If no sanitizer is enabled, return immediately. + if(NOT + (SANITIZE_ADDRESS + OR SANITIZE_MEMORY + OR SANITIZE_THREAD + OR SANITIZE_UNDEFINED + OR SANITIZE_LEAK + OR SANITIZE_CFI + OR SANITIZE_SS)) + message(STATUS "No sanitizer selected.") + return() + endif() + + if(SANITIZE_ADDRESS AND SANITIZE_THREAD) + message( + FATAL_ERROR + "Incompatible Sanitizer combination enabled: AddressSanitizer, ThreadSanitizer") + endif() + if(SANITIZE_ADDRESS AND SANITIZE_MEMORY) + message( + FATAL_ERROR + "Incompatible Sanitizer combination enabled: AddressSanitizer, MemorySanitizer") + endif() + + if(SANITIZE_MEMORY AND SANITIZE_THREAD) + message( + FATAL_ERROR + "Incompatible Sanitizer combination enabled: MemorySanitizer, ThreadSanitizer") + endif() + + foreach(TARGET ${ARGV}) + sanitizer_check_target(${TARGET}) + + # Add sanitizers for target. + if(SANITIZE_ADDRESS) + add_sanitize_address(${TARGET}) + endif() + if(SANITIZE_THREAD) + add_sanitize_thread(${TARGET}) + endif() + if(SANITIZE_MEMORY) + add_sanitize_memory(${TARGET}) + endif() + if(SANITIZE_UNDEFINED) + add_sanitize_undefined(${TARGET}) + endif() + if(SANITIZE_LEAK) + add_sanitize_leak(${TARGET}) + endif() + if(SANITIZE_CFI) + add_sanitize_cfi(${TARGET}) + endif() + if(SANITIZE_SS) + add_sanitize_ss(${TARGET}) + endif() + endforeach() +endfunction(add_sanitizers) diff --git a/MyxxCMake/sanitizers/FindTSan.cmake b/MyxxCMake/sanitizers/FindTSan.cmake new file mode 100644 index 0000000..3c1c179 --- /dev/null +++ b/MyxxCMake/sanitizers/FindTSan.cmake @@ -0,0 +1,53 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# 2021 Markus Eggenbauer +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES "-g -fsanitize=thread") + +include(sanitize-helpers) + +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + message(NOTICE "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for Linux systems and macOS only.") + set(SANITIZE_THREAD Off CACHE BOOL "Enable ThreadSanitizer for sanitized targets." FORCE) +elseif(NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(NOTICE "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for 64bit systems only.") + set(SANITIZE_THREAD Off CACHE BOOL "Enable ThreadSanitizer for sanitized targets." FORCE) +else() + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" "TSan") +endif() + +function(add_sanitize_thread TARGET) + # ThreadSanitizer is not compatible with MemorySanitizer. + # + # if(SANITIZE_THREAD AND SANITIZE_MEMORY) + # message(FATAL_ERROR "ThreadSanitizer is not compatible with " "MemorySanitizer.") + # endif() + + sanitizer_check_target(${TARGET}) + sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") +endfunction() diff --git a/MyxxCMake/sanitizers/FindUBSan.cmake b/MyxxCMake/sanitizers/FindUBSan.cmake new file mode 100644 index 0000000..c248d9c --- /dev/null +++ b/MyxxCMake/sanitizers/FindUBSan.cmake @@ -0,0 +1,37 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# 2021 Markus Eggenbauer +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +option(SANITIZE_UNDEFINED "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES "-g -fsanitize=undefined") + +include(sanitize-helpers) + +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "UndefinedBehaviorSanitizer" "UBSan") + +function(add_sanitize_undefined TARGET) + sanitizer_check_target(${TARGET}) + sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") +endfunction() diff --git a/MyxxCMake/sanitizers/asan-wrapper b/MyxxCMake/sanitizers/asan-wrapper new file mode 100755 index 0000000..5d54103 --- /dev/null +++ b/MyxxCMake/sanitizers/asan-wrapper @@ -0,0 +1,55 @@ +#!/bin/sh + +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# This script is a wrapper for AddressSanitizer. In some special cases you need +# to preload AddressSanitizer to avoid error messages - e.g. if you're +# preloading another library to your application. At the moment this script will +# only do something, if we're running on a Linux platform. OSX might not be +# affected. + + +# Exit immediately, if platform is not Linux. +if [ "$(uname)" != "Linux" ] +then + exec $@ +fi + + +# Get the used libasan of the application ($1). If a libasan was found, it will +# be prepended to LD_PRELOAD. +libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1) +if [ -n "$libasan" ] +then + if [ -n "$LD_PRELOAD" ] + then + export LD_PRELOAD="$libasan:$LD_PRELOAD" + else + export LD_PRELOAD="$libasan" + fi +fi + +# Execute the application. +exec $@ diff --git a/MyxxCMake/sanitizers/sanitize-helpers.cmake b/MyxxCMake/sanitizers/sanitize-helpers.cmake new file mode 100644 index 0000000..8c91f9f --- /dev/null +++ b/MyxxCMake/sanitizers/sanitize-helpers.cmake @@ -0,0 +1,199 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# 2021 Markus Eggenbauer +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Helper function to get the language of a source file. +function(sanitizer_lang_of_source FILE RETURN_VAR) + get_filename_component(LONGEST_EXT "${FILE}" EXT) + # If extension is empty return. This can happen for extensionless headers + if("${LONGEST_EXT}" STREQUAL "") + set(${RETURN_VAR} "" PARENT_SCOPE) + return() + endif() + # Get shortest extension as some files can have dot in their names + string(REGEX REPLACE "^.*(\\.[^.]+)$" "\\1" FILE_EXT ${LONGEST_EXT}) + string(TOLOWER "${FILE_EXT}" FILE_EXT) + string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach(LANG ${ENABLED_LANGUAGES}) + list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) + if(NOT ${TEMP} EQUAL -1) + set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) + return() + endif() + endforeach() + + set(${RETURN_VAR} "" PARENT_SCOPE) +endfunction() + +# Helper function to get compilers used by a target. +function(sanitizer_target_compilers TARGET RETURN_VAR) + # Check if all sources for target use the same compiler. If a target uses + # e.g. C and Fortran mixed and uses different compilers (e.g. clang and + # gfortran) this can trigger huge problems, because different compilers may + # use different implementations for sanitizers. + set(BUFFER "") + get_target_property(TSOURCES ${TARGET} SOURCES) + foreach(FILE ${TSOURCES}) + # If expression was found, FILE is a generator-expression for an object + # library. Object libraries will be ignored. + string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) + if("${_file}" STREQUAL "") + sanitizer_lang_of_source(${FILE} LANG) + if(LANG) + list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) + endif() + endif() + endforeach() + + list(REMOVE_DUPLICATES BUFFER) + set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) +endfunction() + +# Helper function to check compiler flags for language compiler. +function(sanitizer_check_compiler_flag FLAG LANG VARIABLE) + if(${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif(${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif(${LANG} STREQUAL "Fortran") + # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible + # with older Cmake versions, we will check if this module is present + # before we use it. Otherwise we will define Fortran coverage support as + # not available. + include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) + if(INCLUDED) + CHECK_Fortran_COMPILER_FLAG("${FLAG}" ${VARIABLE}) + elseif(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VARIABLE}") + message(STATUS "Performing Test ${VARIABLE}" " - Failed (Check not supported)") + endif() + endif() +endfunction() + +# Helper function to test compiler flags. +function(sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) + set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach(LANG ${ENABLED_LANGUAGES}) + # Sanitizer flags are not dependend on language, but the used compiler. + # So instead of searching flags foreach language, search flags foreach + # compiler used. + set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) + if(NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) + foreach(FLAG ${FLAG_CANDIDATES}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") + endif() + + set(CMAKE_REQUIRED_FLAGS "${FLAG}") + unset(${PREFIX}_FLAG_DETECTED CACHE) + sanitizer_check_compiler_flag("${FLAG}" ${LANG} ${PREFIX}_FLAG_DETECTED) + + if(${PREFIX}_FLAG_DETECTED) + # If compiler is a GNU compiler, search for static flag, if + # SANITIZE_LINK_STATIC is enabled. + if(SANITIZE_LINK_STATIC AND (COMPILER STREQUAL "GNU")) + string(TOLOWER ${PREFIX} PREFIX_lower) + sanitizer_check_compiler_flag("-static-lib${PREFIX_lower}" ${LANG} + ${PREFIX}_STATIC_FLAG_DETECTED) + + if(${PREFIX}_STATIC_FLAG_DETECTED) + set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") + endif() + endif() + + set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" + CACHE STRING "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + break() + endif() + endforeach() + + if(NOT ${PREFIX}_FLAG_DETECTED) + set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + + message( + NOTICE "${NAME} is not available for ${COMPILER} " + "compiler. Targets using this compiler will be " "compiled without ${NAME}.") + endif() + endif() + endforeach() +endfunction() + +# Helper to assign sanitizer flags for TARGET. +function(sanitizer_add_flags TARGET NAME PREFIX) + # Get list of compilers used by target and check, if sanitizer is available + # for this target. Other compiler checks like check for conflicting + # compilers will be done in add_sanitizers function. + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "") + return() + endif() + + # Set compile- and link-flags for target. + set_property(TARGET ${TARGET} APPEND_STRING PROPERTY COMPILE_FLAGS + " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING PROPERTY COMPILE_FLAGS + " ${SanBlist_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS + " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") +endfunction() + +macro(sanitizer_check_target TARGET) + # Check if this target will be compiled by exactly one compiler. Other- + # wise sanitizers can't be used and a warning should be printed once. + get_target_property(TARGET_TYPE ${TARGET} TYPE) + if(TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") + message(NOTICE "Can't use any sanitizers for target ${TARGET}, " + "because it is an interface library and cannot be " "compiled directly.") + return() + endif() + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if(NUM_COMPILERS GREATER 1) + message( + NOTICE "Can't use any sanitizers for target ${TARGET}, " + "because it will be compiled by incompatible compilers. " + "Target will be compiled without sanitizers.") + return() + + # If the target is compiled by no or no known compiler, give a warning. + elseif(NUM_COMPILERS EQUAL 0) + message( + NOTICE "Sanitizers for target ${TARGET} may not be" + " usable, because it uses no or an unknown compiler. " + "This is a false warning for targets using only " "object lib(s) as input.") + return() + endif() +endmacro(sanitizer_check_target)