TryAppendCXXFlags.cmake
1 # Copyright (c) 2023-present The Bitcoin Core developers 2 # Distributed under the MIT software license, see the accompanying 3 # file COPYING or https://opensource.org/license/mit/. 4 5 include_guard(GLOBAL) 6 include(CheckCXXSourceCompiles) 7 8 #[=[ 9 Add language-wide flags, which will be passed to all invocations of the compiler. 10 This includes invocations that drive compiling and those that drive linking. 11 12 Usage examples: 13 14 try_append_cxx_flags("-Wformat -Wformat-security" VAR warn_cxx_flags) 15 16 17 try_append_cxx_flags("-Wsuggest-override" VAR warn_cxx_flags 18 SOURCE "struct A { virtual void f(); }; struct B : A { void f() final; };" 19 ) 20 21 22 try_append_cxx_flags("-fsanitize=${SANITIZERS}" TARGET core_interface 23 RESULT_VAR cxx_supports_sanitizers 24 ) 25 if(NOT cxx_supports_sanitizers) 26 message(FATAL_ERROR "Compiler did not accept requested flags.") 27 endif() 28 29 30 try_append_cxx_flags("-Wunused-parameter" TARGET core_interface 31 IF_CHECK_PASSED "-Wno-unused-parameter" 32 ) 33 34 35 In configuration output, this function prints a string by the following pattern: 36 37 -- Performing Test CXX_SUPPORTS_[flags] 38 -- Performing Test CXX_SUPPORTS_[flags] - Success 39 40 ]=] 41 function(try_append_cxx_flags flags) 42 cmake_parse_arguments(PARSE_ARGV 1 43 TACXXF # prefix 44 "SKIP_LINK" # options 45 "TARGET;VAR;SOURCE;RESULT_VAR" # one_value_keywords 46 "IF_CHECK_PASSED" # multi_value_keywords 47 ) 48 49 set(flags_as_string "${flags}") 50 separate_arguments(flags) 51 52 string(MAKE_C_IDENTIFIER "${flags_as_string}" id_string) 53 string(TOUPPER "${id_string}" id_string) 54 55 set(source "int main() { return 0; }") 56 if(DEFINED TACXXF_SOURCE AND NOT TACXXF_SOURCE STREQUAL source) 57 set(source "${TACXXF_SOURCE}") 58 string(SHA256 source_hash "${source}") 59 string(SUBSTRING ${source_hash} 0 4 source_hash_head) 60 string(APPEND id_string _${source_hash_head}) 61 endif() 62 63 # This avoids running a linker. 64 set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 65 set(CMAKE_REQUIRED_FLAGS "${flags_as_string} ${working_compiler_werror_flag}") 66 set(compiler_result CXX_SUPPORTS_${id_string}) 67 check_cxx_source_compiles("${source}" ${compiler_result}) 68 69 if(${compiler_result}) 70 if(DEFINED TACXXF_IF_CHECK_PASSED) 71 if(DEFINED TACXXF_TARGET) 72 target_compile_options(${TACXXF_TARGET} INTERFACE ${TACXXF_IF_CHECK_PASSED}) 73 endif() 74 if(DEFINED TACXXF_VAR) 75 list(JOIN TACXXF_IF_CHECK_PASSED " " flags_if_check_passed_as_string) 76 string(STRIP "${${TACXXF_VAR}} ${flags_if_check_passed_as_string}" ${TACXXF_VAR}) 77 endif() 78 else() 79 if(DEFINED TACXXF_TARGET) 80 target_compile_options(${TACXXF_TARGET} INTERFACE ${flags}) 81 endif() 82 if(DEFINED TACXXF_VAR) 83 string(STRIP "${${TACXXF_VAR}} ${flags_as_string}" ${TACXXF_VAR}) 84 endif() 85 endif() 86 endif() 87 88 if(DEFINED TACXXF_VAR) 89 set(${TACXXF_VAR} "${${TACXXF_VAR}}" PARENT_SCOPE) 90 endif() 91 92 if(DEFINED TACXXF_RESULT_VAR) 93 set(${TACXXF_RESULT_VAR} "${${compiler_result}}" PARENT_SCOPE) 94 endif() 95 96 if(NOT ${compiler_result} OR TACXXF_SKIP_LINK) 97 return() 98 endif() 99 100 # This forces running a linker. 101 set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) 102 set(CMAKE_REQUIRED_FLAGS "${flags_as_string}") 103 set(CMAKE_REQUIRED_LINK_OPTIONS ${working_linker_werror_flag}) 104 set(linker_result LINKER_SUPPORTS_${id_string}) 105 check_cxx_source_compiles("${source}" ${linker_result}) 106 107 if(${linker_result}) 108 if(DEFINED TACXXF_IF_CHECK_PASSED) 109 if(DEFINED TACXXF_TARGET) 110 target_link_options(${TACXXF_TARGET} INTERFACE ${TACXXF_IF_CHECK_PASSED}) 111 endif() 112 else() 113 if(DEFINED TACXXF_TARGET) 114 target_link_options(${TACXXF_TARGET} INTERFACE ${flags}) 115 endif() 116 endif() 117 else() 118 message(WARNING "'${flags_as_string}' fail(s) to link.") 119 endif() 120 endfunction() 121 122 if(MSVC) 123 try_append_cxx_flags("/WX /options:strict" VAR working_compiler_werror_flag SKIP_LINK) 124 else() 125 try_append_cxx_flags("-Werror" VAR working_compiler_werror_flag SKIP_LINK) 126 endif()