• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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