1# Copyright (c) Monetra Technologies LLC 2# SPDX-License-Identifier: MIT 3 4# EnableWarnings.cmake 5# 6# Checks for and turns on a large number of warning C flags. 7# 8# Adds the following helper functions: 9# 10# remove_warnings(... list of warnings ...) 11# Turn off given list of individual warnings for all targets and subdirectories added after this. 12# 13# remove_all_warnings() 14# Remove all warning flags, add -w to suppress built-in warnings. 15# 16# remove_all_warnings_from_targets(... list of targets ...) 17# Suppress warnings for the given targets only. 18# 19# push_warnings() 20# Save current warning flags by pushing them onto an internal stack. Note that modifications to the internal 21# stack are only visible in the current CMakeLists.txt file and its children. 22# 23# Note: changing warning flags multiple times in the same directory only affects add_subdirectory() calls. 24# Targets in the directory will always use the warning flags in effect at the end of the CMakeLists.txt 25# file - this is due to really weird and annoying legacy behavior of CMAKE_C_FLAGS. 26# 27# pop_warnings() 28# Restore the last set of flags that were saved with push_warnings(). Note that modifications to the internal 29# stack are only visible in the current CMakeLists.txt file and its children. 30# 31 32if (_internal_enable_warnings_already_run) 33 return() 34endif () 35set(_internal_enable_warnings_already_run TRUE) 36 37include(CheckCCompilerFlag) 38include(CheckCXXCompilerFlag) 39 40get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 41 42# internal helper: _int_enable_warnings_set_flags_ex(langs_var configs_var [warnings flags]) 43function(_int_enable_warnings_set_flags_ex langs_var configs_var) 44 if (NOT ARGN) 45 return() 46 endif () 47 48 if (NOT ${configs_var}) 49 set(${configs_var} "NONE") 50 endif () 51 string(TOUPPER "${${configs_var}}" ${configs_var}) 52 53 foreach(_flag ${ARGN}) 54 string(MAKE_C_IDENTIFIER "HAVE_${_flag}" varname) 55 56 if ("C" IN_LIST ${langs_var}) 57 check_c_compiler_flag(${_flag} ${varname}) 58 if (${varname}) 59 foreach (config IN LISTS ${configs_var}) 60 if (config STREQUAL "NONE") 61 set(config) 62 else () 63 set(config "_${config}") 64 endif () 65 string(APPEND CMAKE_C_FLAGS${config} " ${_flag}") 66 endforeach () 67 endif () 68 endif () 69 70 if ("CXX" IN_LIST ${langs_var}) 71 string(APPEND varname "_CXX") 72 check_cxx_compiler_flag(${_flag} ${varname}) 73 if (${varname}) 74 foreach (config IN LISTS ${configs_var}) 75 if (config STREQUAL "NONE") 76 set(config) 77 else () 78 set(config "_${config}") 79 endif () 80 string(APPEND CMAKE_CXX_FLAGS${config} " ${_flag}") 81 endforeach () 82 endif () 83 endif () 84 endforeach() 85 86 foreach(lang C CXX) 87 foreach (config IN LISTS ${configs_var}) 88 string(TOUPPER "${config}" config) 89 if (config STREQUAL "NONE") 90 set(config) 91 else () 92 set(config "_${config}") 93 endif () 94 string(STRIP "${CMAKE_${lang}_FLAGS${config}}" CMAKE_${lang}_FLAGS${config}) 95 set(CMAKE_${lang}_FLAGS${config} "${CMAKE_${lang}_FLAGS${config}}" PARENT_SCOPE) 96 endforeach () 97 endforeach() 98endfunction() 99 100# internal helper: _int_enable_warnings_set_flags(langs_var [warnings flags]) 101macro(_int_enable_warnings_set_flags langs_var) 102 set(configs "NONE") 103 _int_enable_warnings_set_flags_ex(${langs_var} configs ${ARGN}) 104endmacro() 105 106set(_flags_C) 107set(_flags_CXX) 108set(_debug_flags_C) 109set(_debug_flags_CXX) 110 111if (MSVC) 112 # Visual Studio uses a completely different nomenclature for warnings than gcc/mingw/clang, so none of the 113 # "-W[name]" warnings will work. 114 115 # W4 would be better but it produces unnecessary warnings like: 116 # * warning C4706: assignment within conditional expression 117 # Triggered when doing "while(1)" 118 # * warning C4115: 'timeval' : named type definition in parentheses 119 # * warning C4201: nonstandard extension used : nameless struct/union 120 # Triggered by system includes (commctrl.h, shtypes.h, Shlobj.h) 121 set(_flags 122 /W3 123 /we4013 # Treat "function undefined, assuming extern returning int" warning as an error. https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4013 124 ) 125 126 list(APPEND _flags_C ${_flags}) 127 list(APPEND _flags_CXX ${_flags}) 128 129elseif (CMAKE_C_COMPILER_ID MATCHES "Intel") 130 # Intel's compiler warning flags are more like Visual Studio than GCC, though the numbers aren't the same. 131 set(_flags 132 # Use warning level 3, quite wordy. 133 -w3 134 # Disable warnings we don't care about (add more as they are encountered). 135 -wd383 # Spammy warning about initializing from a temporary object in C++ (which is done all the time ...). 136 -wd11074 # Diagnostic related to inlining. 137 -wd11076 # Diagnostic related to inlining. 138 ) 139 140 list(APPEND _flags_C ${_flags}) 141 list(APPEND _flags_CXX ${_flags}) 142 143elseif (CMAKE_C_COMPILER_ID MATCHES "XL") 144 set (_flags 145 -qwarn64 146 -qformat=all 147 -qflag=i:i 148 ) 149 list(APPEND _flags_C ${_flags}) 150 list(APPEND _flags_CXX ${_flags}) 151 152else () 153 # If we're compiling with GCC / Clang / MinGW (or anything else besides Visual Studio or Intel): 154 # C Flags: 155 list(APPEND _flags_C 156 -Wall 157 -Wextra 158 159 # Enable additional warnings not covered by Wall and Wextra. 160 -Wcast-align 161 -Wconversion 162 -Wdeclaration-after-statement 163 -Wdouble-promotion 164 -Wfloat-equal 165 -Wformat-security 166 -Winit-self 167 -Wjump-misses-init 168 -Wlogical-op 169 -Wmissing-braces 170 -Wmissing-declarations 171 -Wmissing-format-attribute 172 -Wmissing-include-dirs 173 -Wmissing-prototypes 174 -Wnested-externs 175 -Wno-coverage-mismatch 176 -Wold-style-definition 177 -Wpacked 178 -Wpointer-arith 179 -Wredundant-decls 180 -Wshadow 181 -Wsign-conversion 182 -Wstrict-overflow 183 -Wstrict-prototypes 184 -Wtrampolines 185 -Wundef 186 -Wunused 187 -Wvariadic-macros 188 -Wvla 189 -Wwrite-strings 190 191 # On Windows MinGW I think implicit fallthrough enabled by -Wextra must not default to 3 192 -Wimplicit-fallthrough=3 193 194 # Treat implicit variable typing and implicit function declarations as errors. 195 -Werror=implicit-int 196 -Werror=implicit-function-declaration 197 198 # Make MacOSX honor -mmacosx-version-min 199 -Werror=partial-availability 200 201 # Some clang versions might warn if an argument like "-I/path/to/headers" is unused, 202 # silence these. 203 -Qunused-arguments 204 ) 205 206 # C++ flags: 207 list(APPEND _flags_CXX 208 -Wall 209 -Wextra 210 211 # Enable additional warnings not covered by Wall and Wextra. 212 -Wcast-align 213 -Wformat-security 214 -Wmissing-declarations 215 -Wmissing-format-attribute 216 -Wpacked-bitfield-compat 217 -Wredundant-decls 218 -Wvla 219 220 # Turn off unused parameter warnings with C++ (they happen often in C++ and Qt). 221 -Wno-unused-parameter 222 223 # Some clang versions might warn if an argument like "-I/path/to/headers" is unused, 224 # silence these. 225 -Qunused-arguments 226 ) 227 228 # Note: when cross-compiling to Windows from Cygwin, the Qt Mingw packages have a bunch of 229 # noisy type-conversion warnings in headers. So, only enable those warnings if we're 230 # not building that configuration. 231 if (NOT (WIN32 AND (CMAKE_HOST_SYSTEM_NAME MATCHES "CYGWIN"))) 232 list(APPEND _flags_CXX 233 -Wconversion 234 -Wfloat-equal 235 -Wsign-conversion 236 ) 237 endif () 238 239 # Add flags to force colored output even when output is redirected via pipe. 240 if (CMAKE_GENERATOR MATCHES "Ninja") 241 set(color_default TRUE) 242 else () 243 set(color_default FALSE) 244 endif () 245 option(FORCE_COLOR "Force compiler to always colorize, even when output is redirected." ${color_default}) 246 mark_as_advanced(FORCE FORCE_COLOR) 247 if (FORCE_COLOR) 248 set(_flags 249 -fdiagnostics-color=always # GCC 250 -fcolor-diagnostics # Clang 251 ) 252 list(APPEND _flags_C ${_flags}) 253 list(APPEND _flags_CXX ${_flags}) 254 endif () 255 256 # Add -fno-omit-frame-pointer (and optionally -fno-inline) to make debugging and stack dumps nicer. 257 set(_flags 258 -fno-omit-frame-pointer 259 ) 260 option(M_NO_INLINE "Disable function inlining for RelWithDebInfo and Debug configurations?" FALSE) 261 if (M_NO_INLINE) 262 list(APPEND _flags 263 -fno-inline 264 ) 265 endif () 266 list(APPEND _debug_flags_C ${_flags}) 267 list(APPEND _debug_flags_CXX ${_flags}) 268endif () 269 270# Check and set compiler flags. 271set(_debug_configs 272 RelWithDebInfo 273 Debug 274) 275foreach(_lang ${languages}) 276 _int_enable_warnings_set_flags(_lang ${_flags_${_lang}}) 277 _int_enable_warnings_set_flags_ex(_lang _debug_configs ${_debug_flags_${_lang}}) 278 279 # Ensure pure Debug builds are NOT optimized (not possible on Visual Studio). 280 # Any optimization of a Debug build will prevent debuggers like lldb from 281 # fully displaying backtraces and stepping. 282 if (NOT MSVC) 283 set(_config Debug) 284 _int_enable_warnings_set_flags_ex(_lang _config -O0) 285 endif () 286endforeach() 287 288 289 290# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 291# Helper functions 292 293 294# This function can be called in subdirectories, to prune out warnings that they don't want. 295# vararg: warning flags to remove from list of enabled warnings. All "no" flags after EXPLICIT_DISABLE 296# will be added to C flags. 297# 298# Ex.: remove_warnings(-Wall -Wdouble-promotion -Wcomment) prunes those warnings flags from the compile command. 299function(remove_warnings) 300 get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 301 set(langs C) 302 if ("CXX" IN_LIST languages) 303 list(APPEND langs CXX) 304 endif () 305 306 foreach(lang ${langs}) 307 set(toadd) 308 set(in_explicit_disable FALSE) 309 foreach (flag ${ARGN}) 310 if (flag STREQUAL "EXPLICIT_DISABLE") 311 set(in_explicit_disable TRUE) 312 elseif (in_explicit_disable) 313 list(APPEND toadd "${flag}") 314 else () 315 string(REGEX REPLACE "${flag}([ \t]+|$)" "" CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}") 316 endif () 317 endforeach () 318 _int_enable_warnings_set_flags(lang ${toadd}) 319 string(STRIP "${CMAKE_${lang}_FLAGS}" CMAKE_${lang}_FLAGS) 320 set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}" PARENT_SCOPE) 321 endforeach() 322endfunction() 323 324 325# Explicitly suppress all warnings. As long as this flag is the last warning flag, warnings will be 326# suppressed even if earlier flags enabled warnings. 327function(remove_all_warnings) 328 get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 329 set(langs C) 330 if ("CXX" IN_LIST languages) 331 list(APPEND langs CXX) 332 endif () 333 334 foreach(lang ${langs}) 335 string(REGEX REPLACE "[-/][Ww][^ \t]*([ \t]+|$)" "" CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}") 336 if (MSVC) 337 string(APPEND CMAKE_${lang}_FLAGS " /w") 338 else () 339 string(APPEND CMAKE_${lang}_FLAGS " -w") 340 endif () 341 string(STRIP "${CMAKE_${lang}_FLAGS}" CMAKE_${lang}_FLAGS) 342 set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}" PARENT_SCOPE) 343 endforeach() 344endfunction() 345 346 347function(remove_all_warnings_from_targets) 348 foreach (target ${ARGN}) 349 if (MSVC) 350 target_compile_options(${target} PRIVATE "/w") 351 else () 352 target_compile_options(${target} PRIVATE "-w") 353 endif () 354 endforeach() 355endfunction() 356 357 358# Save the current warning settings to an internal variable. 359function(push_warnings) 360 get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 361 set(langs C) 362 if ("CXX" IN_LIST languages) 363 list(APPEND langs CXX) 364 endif () 365 366 foreach(lang ${langs}) 367 if (CMAKE_${lang}_FLAGS MATCHES ";") 368 message(AUTHOR_WARNING "Cannot push warnings for ${lang}, CMAKE_${lang}_FLAGS contains semicolons") 369 continue() 370 endif () 371 # Add current flags to end of internal list. 372 list(APPEND _enable_warnings_internal_${lang}_flags_stack "${CMAKE_${lang}_FLAGS}") 373 # Propagate results up to caller's scope. 374 set(_enable_warnings_internal_${lang}_flags_stack "${_enable_warnings_internal_${lang}_flags_stack}" PARENT_SCOPE) 375 endforeach() 376endfunction() 377 378 379# Restore the current warning settings from an internal variable. 380function(pop_warnings) 381 get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 382 set(langs C) 383 if ("CXX" IN_LIST languages) 384 list(APPEND langs CXX) 385 endif () 386 387 foreach(lang ${langs}) 388 if (NOT _enable_warnings_internal_${lang}_flags_stack) 389 continue() 390 endif () 391 # Pop flags off of end of list, overwrite current flags with whatever we popped off. 392 list(GET _enable_warnings_internal_${lang}_flags_stack -1 CMAKE_${lang}_FLAGS) 393 list(REMOVE_AT _enable_warnings_internal_${lang}_flags_stack -1) 394 # Propagate results up to caller's scope. 395 set(_enable_warnings_internal_${lang}_flags_stack "${_enable_warnings_internal_${lang}_flags_stack}" PARENT_SCOPE) 396 string(STRIP "${CMAKE_${lang}_FLAGS}" CMAKE_${lang}_FLAGS) 397 set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}" PARENT_SCOPE) 398 endforeach() 399endfunction() 400