From 174ebfe191fdf7997d9fd37cf3b002e49c6f8d13 Mon Sep 17 00:00:00 2001 From: "l.feng" <43399351+msclock@users.noreply.github.com> Date: Sat, 14 Dec 2024 14:41:12 +0800 Subject: [PATCH] perf: cache flags for compiler, hardening, sanitizer Signed-off-by: l.feng <43399351+msclock@users.noreply.github.com> --- cmake/Common.cmake | 11 +-- cmake/build/CompilerFlags.cmake | 125 +++++++++++++++----------- cmake/build/Hardening.cmake | 114 +++++++++++++----------- cmake/build/Sanitizer.cmake | 150 ++++++++++++++++++-------------- 4 files changed, 231 insertions(+), 169 deletions(-) diff --git a/cmake/Common.cmake b/cmake/Common.cmake index f89c46a..e020b05 100644 --- a/cmake/Common.cmake +++ b/cmake/Common.cmake @@ -78,7 +78,7 @@ function(check_flags_available return_var flags) set(FLAGS_BACKUP ${CMAKE_REQUIRED_FLAGS}) # Set QUIET flag to suppress output during compilation check - set(CMAKE_REQUIRED_QUIET TRUE) + set(CMAKE_REQUIRED_QUIET ON) # Set the flags to be checked for availability set(CMAKE_REQUIRED_FLAGS "${flags}") @@ -88,10 +88,11 @@ function(check_flags_available return_var flags) "int main() { return 0; }" is_available FAIL_REGEX - D9002 - "unused-command-line-argument" - "invalid argument" - "unknown-warning-option") # linker error + [[D9002]] + [[unused-command-line-argument]] + [[invalid argument]] + [[unknown argument]] + [[unknown-warning-option]]) # Store the result of flag availability check in the specified variable set(${return_var} diff --git a/cmake/build/CompilerFlags.cmake b/cmake/build/CompilerFlags.cmake index c5a4e52..1f82cc0 100644 --- a/cmake/build/CompilerFlags.cmake +++ b/cmake/build/CompilerFlags.cmake @@ -104,63 +104,79 @@ message( COMPILER_FLAGS_SKIP_TARGETS_REGEXES: List of regexes to skip targets. Default is empty." ) -if(MSVC) - set(_warnings_cxx_temp ${COMPILER_FLAGS_WARNINGS_MSVC}) -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - ".*Clang") - set(_warnings_cxx_temp ${COMPILER_FLAGS_WARNINGS_GNU}) -else() - message( - AUTHOR_WARNING - "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") -endif() - -message(VERBOSE "Check Compiler Warnings CXX: ${_warnings_cxx_temp}") - -foreach(_warn ${_warnings_cxx_temp}) - check_and_append_flag(FLAGS "${_warn}" TARGETS compiler_warnings_cxx) -endforeach() - -unset(_warnings_cxx_temp) +string( + SHA256 + _compiler_flags_hash + "${COMPILER_FLAGS_WARNINGS_MSVC}#${COMPILER_FLAGS_WARNINGS_GNU}#${COMPILER_FLAGS_WARNINGS_CUDA}#${CMAKE_COMPILE_WARNING_AS_ERROR}" +) +if(NOT DEFINED CACHE{__COMPILER_FLAGS_HASH} OR NOT __COMPILER_FLAGS_HASH + STREQUAL _compiler_flags_hash) + set(__COMPILER_FLAGS_HASH + "${_compiler_flags_hash}" + CACHE INTERNAL "Hash of compiler flags options") + set(__COMPILER_WARNINGS_C "") + set(__COMPILER_WARNINGS_CXX "") + set(__COMPILER_WARNINGS_CUDA "") -if(CMAKE_COMPILE_WARNING_AS_ERROR) if(MSVC) - check_and_append_flag(FLAGS "/WX" TARGETS compiler_warnings_cxx) + set(_warnings_cxx_temp ${COMPILER_FLAGS_WARNINGS_MSVC}) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") - check_and_append_flag(FLAGS "-Werror" TARGETS compiler_warnings_cxx) + set(_warnings_cxx_temp ${COMPILER_FLAGS_WARNINGS_GNU}) else() message( AUTHOR_WARNING - "No compiler warnings as errors set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'" - ) + "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") endif() + + message(VERBOSE "Check Compiler Warnings CXX: ${_warnings_cxx_temp}") + + foreach(_warn ${_warnings_cxx_temp}) + check_and_append_flag(FLAGS "${_warn}" TARGETS __COMPILER_WARNINGS_CXX) + endforeach() + + unset(_warnings_cxx_temp) + + if(CMAKE_COMPILE_WARNING_AS_ERROR) + if(MSVC) + check_and_append_flag(FLAGS "/WX" TARGETS __COMPILER_WARNINGS_CXX) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID + MATCHES ".*Clang") + check_and_append_flag(FLAGS "-Werror" TARGETS __COMPILER_WARNINGS_CXX) + else() + message( + AUTHOR_WARNING + "No compiler warnings as errors set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'" + ) + endif() + endif() + + # use the same warning flags for C + set(__COMPILER_WARNINGS_C "${__COMPILER_WARNINGS_CXX}") + + foreach(_warn ${COMPILER_FLAGS_WARNINGS_CUDA}) + check_and_append_flag(FLAGS "${_warn}" TARGETS __COMPILER_WARNINGS_CUDA) + endforeach() + + flags_to_list(__COMPILER_WARNINGS_C "${__COMPILER_WARNINGS_C}") + flags_to_list(__COMPILER_WARNINGS_CXX "${__COMPILER_WARNINGS_CXX}") + flags_to_list(__COMPILER_WARNINGS_CUDA "${__COMPILER_WARNINGS_CUDA}") + set(__COMPILER_WARNINGS_C + "${__COMPILER_WARNINGS_C}" + CACHE INTERNAL "compiler warnings flags for C") + set(__COMPILER_WARNINGS_CXX + "${__COMPILER_WARNINGS_CXX}" + CACHE INTERNAL "compiler warnings flags for CXX") + set(__COMPILER_WARNINGS_CUDA + "${__COMPILER_WARNINGS_CUDA}" + CACHE INTERNAL "compiler warnings flags for CUDA") endif() -# use the same warning flags for C -set(compiler_warnings_c "${compiler_warnings_cxx}") - -foreach(_warn ${COMPILER_FLAGS_WARNINGS_CUDA}) - check_and_append_flag(FLAGS "${_warn}" TARGETS compiler_warnings_cuda) -endforeach() - -flags_to_list(compiler_warnings_c "${compiler_warnings_c}") -flags_to_list(compiler_warnings_cxx "${compiler_warnings_cxx}") -flags_to_list(compiler_warnings_cuda "${compiler_warnings_cuda}") -message(STATUS "Compiler final warnings for C: ${compiler_warnings_c}") -message(STATUS "Compiler final warnings for CXX: ${compiler_warnings_cxx}") -message(STATUS "Compiler final warnings for CUDA: ${compiler_warnings_cuda}") - -add_custom_target(compiler_flags_warnings) -set_target_properties(compiler_flags_warnings - PROPERTIES _c "${compiler_warnings_c}") -set_target_properties(compiler_flags_warnings - PROPERTIES _cxx "${compiler_warnings_cxx}") -set_target_properties(compiler_flags_warnings - PROPERTIES _cuda "${compiler_warnings_cuda}") -unset(compiler_warnings_c) -unset(compiler_warnings_cxx) -unset(compiler_warnings_cuda) +message(STATUS "Compiler final warnings for C: $CACHE{__COMPILER_WARNINGS_C}") +message( + STATUS "Compiler final warnings for CXX: $CACHE{__COMPILER_WARNINGS_CXX}") +message( + STATUS "Compiler final warnings for CUDA: $CACHE{__COMPILER_WARNINGS_CUDA}") #[[ Function to apply compiler warnings to a target. @@ -184,9 +200,18 @@ function(warn_target target) endforeach() endif() - get_target_property(_c compiler_flags_warnings _c) - get_target_property(_cxx compiler_flags_warnings _cxx) - get_target_property(_cuda compiler_flags_warnings _cuda) + if(NOT DEFINED CACHE{__COMPILER_WARNINGS_C} + OR NOT DEFINED CACHE{__COMPILER_WARNINGS_CXX} + OR NOT DEFINED CACHE{__COMPILER_WARNINGS_CUDA}) + message( + FATAL_ERROR + "Compiler warnings not set. Please call 'include(${CMAKE_CURRENT_LIST_DIR}/CompilerFlags.cmake)'" + ) + endif() + + set(_c $CACHE{__COMPILER_WARNINGS_C}) + set(_cxx $CACHE{__COMPILER_WARNINGS_CXX}) + set(_cuda $CACHE{__COMPILER_WARNINGS_CUDA}) if(arg_INCLUDE_FLAGS) message(VERBOSE diff --git a/cmake/build/Hardening.cmake b/cmake/build/Hardening.cmake index d6240b8..b6947e9 100644 --- a/cmake/build/Hardening.cmake +++ b/cmake/build/Hardening.cmake @@ -96,65 +96,76 @@ if(NOT USE_HARDENING) message(STATUS "Hardening disabled by USE_HARDENING evaluates to false") endif() -# Create a custom target to hold the hardening flags +string(SHA256 _hardening_flags_hash + "${USE_HARDENING_FLAGS}#${USE_HARDENING_LINKS}") +if(NOT DEFINED CACHE{__HARDENING_FLAGS_HASH} OR NOT __HARDENING_FLAGS_HASH + STREQUAL _hardening_flags_hash) + set(__HARDENING_FLAGS_HASH + "${_hardening_flags_hash}" + CACHE INTERNAL "Hash of hardening flags options") + set(__HARDENING_FLAGS "") + set(__HARDENING_LINKS "") -message(VERBOSE "Check Hardening flags: ${USE_HARDENING_FLAGS}") + # Create a custom target to hold the hardening flags -foreach(_harden ${USE_HARDENING_FLAGS}) - check_and_append_flag(FLAGS ${_harden} TARGETS hardening_flags) -endforeach() + message(VERBOSE "Check Hardening flags: ${USE_HARDENING_FLAGS}") -flags_to_list(hardening_flags "${hardening_flags}") + foreach(_harden ${USE_HARDENING_FLAGS}) + check_and_append_flag(FLAGS ${_harden} TARGETS __HARDENING_FLAGS) + endforeach() -message(VERBOSE "Check Hardening links: ${USE_HARDENING_LINKS}") + flags_to_list(__HARDENING_FLAGS "${__HARDENING_FLAGS}") -foreach(_harden ${USE_HARDENING_LINKS}) - flags_to_list(_harden_list "${_harden}") + message(VERBOSE "Check Hardening links: ${USE_HARDENING_LINKS}") - if(hardening_flags MATCHES "${_harden_list}") - list(APPEND hardening_links ${_harden}) - endif() -endforeach() - -# Enable minimal runtime undefined but not not propagete globally, see -# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#minimal-runtime -if(hardening_flags MATCHES "-fsanitize=undefined;-fsanitize-minimal-runtime") - message(VERBOSE - "Try to enabling minimal runtime undefined behavior sanitizer") - check_and_append_flag(FLAGS "-fno-sanitize-recover=undefined" TARGETS - no_sanitize_recover_ub) - flags_to_list(no_sanitize_recover_ub "${no_sanitize_recover_ub}") - list(APPEND hardening_flags ${no_sanitize_recover_ub}) - list(APPEND hardening_links ${no_sanitize_recover_ub}) -endif() + foreach(_harden ${USE_HARDENING_LINKS}) + flags_to_list(_harden_list "${_harden}") -flags_to_list(hardening_links "${hardening_links}") + if(__HARDENING_FLAGS MATCHES "${_harden_list}") + list(APPEND __HARDENING_LINKS ${_harden}) + endif() + endforeach() -# Handle the conflics between hardening ubsan and asan -if(TARGET sanitizer_flags) - get_target_property(_san sanitizer_flags _san) + # Enable minimal runtime undefined but not not propagete globally, see + # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#minimal-runtime + if(__HARDENING_FLAGS MATCHES + "-fsanitize=undefined;-fsanitize-minimal-runtime") + message(VERBOSE + "Try to enabling minimal runtime undefined behavior sanitizer") + check_and_append_flag(FLAGS "-fno-sanitize-recover=undefined" TARGETS + no_sanitize_recover_ub) + flags_to_list(no_sanitize_recover_ub "${no_sanitize_recover_ub}") + list(APPEND __HARDENING_FLAGS ${no_sanitize_recover_ub}) + list(APPEND __HARDENING_LINKS ${no_sanitize_recover_ub}) + endif() - if(_san - AND _san MATCHES "-fsanitize=address" - AND hardening_flags MATCHES "-fsanitize-minimal-runtime") - message( - WARNING "Try to disable usan minimal runtime due to conflict with asan") - list(REMOVE_ITEM hardening_flags "-fsanitize=undefined" - "-fsanitize-minimal-runtime" "-fno-sanitize-recover=undefined") - list(REMOVE_ITEM hardening_links "-fsanitize=undefined" - "-fsanitize-minimal-runtime" "-fno-sanitize-recover=undefined") + flags_to_list(__HARDENING_LINKS "${__HARDENING_LINKS}") + + # Handle the conflics between hardening ubsan and asan + if(TARGET sanitizer_flags) + get_target_property(_san sanitizer_flags _san) + + if(_san + AND _san MATCHES "-fsanitize=address" + AND __HARDENING_FLAGS MATCHES "-fsanitize-minimal-runtime") + message( + WARNING "Try to disable usan minimal runtime due to conflict with asan") + list(REMOVE_ITEM __HARDENING_FLAGS "-fsanitize=undefined" + "-fsanitize-minimal-runtime" "-fno-sanitize-recover=undefined") + list(REMOVE_ITEM __HARDENING_LINKS "-fsanitize=undefined" + "-fsanitize-minimal-runtime" "-fno-sanitize-recover=undefined") + endif() endif() + set(__HARDENING_FLAGS + "${__HARDENING_FLAGS}" + CACHE INTERNAL "Hardening flags") + set(__HARDENING_LINKS + "${__HARDENING_LINKS}" + CACHE INTERNAL "Hardening links") endif() -message(STATUS "Hardening final flags: ${hardening_flags}") -message(STATUS "Hardening final links: ${hardening_links}") - -add_custom_target(hardening_flags) -set_target_properties(hardening_flags PROPERTIES _flags "${hardening_flags}") -set_target_properties(hardening_flags PROPERTIES _links "${hardening_links}") - -unset(hardening_flags) -unset(hardening_links) +message(STATUS "Hardening final flags: $CACHE{__HARDENING_FLAGS}") +message(STATUS "Hardening final links: $CACHE{__HARDENING_LINKS}") function(harden_target target) set(_opts) @@ -196,8 +207,13 @@ function(harden_target target) endif() endif() - get_target_property(_flags hardening_flags _flags) - get_target_property(_links hardening_flags _links) + if(NOT DEFINED CACHE{__HARDENING_FLAGS} OR NOT DEFINED + CACHE{__HARDENING_LINKS}) + message(FATAL_ERROR "Hardening flags not defined") + endif() + + set(_flags $CACHE{__HARDENING_FLAGS}) + set(_links $CACHE{__HARDENING_LINKS}) set(FLAGS ${_flags} ${target_flags}) set(LINKS ${_links} ${target_flags}) diff --git a/cmake/build/Sanitizer.cmake b/cmake/build/Sanitizer.cmake index 4e52faf..b37f6f2 100644 --- a/cmake/build/Sanitizer.cmake +++ b/cmake/build/Sanitizer.cmake @@ -110,88 +110,102 @@ if(NOT USE_SANITIZER) message(STATUS "Sanitizer disabled by USE_SANITIZER evaluates to false.") endif() -if(USE_SANITIZER MATCHES [[thread]] AND (USE_SANITIZER MATCHES [[address]] - OR USE_SANITIZER MATCHES [[leak]])) - message( - FATAL_ERROR "Thread sanitizer can not work with Address or Leak sanitizers." - ) -endif() +string( + SHA256 + _sanitizer_flags_hash + "${USE_SANITIZER_ASAN_FLAGS}#${USE_SANITIZER_MSAN_FLAGS}#${USE_SANITIZER_USAN_FLAGS}#${USE_SANITIZER_TSAN_FLAGS}#${USE_SANITIZER_LSAN_FLAGS}#${USE_SANITIZER_CFI_FLAGS}#${USE_SANITIZER_EXTRA_FLAGS}#${USE_SANITIZER_BLACKLIST_FILE}" +) +if(NOT DEFINED CACHE{__SANITIZER_FLAGS_HASH} OR NOT __SANITIZER_FLAGS_HASH + STREQUAL _sanitizer_flags_hash) + set(__SANITIZER_FLAGS_HASH + "${_sanitizer_flags_hash}" + CACHE INTERNAL "Hash of sanitizer flags options") + set(__SAN_AVAILABLE_FLAGS "") + + if(USE_SANITIZER MATCHES [[thread]] AND (USE_SANITIZER MATCHES [[address]] + OR USE_SANITIZER MATCHES [[leak]])) + message( + FATAL_ERROR + "Thread sanitizer can not work with Address or Leak sanitizers.") + endif() -if(USE_SANITIZER MATCHES [[memory(withorigins)?]] - AND (USE_SANITIZER MATCHES [[address]] - OR USE_SANITIZER MATCHES [[leak]] - OR USE_SANITIZER MATCHES [[thread]])) - message( - FATAL_ERROR - "Memory sanitizer with origins track can not work with Address, Leak, and Thread sanitizers." - ) -endif() + if(USE_SANITIZER MATCHES [[memory(withorigins)?]] + AND (USE_SANITIZER MATCHES [[address]] + OR USE_SANITIZER MATCHES [[leak]] + OR USE_SANITIZER MATCHES [[thread]])) + message( + FATAL_ERROR + "Memory sanitizer with origins track can not work with Address, Leak, and Thread sanitizers." + ) + endif() -if(USE_SANITIZER MATCHES [[address]]) - message(VERBOSE "Testing with Address sanitizer") + if(USE_SANITIZER MATCHES [[address]]) + message(VERBOSE "Testing with Address sanitizer") - foreach(_flag ${USE_SANITIZER_ASAN_FLAGS}) - check_and_append_flag(FLAGS "${_flag}" TARGETS san_available_flags) - endforeach() -endif() + foreach(_flag ${USE_SANITIZER_ASAN_FLAGS}) + check_and_append_flag(FLAGS "${_flag}" TARGETS __SAN_AVAILABLE_FLAGS) + endforeach() + endif() -if(USE_SANITIZER MATCHES [[memory(withorigins)?]]) - message(VERBOSE "Testing with Memory sanitizer with origins track") + if(USE_SANITIZER MATCHES [[memory(withorigins)?]]) + message(VERBOSE "Testing with Memory sanitizer with origins track") - foreach(_flag ${USE_SANITIZER_MSAN_FLAGS}) - check_and_append_flag(FLAGS "${_flag}" TARGETS san_available_flags) - endforeach() -endif() + foreach(_flag ${USE_SANITIZER_MSAN_FLAGS}) + check_and_append_flag(FLAGS "${_flag}" TARGETS __SAN_AVAILABLE_FLAGS) + endforeach() + endif() -if(USE_SANITIZER MATCHES [[undefined]]) - message(VERBOSE "Testing with Undefined Behaviour sanitizer") + if(USE_SANITIZER MATCHES [[undefined]]) + message(VERBOSE "Testing with Undefined Behaviour sanitizer") - foreach(_flag ${USE_SANITIZER_USAN_FLAGS}) - check_and_append_flag(FLAGS "${_flag}" TARGETS san_available_flags) - endforeach() + foreach(_flag ${USE_SANITIZER_USAN_FLAGS}) + check_and_append_flag(FLAGS "${_flag}" TARGETS __SAN_AVAILABLE_FLAGS) + endforeach() - if(EXISTS "${USE_SANITIZER_BLACKLIST_FILE}") - append_variable("-fsanitize-blacklist=${USE_SANITIZER_BLACKLIST_FILE}" - san_available_flags) + if(EXISTS "${USE_SANITIZER_BLACKLIST_FILE}") + append_variable("-fsanitize-blacklist=${USE_SANITIZER_BLACKLIST_FILE}" + __SAN_AVAILABLE_FLAGS) + endif() endif() -endif() -if(USE_SANITIZER MATCHES [[thread]]) - message(VERBOSE "Testing with Thread sanitizer") + if(USE_SANITIZER MATCHES [[thread]]) + message(VERBOSE "Testing with Thread sanitizer") - foreach(_flag ${USE_SANITIZER_TSAN_FLAGS}) - check_and_append_flag(FLAGS "${_flag}" TARGETS san_available_flags) - endforeach() -endif() + foreach(_flag ${USE_SANITIZER_TSAN_FLAGS}) + check_and_append_flag(FLAGS "${_flag}" TARGETS __SAN_AVAILABLE_FLAGS) + endforeach() + endif() -if(USE_SANITIZER MATCHES [[leak]]) - message(VERBOSE "Testing with Leak sanitizer") + if(USE_SANITIZER MATCHES [[leak]]) + message(VERBOSE "Testing with Leak sanitizer") - foreach(_flag ${USE_SANITIZER_LSAN_FLAGS}) - check_and_append_flag(FLAGS "${_flag}" TARGETS san_available_flags) - endforeach() -endif() + foreach(_flag ${USE_SANITIZER_LSAN_FLAGS}) + check_and_append_flag(FLAGS "${_flag}" TARGETS __SAN_AVAILABLE_FLAGS) + endforeach() + endif() -if(USE_SANITIZER MATCHES [[cfi]]) - message(VERBOSE "Testing with Control Flow Integrity(CFI) sanitizer") + if(USE_SANITIZER MATCHES [[cfi]]) + message(VERBOSE "Testing with Control Flow Integrity(CFI) sanitizer") - foreach(_flag ${USE_SANITIZER_CFI_FLAGS}) - check_and_append_flag(FLAGS "${_flag}" TARGETS san_available_flags) - endforeach() -endif() + foreach(_flag ${USE_SANITIZER_CFI_FLAGS}) + check_and_append_flag(FLAGS "${_flag}" TARGETS __SAN_AVAILABLE_FLAGS) + endforeach() + endif() -if(USE_SANITIZER_EXTRA_FLAGS) - message(VERBOSE "Test with extra flags: ${USE_SANITIZER_EXTRA_FLAGS}") - check_and_append_flag(FLAGS "${USE_SANITIZER_EXTRA_FLAGS}" TARGETS - san_available_flags) -endif() + if(USE_SANITIZER_EXTRA_FLAGS) + message(VERBOSE "Test with extra flags: ${USE_SANITIZER_EXTRA_FLAGS}") + check_and_append_flag(FLAGS "${USE_SANITIZER_EXTRA_FLAGS}" TARGETS + __SAN_AVAILABLE_FLAGS) + endif() + + flags_to_list(__SAN_AVAILABLE_FLAGS "${__SAN_AVAILABLE_FLAGS}") -flags_to_list(san_available_flags "${san_available_flags}") -message(STATUS "Sanitizer final flags: ${san_available_flags}") + set(__SAN_AVAILABLE_FLAGS + ${__SAN_AVAILABLE_FLAGS} + CACHE INTERNAL "Sanitizer flags") +endif() -add_custom_target(sanitizer_flags) -set_target_properties(sanitizer_flags PROPERTIES _san "${san_available_flags}") -unset(san_available_flags) +message(STATUS "Sanitizer final flags: ${__SAN_AVAILABLE_FLAGS}") #[[ A function to copy sanitizer runtime when open ASAN flags on windows.Basically, @@ -289,7 +303,13 @@ function(sanitize_target target) endforeach() endif() - get_target_property(_san sanitizer_flags _san) + if(NOT DEFINED CACHE{__SAN_AVAILABLE_FLAGS}) + message( + FATAL_ERROR + "Sanitizer flags not defined. Please check your configuration.") + endif() + + set(_san $CACHE{__SAN_AVAILABLE_FLAGS}) if(NOT MSVC) set(FLAGS ${_san})