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# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 43# Helper functions 44 45 46# This function can be called in subdirectories, to prune out warnings that they don't want. 47# vararg: warning flags to remove from list of enabled warnings. All "no" flags after EXPLICIT_DISABLE 48# will be added to C flags. 49# 50# Ex.: remove_warnings(-Wall -Wdouble-promotion -Wcomment) prunes those warnings flags from the compile command. 51function(remove_warnings) 52 get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 53 set(langs C) 54 if ("CXX" IN_LIST languages) 55 list(APPEND langs CXX) 56 endif () 57 58 foreach(lang ${langs}) 59 set(toadd) 60 set(in_explicit_disable FALSE) 61 foreach (flag ${ARGN}) 62 if (flag STREQUAL "EXPLICIT_DISABLE") 63 set(in_explicit_disable TRUE) 64 elseif (in_explicit_disable) 65 list(APPEND toadd "${flag}") 66 else () 67 string(REGEX REPLACE "${flag}([ \t]+|$)" "" CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}") 68 endif () 69 endforeach () 70 _int_enable_warnings_set_flags(lang ${toadd}) 71 string(STRIP "${CMAKE_${lang}_FLAGS}" CMAKE_${lang}_FLAGS) 72 set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}" PARENT_SCOPE) 73 endforeach() 74endfunction() 75 76 77# Explicitly suppress all warnings. As long as this flag is the last warning flag, warnings will be 78# suppressed even if earlier flags enabled warnings. 79function(remove_all_warnings) 80 get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 81 set(langs C) 82 if ("CXX" IN_LIST languages) 83 list(APPEND langs CXX) 84 endif () 85 86 foreach(lang ${langs}) 87 string(REGEX REPLACE "[-/][Ww][^ \t]*([ \t]+|$)" "" CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}") 88 if (MSVC) 89 string(APPEND CMAKE_${lang}_FLAGS " /w") 90 else () 91 string(APPEND CMAKE_${lang}_FLAGS " -w") 92 endif () 93 string(STRIP "${CMAKE_${lang}_FLAGS}" CMAKE_${lang}_FLAGS) 94 set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}" PARENT_SCOPE) 95 endforeach() 96endfunction() 97 98 99function(remove_all_warnings_from_targets) 100 foreach (target ${ARGN}) 101 if (MSVC) 102 target_compile_options(${target} PRIVATE "/w") 103 else () 104 target_compile_options(${target} PRIVATE "-w") 105 endif () 106 endforeach() 107endfunction() 108 109 110# Save the current warning settings to an internal variable. 111function(push_warnings) 112 get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 113 set(langs C) 114 if ("CXX" IN_LIST languages) 115 list(APPEND langs CXX) 116 endif () 117 118 foreach(lang ${langs}) 119 if (CMAKE_${lang}_FLAGS MATCHES ";") 120 message(AUTHOR_WARNING "Cannot push warnings for ${lang}, CMAKE_${lang}_FLAGS contains semicolons") 121 continue() 122 endif () 123 # Add current flags to end of internal list. 124 list(APPEND _enable_warnings_internal_${lang}_flags_stack "${CMAKE_${lang}_FLAGS}") 125 # Propagate results up to caller's scope. 126 set(_enable_warnings_internal_${lang}_flags_stack "${_enable_warnings_internal_${lang}_flags_stack}" PARENT_SCOPE) 127 endforeach() 128endfunction() 129 130 131# Restore the current warning settings from an internal variable. 132function(pop_warnings) 133 get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 134 set(langs C) 135 if ("CXX" IN_LIST languages) 136 list(APPEND langs CXX) 137 endif () 138 139 foreach(lang ${langs}) 140 if (NOT _enable_warnings_internal_${lang}_flags_stack) 141 continue() 142 endif () 143 # Pop flags off of end of list, overwrite current flags with whatever we popped off. 144 list(GET _enable_warnings_internal_${lang}_flags_stack -1 CMAKE_${lang}_FLAGS) 145 list(REMOVE_AT _enable_warnings_internal_${lang}_flags_stack -1) 146 # Propagate results up to caller's scope. 147 set(_enable_warnings_internal_${lang}_flags_stack "${_enable_warnings_internal_${lang}_flags_stack}" PARENT_SCOPE) 148 string(STRIP "${CMAKE_${lang}_FLAGS}" CMAKE_${lang}_FLAGS) 149 set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}" PARENT_SCOPE) 150 endforeach() 151endfunction() 152 153 154# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 155# Runs when included automatically 156 157# internal helper: _int_enable_warnings_set_flags_ex(langs_var configs_var [warnings flags]) 158function(_int_enable_warnings_set_flags_ex langs_var configs_var) 159 if (NOT ARGN) 160 return() 161 endif () 162 163 if (NOT ${configs_var}) 164 set(${configs_var} "NONE") 165 endif () 166 string(TOUPPER "${${configs_var}}" ${configs_var}) 167 168 foreach(_flag ${ARGN}) 169 string(MAKE_C_IDENTIFIER "HAVE_${_flag}" varname) 170 171 if ("C" IN_LIST ${langs_var}) 172 check_c_compiler_flag(${_flag} ${varname}) 173 if (${varname}) 174 foreach (config IN LISTS ${configs_var}) 175 if (config STREQUAL "NONE") 176 set(config) 177 else () 178 set(config "_${config}") 179 endif () 180 string(APPEND CMAKE_C_FLAGS${config} " ${_flag}") 181 endforeach () 182 endif () 183 endif () 184 185 if ("CXX" IN_LIST ${langs_var}) 186 string(APPEND varname "_CXX") 187 check_cxx_compiler_flag(${_flag} ${varname}) 188 if (${varname}) 189 foreach (config IN LISTS ${configs_var}) 190 if (config STREQUAL "NONE") 191 set(config) 192 else () 193 set(config "_${config}") 194 endif () 195 string(APPEND CMAKE_CXX_FLAGS${config} " ${_flag}") 196 endforeach () 197 endif () 198 endif () 199 endforeach() 200 201 foreach(lang C CXX) 202 foreach (config IN LISTS ${configs_var}) 203 string(TOUPPER "${config}" config) 204 if (config STREQUAL "NONE") 205 set(config) 206 else () 207 set(config "_${config}") 208 endif () 209 string(STRIP "${CMAKE_${lang}_FLAGS${config}}" CMAKE_${lang}_FLAGS${config}) 210 set(CMAKE_${lang}_FLAGS${config} "${CMAKE_${lang}_FLAGS${config}}" PARENT_SCOPE) 211 endforeach () 212 endforeach() 213endfunction() 214 215# internal helper: _int_enable_warnings_set_flags(langs_var [warnings flags]) 216macro(_int_enable_warnings_set_flags langs_var) 217 set(configs "NONE") 218 _int_enable_warnings_set_flags_ex(${langs_var} configs ${ARGN}) 219endmacro() 220 221set(_flags_C) 222set(_flags_CXX) 223set(_debug_flags_C) 224set(_debug_flags_CXX) 225 226if (MSVC) 227 # Don't automatically set /W3 228 CMAKE_POLICY (SET CMP0092 NEW) 229 230 # Visual Studio uses a completely different nomenclature for warnings than gcc/mingw/clang, so none of the 231 # "-W[name]" warnings will work. 232 233 set(_flags 234 # Enable warnings 235 /W4 # Baseline reasonable warnings 236 /w14242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data 237 /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 238 /w14263 # 'function': member function does not override any base class virtual member function 239 /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may 240 # not be destructed correctly 241 /w14287 # 'operator': unsigned/negative constant mismatch 242 /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside 243 # the for-loop scope 244 /w14296 # 'operator': expression is always 'boolean_value' 245 /w14311 # 'variable': pointer truncation from 'type1' to 'type2' 246 /w14545 # expression before comma evaluates to a function which is missing an argument list 247 /w14546 # function call before comma missing argument list 248 /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect 249 /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? 250 /w14555 # expression has no effect; expected expression with side- effect 251 /w14619 # pragma warning: there is no warning number 'number' 252 /w14640 # Enable warning on thread un-safe static member initialization 253 /w14826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. 254 /w14905 # wide string literal cast to 'LPSTR' 255 /w14906 # string literal cast to 'LPWSTR' 256 /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied 257 258 # Disable some warnings 259 /wd4201 # nonstandard extension used: nameless struct/union. Used in some windows headers, e.g. IO_STATUS_BLOCK, 260 # disable. 261 262 # Turn some warnings into errors 263 /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 264 ) 265 266 list(APPEND _flags_C ${_flags}) 267 list(APPEND _flags_CXX ${_flags}) 268 269elseif (CMAKE_C_COMPILER_ID MATCHES "Intel") 270 # Intel's compiler warning flags are more like Visual Studio than GCC, though the numbers aren't the same. 271 set(_flags 272 # Use warning level 3, quite wordy. 273 -w3 274 # Disable warnings we don't care about (add more as they are encountered). 275 -wd383 # Spammy warning about initializing from a temporary object in C++ (which is done all the time ...). 276 -wd11074 # Diagnostic related to inlining. 277 -wd11076 # Diagnostic related to inlining. 278 ) 279 280 list(APPEND _flags_C ${_flags}) 281 list(APPEND _flags_CXX ${_flags}) 282 283elseif (CMAKE_C_COMPILER_ID MATCHES "XL") 284 set (_flags 285 -qwarn64 286 -qformat=all 287 -qflag=i:i 288 ) 289 list(APPEND _flags_C ${_flags}) 290 list(APPEND _flags_CXX ${_flags}) 291 292else () 293 # If we're compiling with GCC / Clang / MinGW (or anything else besides Visual Studio or Intel): 294 # C Flags: 295 list(APPEND _flags_C 296 -Wall 297 -Wextra 298 299 # Enable additional warnings not covered by Wall and Wextra. 300 -Waggregate-return 301 -Wcast-align 302 -Wcast-qual 303 -Wconversion 304 -Wdeclaration-after-statement 305 -Wdouble-promotion 306 -Wfloat-equal 307 -Wformat-security 308 -Winit-self 309 -Wjump-misses-init 310 -Wlogical-op 311 -Wmissing-braces 312 -Wmissing-declarations 313 -Wmissing-format-attribute 314 -Wmissing-include-dirs 315 -Wmissing-prototypes 316 -Wnested-externs 317 -Wno-coverage-mismatch 318 -Wold-style-definition 319 -Wpacked 320 -Wpedantic 321 -Wpointer-arith 322 -Wredundant-decls 323 -Wshadow 324 -Wsign-conversion 325 -Wstrict-overflow 326 -Wstrict-prototypes 327 -Wtrampolines 328 -Wundef 329 -Wunreachable-code 330 -Wunused 331 -Wvariadic-macros 332 -Wvla 333 -Wwrite-strings 334 335 # On Windows MinGW I think implicit fallthrough enabled by -Wextra must not default to 3 336 -Wimplicit-fallthrough=3 337 338 # Treat implicit variable typing and implicit function declarations as errors. 339 -Werror=implicit-int 340 -Werror=implicit-function-declaration 341 342 # Make MacOSX honor -mmacosx-version-min 343 -Werror=partial-availability 344 345 # Some clang versions might warn if an argument like "-I/path/to/headers" is unused, 346 # silence these. 347 -Qunused-arguments 348 349 -Wno-long-long 350 ) 351 352 # C++ flags: 353 list(APPEND _flags_CXX 354 -Wall 355 -Wextra 356 357 # Enable additional warnings not covered by Wall and Wextra. 358 -Wcast-align 359 -Wformat-security 360 -Wmissing-declarations 361 -Wmissing-format-attribute 362 -Wpacked-bitfield-compat 363 -Wredundant-decls 364 -Wvla 365 366 # Turn off unused parameter warnings with C++ (they happen often in C++ and Qt). 367 -Wno-unused-parameter 368 369 # Some clang versions might warn if an argument like "-I/path/to/headers" is unused, 370 # silence these. 371 -Qunused-arguments 372 ) 373 374 # Note: when cross-compiling to Windows from Cygwin, the Qt Mingw packages have a bunch of 375 # noisy type-conversion warnings in headers. So, only enable those warnings if we're 376 # not building that configuration. 377 if (NOT (WIN32 AND (CMAKE_HOST_SYSTEM_NAME MATCHES "CYGWIN"))) 378 list(APPEND _flags_CXX 379 -Wconversion 380 -Wfloat-equal 381 -Wsign-conversion 382 ) 383 endif () 384 385 # Add flags to force colored output even when output is redirected via pipe. 386 if (CMAKE_GENERATOR MATCHES "Ninja") 387 set(color_default TRUE) 388 else () 389 set(color_default FALSE) 390 endif () 391 option(FORCE_COLOR "Force compiler to always colorize, even when output is redirected." ${color_default}) 392 mark_as_advanced(FORCE FORCE_COLOR) 393 if (FORCE_COLOR) 394 set(_flags 395 -fdiagnostics-color=always # GCC 396 -fcolor-diagnostics # Clang 397 ) 398 list(APPEND _flags_C ${_flags}) 399 list(APPEND _flags_CXX ${_flags}) 400 endif () 401 402 # Add -fno-omit-frame-pointer (and optionally -fno-inline) to make debugging and stack dumps nicer. 403 set(_flags 404 -fno-omit-frame-pointer 405 ) 406 option(M_NO_INLINE "Disable function inlining for RelWithDebInfo and Debug configurations?" FALSE) 407 if (M_NO_INLINE) 408 list(APPEND _flags 409 -fno-inline 410 ) 411 endif () 412 list(APPEND _debug_flags_C ${_flags}) 413 list(APPEND _debug_flags_CXX ${_flags}) 414endif () 415 416# Check and set compiler flags. 417set(_debug_configs 418 RelWithDebInfo 419 Debug 420) 421foreach(_lang ${languages}) 422 _int_enable_warnings_set_flags(_lang ${_flags_${_lang}}) 423 _int_enable_warnings_set_flags_ex(_lang _debug_configs ${_debug_flags_${_lang}}) 424 425 # Ensure pure Debug builds are NOT optimized (not possible on Visual Studio). 426 # Any optimization of a Debug build will prevent debuggers like lldb from 427 # fully displaying backtraces and stepping. 428 if (NOT MSVC) 429 set(_config Debug) 430 _int_enable_warnings_set_flags_ex(_lang _config -O0) 431 endif () 432endforeach() 433 434# CMP0092 doesn't appear to really work, really remove the /W3 here. 435if (MSVC) 436 remove_warnings(/W3) 437endif () 438