diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f6333e9..826b4674 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -include(${CMAKE_SOURCE_DIR}/features/qskconfig.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/QskBuild.cmake) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) if(QT_VERSION_MAJOR VERSION_EQUAL "5") diff --git a/cmake/ECMEnableSanitizers.cmake b/cmake/ECMEnableSanitizers.cmake new file mode 100644 index 00000000..dcdb8621 --- /dev/null +++ b/cmake/ECMEnableSanitizers.cmake @@ -0,0 +1,172 @@ +# SPDX-FileCopyrightText: 2014 Mathieu Tarral +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +ECMEnableSanitizers +------------------- + +Enable compiler sanitizer flags. + +The following sanitizers are supported: + +- Address Sanitizer +- Memory Sanitizer +- Thread Sanitizer +- Leak Sanitizer +- Undefined Behaviour Sanitizer + +All of them are implemented in Clang, depending on your version, and +there is an work in progress in GCC, where some of them are currently +implemented. + +This module will check your current compiler version to see if it +supports the sanitizers that you want to enable + +Usage +===== + +Simply add:: + + include(ECMEnableSanitizers) + +to your ``CMakeLists.txt``. Note that this module is included in +KDECompilerSettings, so projects using that module do not need to also +include this one. + +The sanitizers are not enabled by default. Instead, you must set +``ECM_ENABLE_SANITIZERS`` (either in your ``CMakeLists.txt`` or on the +command line) to a semicolon-separated list of sanitizers you wish to enable. +The options are: + +- address +- memory +- thread +- leak +- undefined +- fuzzer-no-link +- fuzzer + +The sanitizers "address", "memory" and "thread" are mutually exclusive. You +cannot enable two of them in the same build. + +"leak" requires the "address" sanitizer. + +.. note:: + + To reduce the overhead induced by the instrumentation of the sanitizers, it + is advised to enable compiler optimizations (``-O1`` or higher). + +Example +======= + +This is an example of usage:: + + mkdir build + cd build + cmake -DECM_ENABLE_SANITIZERS='address;leak;undefined' .. + +.. note:: + + Most of the sanitizers will require Clang. To enable it, use:: + + -DCMAKE_CXX_COMPILER=clang++ + +Since 1.3.0. +#]=======================================================================] + +# MACRO check_compiler_version +#----------------------------- +macro (check_compiler_version gcc_required_version clang_required_version msvc_required_version) + if ( + ( + CMAKE_CXX_COMPILER_ID MATCHES "GNU" + AND + CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${gcc_required_version} + ) + OR + ( + CMAKE_CXX_COMPILER_ID MATCHES "Clang" + AND + CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${clang_required_version} + ) + OR + ( + CMAKE_CXX_COMPILER_ID MATCHES "MSVC" + AND + CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${msvc_required_version} + ) + ) + # error ! + message(FATAL_ERROR "You ask to enable the sanitizer ${CUR_SANITIZER}, + but your compiler ${CMAKE_CXX_COMPILER_ID} version ${CMAKE_CXX_COMPILER_VERSION} + does not support it ! + You should use at least GCC ${gcc_required_version}, Clang ${clang_required_version} + or MSVC ${msvc_required_version} + (99.99 means not implemented yet)") + endif () +endmacro () + +# MACRO check_compiler_support +#------------------------------ +macro (enable_sanitizer_flags sanitize_option) + if (${sanitize_option} MATCHES "address") + check_compiler_version("4.8" "3.1" "19.28") + if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set(XSAN_COMPILE_FLAGS "-fsanitize=address") + else() + set(XSAN_COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls") + set(XSAN_LINKER_FLAGS "asan") + endif() + elseif (${sanitize_option} MATCHES "thread") + check_compiler_version("4.8" "3.1" "99.99") + set(XSAN_COMPILE_FLAGS "-fsanitize=thread") + set(XSAN_LINKER_FLAGS "tsan") + elseif (${sanitize_option} MATCHES "memory") + check_compiler_version("99.99" "3.1" "99.99") + set(XSAN_COMPILE_FLAGS "-fsanitize=memory") + elseif (${sanitize_option} MATCHES "leak") + check_compiler_version("4.9" "3.4" "99.99") + set(XSAN_COMPILE_FLAGS "-fsanitize=leak") + set(XSAN_LINKER_FLAGS "lsan") + elseif (${sanitize_option} MATCHES "undefined") + check_compiler_version("4.9" "3.1" "99.99") + set(XSAN_COMPILE_FLAGS "-fsanitize=undefined -fsanitize=float-divide-by-zero -fno-omit-frame-pointer -fno-optimize-sibling-calls") + elseif (${sanitize_option} MATCHES "fuzzer-no-link") + check_compiler_version("99.99" "6.0" "99.99") + set(XSAN_COMPILE_FLAGS "-fsanitize=fuzzer-no-link") + set(XSAN_LINKER_FLAGS "-fsanitize=fuzzer-no-link") + elseif (${sanitize_option} MATCHES "fuzzer") + check_compiler_version("99.99" "6.0" "99.99") + set(XSAN_COMPILE_FLAGS "-fsanitize=fuzzer") + else () + message(FATAL_ERROR "Compiler sanitizer option \"${sanitize_option}\" not supported.") + endif () +endmacro () + +if (ECM_ENABLE_SANITIZERS) + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # for each element of the ECM_ENABLE_SANITIZERS list + foreach ( CUR_SANITIZER ${ECM_ENABLE_SANITIZERS} ) + # lowercase filter + string(TOLOWER ${CUR_SANITIZER} CUR_SANITIZER) + # check option and enable appropriate flags + enable_sanitizer_flags ( ${CUR_SANITIZER} ) + # TODO: GCC will not link pthread library if enabled ASan + if(CMAKE_C_COMPILER_ID MATCHES "Clang") + set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${XSAN_COMPILE_FLAGS}" ) + endif() + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${XSAN_COMPILE_FLAGS}" ) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + link_libraries(${XSAN_LINKER_FLAGS}) + endif() + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + string(REPLACE "-Wl,--no-undefined" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") + string(REPLACE "-Wl,--no-undefined" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") + endif () + endforeach() + else() + message(STATUS "Tried to enable sanitizers (-DECM_ENABLE_SANITIZERS=${ECM_ENABLE_SANITIZERS}), \ +but compiler (${CMAKE_CXX_COMPILER_ID}) does not have sanitizer support") + endif() +endif() diff --git a/features/qskconfig.cmake b/cmake/QskBuild.cmake similarity index 84% rename from features/qskconfig.cmake rename to cmake/QskBuild.cmake index b40efdda..10624572 100644 --- a/features/qskconfig.cmake +++ b/cmake/QskBuild.cmake @@ -5,33 +5,15 @@ set(QSK_VERSION ${CMAKE_PROJECT_VERSION}) -# trying the PREFIX environment variable first -set(QSK_INSTALL_PREFIX $ENV{PREFIX}) # TODO still required? - -if("${QSK_INSTALL_PREFIX}" STREQUAL "") - set(QSK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") # TODO where does QT_INSTALL_PREFIX come from? - - if (UNIX) - set(QSK_INSTALL_PREFIX /usr/local/qskinny-${QSK_VERSION}) - endif() - - if (MSVC) - set(QSK_INSTALL_PREFIX C:/Qskinny-${QSK_VERSION}) - endif() -endif() - # TODO find compiler flag equivalent list(APPEND CONFIG no_private_qt_headers_warning) list(APPEND CONFIG warn_on) list(APPEND CONFIG no_keywords) -list(APPEND CONFIG silent) #list(APPEND CONFIG -= depend_includepath) # TODO was -= list(APPEND CONFIG strict_c++) list(APPEND CONFIG c++17) list(APPEND CONFIG pedantic) list(APPEND CONFIG hide_symbols) -#list(APPEND CONFIG += debug) -#list(APPEND CONFIG += sanitize) # TODO # use_no_rpath { @@ -140,15 +122,20 @@ if ("pedantic" IN_LIST CONFIG) endif() endif() -if ("sanitize" IN_LIST CONFIG) - list(APPEND CONFIG sanitizer) - list(APPEND CONFIG sanitize_address) - # CONFIG *= sanitize_undefined +# DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x000000 - if ("${CMAKE_CXX_COMPILER}" MATCHES ".*linux-g\\+\\+" OR "${CMAKE_CXX_COMPILER}" MATCHES ".*linux-g\\+\\+-64") - # set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -fsanitize-address-use-after-scope) - #QMAKE_LFLAGS *= -fsanitize-address-use-after-scope +set(LOCAL_CMAKE_RULES $ENV{QSK_LOCAL_CMAKE_RULES}) + +if(NOT "${LOCAL_CMAKE_RULES}" STREQUAL "") + + if(EXISTS ${LOCAL_CMAKE_RULES}) + + # When not working with the Qt/Creator it is often more convenient + # to include the specific options of your local build, than passing + # them all on the command line + + include(${LOCAL_CMAKE_RULES}) endif() endif() @@ -156,18 +143,21 @@ if (CMAKE_BUILD_TYPE EQUAL "Debug") add_compile_definitions(ITEM_STATISTICS=1) endif() -# DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x000000 +if ( NOT "${ECM_ENABLE_SANITIZERS}" STREQUAL "") -set(LOCAL_PRI $ENV{QSK_LOCAL_PRI}) + # see: https://api.kde.org/ecm/module/ECMEnableSanitizers.html + # + # a semicolon-separated list of sanitizers: + # - address + # - memory + # - thread + # - leak + # - undefined + # - fuzzer + # + # where “address”, “memory” and “thread” are mutually exclusive + # f.e: set(ECM_ENABLE_SANITIZERS address;leak;undefined) -if (${LOCAL_PRI}) # TODO not empty check + include(${CMAKE_SOURCE_DIR}/cmake/ECMEnableSanitizers.cmake) - if(EXISTS ${LOCAL_PRI}) - - # When not working with the Qt/Creator it is often more convenient - # to include the specific options of your local build, than passing - # them all on the command line - - include(${LOCAL_PRI}) - endif() endif()