TryAppendCXXFlags.cmake raw
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()
127