From f8f5c16071710221231911fc2f6cd1e12ad804f9 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 24 Jun 2021 12:30:32 +0200 Subject: [PATCH] CMake: Propagate sanitizer flags to public projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure that Qt user projects build with sanitizer flags if Qt was configured with any of the sanitizers enabled. To compile Qt with sanitizer support enable any of Qt sanitizer features. Passing -DECM_ENABLE_SANITIZERS=address to CMake is NOT supported anymore. When configuring Qt using CMake directly, pass -DFEATURE_sanitizer_address=ON -DFEATURE_sanitizer_undefined=ON instead of -DECM_ENABLE_SANITIZERS=address;undefined When configuring Qt with the configure script pass -sanitize address -sanitize undefined as usual. QtConfig.cmake now records the sanitizer options that should be enabled for all consuming projects based on the enabled Qt features. This applies to internal Qt builds as well as well as tests an examples. The recorded sanitizer options are assigned to the ECM_ENABLE_SANITIZERS variable in the directory scope where find_package(Qt6) is called. The ECMEnableSanitizers module is included to add the necessary flags to all targets in that directory scope or its children. This behavior can be opted out by setting the QT_NO_ADD_SANITIZER_OPTIONS variable in projects that use Qt and might be handling sanitizer options differently. Amends 7e03bc39b8bcdaa4e83e72ac99e117561c124951 Fixes: QTBUG-87989 Task-number: QTBUG-92083 Change-Id: I2e3371147277bdf8f55a39abaa34478dea4853a6 Reviewed-by: Robert Löhning Reviewed-by: Fabian Kosmale (cherry picked from commit 19816826873ebb3a89b2173f8685a8d448410680) Reviewed-by: Qt Cherry-pick Bot --- cmake/QtBaseGlobalTargets.cmake | 57 ++++++++++++++++++------------ cmake/QtConfig.cmake.in | 14 ++++++++ cmake/QtPostProcessHelpers.cmake | 5 --- cmake/QtProcessConfigureArgs.cmake | 1 - cmake/QtSanitizerHelpers.cmake | 33 +++++------------ cmake/QtSetup.cmake | 4 --- cmake/configure-cmake-mapping.md | 3 +- qt_cmdline.cmake | 1 - 8 files changed, 58 insertions(+), 60 deletions(-) diff --git a/cmake/QtBaseGlobalTargets.cmake b/cmake/QtBaseGlobalTargets.cmake index 62cb7d73929..0e09d1a259c 100644 --- a/cmake/QtBaseGlobalTargets.cmake +++ b/cmake/QtBaseGlobalTargets.cmake @@ -21,28 +21,6 @@ file(RELATIVE_PATH __GlobalConfig_relative_path_from_bin_dir_to_cmake_config_dir ${__qt_bin_dir_absolute} ${__GlobalConfig_install_dir_absolute}) -# Generate and install Qt6 config file. -qt_internal_get_min_new_policy_cmake_version(min_new_policy_version) -qt_internal_get_max_new_policy_cmake_version(max_new_policy_version) -configure_package_config_file( - "${PROJECT_SOURCE_DIR}/cmake/QtConfig.cmake.in" - "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}Config.cmake" - INSTALL_DESTINATION "${__GlobalConfig_install_dir}" -) - -write_basic_package_version_file( - ${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}ConfigVersion.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY AnyNewerVersion -) - -qt_install(FILES - "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}Config.cmake" - "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}ConfigVersion.cmake" - DESTINATION "${__GlobalConfig_install_dir}" - COMPONENT Devel -) - # Configure and install the QtBuildInternals package. set(__build_internals_path_suffix "${INSTALL_CMAKE_NAMESPACE}BuildInternals") qt_path_join(__build_internals_build_dir ${QT_CONFIG_BUILD_DIR} ${__build_internals_path_suffix}) @@ -141,6 +119,10 @@ qt_internal_setup_public_platform_target() include(QtInternalTargets) qt_internal_run_common_config_tests() +# Setup sanitizer options for qtbase directory scope based on features computed above. +qt_internal_set_up_sanitizer_options() +include("${CMAKE_CURRENT_LIST_DIR}/3rdparty/extra-cmake-modules/modules/ECMEnableSanitizers.cmake") + set(__export_targets Platform GlobalConfig GlobalConfigPrivate @@ -160,6 +142,29 @@ qt_internal_export_modern_cmake_config_targets_file(TARGETS ${__export_targets} CONFIG_INSTALL_DIR ${__GlobalConfig_install_dir}) +# Generate and install Qt6 config file. Make sure it happens after the global feature evaluation so +# they can be accessed in the Config file if needed. +qt_internal_get_min_new_policy_cmake_version(min_new_policy_version) +qt_internal_get_max_new_policy_cmake_version(max_new_policy_version) +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/QtConfig.cmake.in" + "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}Config.cmake" + INSTALL_DESTINATION "${__GlobalConfig_install_dir}" +) + +write_basic_package_version_file( + "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}ConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) + +qt_install(FILES + "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}Config.cmake" + "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}ConfigVersion.cmake" + DESTINATION "${__GlobalConfig_install_dir}" + COMPONENT Devel +) + # Install internal CMake files. # The functions defined inside can not be used in public projects. # They can only be used while building Qt itself. @@ -270,7 +275,13 @@ if(QT_WILL_INSTALL) endif() # TODO: Check whether this is the right place to install these -qt_copy_or_install(DIRECTORY cmake/3rdparty DESTINATION "${__GlobalConfig_install_dir}") +qt_copy_or_install(DIRECTORY "cmake/3rdparty" DESTINATION "${__GlobalConfig_install_dir}") + +# In prefix builds we also need to copy the files into the build config directory, so that the +# build-dir Qt6Config.cmake finds the files when building other repos in a top-level build. +if(QT_WILL_INSTALL) + file(COPY "cmake/3rdparty" DESTINATION "${__GlobalConfig_build_dir}") +endif() # Install our custom Find modules, which will be used by the find_dependency() calls # inside the generated ModuleDependencies cmake files. diff --git a/cmake/QtConfig.cmake.in b/cmake/QtConfig.cmake.in index b0b4dd3a82d..cc3b2d7fced 100644 --- a/cmake/QtConfig.cmake.in +++ b/cmake/QtConfig.cmake.in @@ -66,6 +66,20 @@ if(NOT DEFINED QT_CMAKE_EXPORT_NAMESPACE) set(QT_CMAKE_EXPORT_NAMESPACE @QT_CMAKE_EXPORT_NAMESPACE@) endif() +# Propagate sanitizer flags to both internal Qt builds and user projects. +# Allow opt-out in case if downstream projects handle it in a different way. +set(QT_CONFIGURED_SANITIZER_OPTIONS "@ECM_ENABLE_SANITIZERS@") + +if(QT_CONFIGURED_SANITIZER_OPTIONS + AND NOT __qt_sanitizer_options_set + AND NOT QT_NO_ADD_SANITIZER_OPTIONS) + set(ECM_ENABLE_SANITIZERS "${QT_CONFIGURED_SANITIZER_OPTIONS}") + include( + "${CMAKE_CURRENT_LIST_DIR}/3rdparty/extra-cmake-modules/modules/ECMEnableSanitizers.cmake") +endif() +# Mark that the current directory scope has its sanitizer flags set. +set(__qt_sanitizer_options_set TRUE) + # Find required dependencies, if any. include(CMakeFindDependencyMacro) if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@Dependencies.cmake") diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake index 1cfba15be21..5d3e9492062 100644 --- a/cmake/QtPostProcessHelpers.cmake +++ b/cmake/QtPostProcessHelpers.cmake @@ -541,11 +541,6 @@ endif()\n") "set(QT_BUILD_TOOLS_WHEN_CROSSCOMPILING \"TRUE\" CACHE BOOL \"\" FORCE)\n") endif() - if(ECM_ENABLE_SANITIZERS) - string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS - "set(ECM_ENABLE_SANITIZERS \"${ECM_ENABLE_SANITIZERS}\" CACHE BOOL \"\" FORCE)\n") - endif() - # Save the default qpa platform. # Used by qtwayland/src/plugins/platforms/qwayland-generic/CMakeLists.txt. Otherwise # the DEFAULT_IF condition is evaluated incorrectly. diff --git a/cmake/QtProcessConfigureArgs.cmake b/cmake/QtProcessConfigureArgs.cmake index ba056290cc8..2a415454a8a 100644 --- a/cmake/QtProcessConfigureArgs.cmake +++ b/cmake/QtProcessConfigureArgs.cmake @@ -797,7 +797,6 @@ translate_string_input(platform QT_QMAKE_TARGET_MKSPEC) translate_string_input(xplatform QT_QMAKE_TARGET_MKSPEC) guess_compiler_from_mkspec() translate_string_input(qpa_default_platform QT_QPA_DEFAULT_PLATFORM) -translate_list_input(sanitize ECM_ENABLE_SANITIZERS) translate_path_input(android-sdk ANDROID_SDK_ROOT) translate_path_input(android-ndk ANDROID_NDK_ROOT) diff --git a/cmake/QtSanitizerHelpers.cmake b/cmake/QtSanitizerHelpers.cmake index 90a1f738cb8..ff747920582 100644 --- a/cmake/QtSanitizerHelpers.cmake +++ b/cmake/QtSanitizerHelpers.cmake @@ -1,42 +1,25 @@ -function(qt_internal_set_up_sanitizer_features) +# Computes which sanitizer options should be set based on features evaluated in qtbase. +# Sets ECM_ENABLE_SANITIZERS with those options in the function calling scope. +function(qt_internal_set_up_sanitizer_options) set(ECM_ENABLE_SANITIZERS "" CACHE STRING "Enable sanitizers") set_property(CACHE ECM_ENABLE_SANITIZERS PROPERTY STRINGS "address;memory;thread;undefined;fuzzer;fuzzer-no-link") - # If FEATURE_sanitize_foo is set on the command line, make sure to set the appropriate - # ECM_ENABLE_SANITIZERS value. Also the other way around. This basically allows setting either - # the feature or ECM_ENABLE_SANITIZERS directly. - # - # TODO: Decide which one of these should be the source of truth, because reconfiguration with - # different options might not work as expected when ECM_ENABLE_SANITIZERS is provided instead of - # the features. + # If QT_FEATURE_sanitize_foo was enabled, make sure to set the appropriate + # ECM_ENABLE_SANITIZERS value. set(enabled_sanitizer_features "") foreach(sanitizer_type address memory thread undefined) - if(FEATURE_sanitize_${sanitizer_type}) + if(QT_FEATURE_sanitize_${sanitizer_type}) list(APPEND enabled_sanitizer_features "${sanitizer_type}") endif() endforeach() # There's a mismatch between fuzzer-no-link ECM option and fuzzer_no_link Qt feature. - if(FEATURE_sanitize_fuzzer_no_link) + if(QT_FEATURE_sanitize_fuzzer_no_link) list(APPEND enabled_sanitizer_features "fuzzer-no-link") endif() if(enabled_sanitizer_features) - set(ECM_ENABLE_SANITIZERS - "${enabled_sanitizer_features}" CACHE STRING "Enable sanitizers" FORCE) - endif() - - if(ECM_ENABLE_SANITIZERS) - foreach(sanitizer_type ${ECM_ENABLE_SANITIZERS}) - message(STATUS "Enabling sanitizer: ${sanitizer_type}") - set(feature_name "FEATURE_sanitize_${sanitizer_type}") - - # Transform fuzzer-no-link dashes to underscores. - string(REGEX REPLACE "-" "_" feature_name "${feature_name}") - - set(${feature_name} "ON" CACHE BOOL "Enable ${sanitizer_type} sanitizer" FORCE) - set(QT_${feature_name} "ON" CACHE BOOL "Enable ${sanitizer_type} sanitizer" FORCE) - endforeach() + set(ECM_ENABLE_SANITIZERS "${enabled_sanitizer_features}" PARENT_SCOPE) endif() endfunction() diff --git a/cmake/QtSetup.cmake b/cmake/QtSetup.cmake index 83e932da552..30b3cbae677 100644 --- a/cmake/QtSetup.cmake +++ b/cmake/QtSetup.cmake @@ -208,10 +208,6 @@ qt_set_up_nonprefix_build() qt_set_language_standards() -## Enable support for sanitizers: -qt_internal_set_up_sanitizer_features() -include(${CMAKE_CURRENT_LIST_DIR}/3rdparty/extra-cmake-modules/modules/ECMEnableSanitizers.cmake) - option(QT_USE_CCACHE "Enable the use of ccache") if(QT_USE_CCACHE) find_program(CCACHE_PROGRAM ccache) diff --git a/cmake/configure-cmake-mapping.md b/cmake/configure-cmake-mapping.md index f9a71b59d5c..6e2d4b59c60 100644 --- a/cmake/configure-cmake-mapping.md +++ b/cmake/configure-cmake-mapping.md @@ -54,7 +54,8 @@ The effort of this is tracked in QTBUG-85373 and QTBUG-85349. | -gcov | | | | -trace [backend] | -DINPUT_trace=yes or -DINPUT_trace= | | | | or -DFEATURE_ | | -| -sanitize address -sanitize undefined | -DECM_ENABLE_SANITIZERS=address;undefined | | +| -sanitize address -sanitize undefined | -DFEATURE_sanitize_address=ON | Directly setting -DECM_ENABLE_SANITIZERS=foo is not supported | +| | -DFEATURE_sanitize_undefined=ON | | | -coverage | | | | -c++std c++20 | -DFEATURE_cxx20=ON | | | -sse2/-sse3/-ssse3/-sse4.1 | -DFEATURE_sse4=ON | | diff --git a/qt_cmdline.cmake b/qt_cmdline.cmake index 521d32d9756..bef3523b7ca 100644 --- a/qt_cmdline.cmake +++ b/qt_cmdline.cmake @@ -194,6 +194,5 @@ function(qt_commandline_sanitize arg val nextok) else() qtConfAddError("Invalid argument '${val}' to command line parameter '${arg}'") endif() - qtConfCommandlineAppendInput(sanitize "${val}") endfunction() # special case end