1
2# Copyright (C) 2011-2020 Daniel Scharrer
3#
4# This software is provided 'as-is', without any express or implied
5# warranty.  In no event will the author(s) be held liable for any damages
6# arising from the use of this software.
7#
8# Permission is granted to anyone to use this software for any purpose,
9# including commercial applications, and to alter it and redistribute it
10# freely, subject to the following restrictions:
11#
12# 1. The origin of this software must not be misrepresented; you must not
13#    claim that you wrote the original software. If you use this software
14#    in a product, an acknowledgment in the product documentation would be
15#    appreciated but is not required.
16# 2. Altered source versions must be plainly marked as such, and must not be
17#    misrepresented as being the original software.
18# 3. This notice may not be removed or altered from any source distribution.
19
20# Note: In CMake before 3.0 set(var "" PARENT_SCOPE) *unsets* the variable in the
21# parent scope instead of setting it to the empty string.
22# This means if(var STREQUAL "") will be false since var is not defined and thus not expanded.
23
24function(check_compile RESULT FILE FLAG TYPE)
25
26	string(REGEX REPLACE "[^a-zA-Z0-9_][^a-zA-Z0-9_]*" "-" cachevar "${TYPE}-${FLAG}")
27	set(cahevar "CHECK_COMPILE_${cahevar}")
28
29	if(DEFINED ${cachevar})
30		if(${cachevar})
31			set(${RESULT} "${FLAG}" PARENT_SCOPE)
32		else()
33			set(${RESULT} "" PARENT_SCOPE)
34		endif()
35		return()
36	endif()
37
38	string(REGEX REPLACE "[^a-zA-Z0-9]" "\\\\\\0" escaped_flag ${FLAG} )
39
40	# CMake already has a check_cxx_compiler_flag macro in CheckCXXCompilerFlag, but
41	# it prints the result variable in the output (which is ugly!) and also uses it
42	# as a key to cache checks - so it would need to be unique for each flag.
43	# Unfortunately it also naively pastes the variable name inside a regexp so
44	# if we tried to use the flag itself in the variable name it will fail for -std=c++11.
45	# But we can at least use the expressions for warnings from that macro (and more):
46	set(fail_regexps
47		"warning:"                                     # general
48		"unrecognized .*option"                        # GNU
49		"${escaped_flag}.* not supported"              # GNU
50		"unknown .*option"                             # Clang
51		"ignoring unknown option"                      # MSVC
52		"warning D9002"                                # MSVC, any lang
53		"warning #[0-9]*:"                             # Intel
54		"option.*not supported"                        # Intel
55		"invalid argument .*option"                    # Intel
56		"ignoring option .*argument required"          # Intel
57		"command line warning"                         # Intel
58		"[Uu]nknown option"                            # HP
59		"[Ww]arning: [Oo]ption"                        # SunPro
60		"command option .* is not recognized"          # XL
61		"not supported in this configuration; ignored" # AIX
62		"File with unknown suffix passed to linker"    # PGI
63		"WARNING: unknown flag:"                       # Open64
64	)
65
66	# Set the flags to check
67	set(old_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
68	set(old_CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
69	set(old_CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
70	set(old_CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
71	if(TYPE STREQUAL "linker flag")
72		set(CMAKE_EXE_LINKER_FLAGS "${old_CMAKE_EXE_LINKER_FLAGS} ${FLAG}")
73		set(CMAKE_SHARED_LINKER_FLAGS "${old_CMAKE_SHARED_LINKER_FLAGS} ${FLAG}")
74		set(CMAKE_MODULE_LINKER_FLAGS "${old_CMAKE_MODULE_LINKER_FLAGS} ${FLAG}")
75	elseif(TYPE STREQUAL "compiler flag")
76		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}")
77	endif()
78
79	# Check if we can compile and link a simple file with the new flags
80	try_compile(
81		check_compiler_flag ${PROJECT_BINARY_DIR} ${FILE}
82		CMAKE_FLAGS "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}"
83		            "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}"
84		            "-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS}"
85		            "-DCMAKE_MODULE_LINKER_FLAGS:STRING=${CMAKE_MODULE_LINKER_FLAGS}"
86		OUTPUT_VARIABLE ERRORLOG
87	)
88
89	# Restore the old flags
90	set(CMAKE_CXX_FLAGS "${old_CMAKE_CXX_FLAGS}")
91	set(CMAKE_EXE_LINKER_FLAGS "${old_CMAKE_EXE_LINKER_FLAGS}")
92	set(CMAKE_SHARED_LINKER_FLAGS "${old_CMAKE_SHARED_LINKER_FLAGS}")
93	set(CMAKE_MODULE_LINKER_FLAGS "${old_CMAKE_MODULE_LINKER_FLAGS}")
94
95	if(NOT check_compiler_flag)
96		message(STATUS "Checking ${TYPE}: ${FLAG} - unsupported")
97		set(${RESULT} "" PARENT_SCOPE)
98		set("${cachevar}" 0 CACHE INTERNAL "...")
99	else()
100
101		set(has_warning 0)
102		foreach(expr IN LISTS fail_regexps)
103			if("${ERRORLOG}" MATCHES "${expr}")
104				set(has_warning 1)
105			endif()
106		endforeach()
107
108		if(has_warning)
109			message(STATUS "Checking ${TYPE}: ${FLAG} - unsupported (warning)")
110			set(${RESULT} "" PARENT_SCOPE)
111			set("${cachevar}" 0 CACHE INTERNAL "...")
112		else()
113			message(STATUS "Checking ${TYPE}: ${FLAG}")
114			set(${RESULT} "${FLAG}" PARENT_SCOPE)
115			set("${cachevar}" 1 CACHE INTERNAL "...")
116		endif()
117
118	endif()
119
120endfunction(check_compile)
121
122function(check_flag RESULT FLAG TYPE)
123	set(compile_test_file "${CMAKE_CURRENT_BINARY_DIR}/compile_flag_test.cpp")
124	if(MSVC)
125		file(WRITE ${compile_test_file} "int main(){ return 0; }\n")
126	else()
127		file(WRITE ${compile_test_file} "__attribute__((const)) int main(){ return 0; }\n")
128	endif()
129	check_compile(result "${compile_test_file}" "${FLAG}" "${TYPE} flag")
130	set(${RESULT} "${result}" PARENT_SCOPE)
131endfunction(check_flag)
132
133macro(strip_warning_flags VAR)
134	string(REGEX REPLACE "(^| )\\-(W[^ l][^ ]*|Wl[^,][^ ]*|pedantic)" "" ${VAR} "${${VAR}}")
135endmacro()
136
137function(check_builtin RESULT EXPR)
138	string(REGEX REPLACE "[^a-zA-Z0-9_][^a-zA-Z0-9_]*" "-" check "${EXPR}")
139	string(REGEX REPLACE "_*\\-_*" "-" check "${check}")
140	string(REGEX REPLACE "^[_\\-]+" "" check "${check}")
141	string(REGEX REPLACE "[_\\-]+$" "" check "${check}")
142	set(compile_test_file "${CMAKE_CURRENT_BINARY_DIR}/check-builtin-${check}.cpp")
143	string(REGEX MATCH "[a-zA-Z_][a-zA-Z_0-9]*" type "${EXPR}")
144	file(WRITE ${compile_test_file} "__attribute__((const)) int main(){ (void)(${EXPR}); return 0; }\n")
145	set(old_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
146	set(old_CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
147	strip_warning_flags(CMAKE_CXX_FLAGS)
148	strip_warning_flags(CMAKE_EXE_LINKER_FLAGS)
149	check_compile(result "${compile_test_file}" "${type}" "compiler builtin")
150	set(CMAKE_CXX_FLAGS "${old_CMAKE_CXX_FLAGS}")
151	set(CMAKE_EXE_LINKER_FLAGS "${old_CMAKE_EXE_LINKER_FLAGS}")
152	set(${RESULT} "${result}" PARENT_SCOPE)
153endfunction(check_builtin)
154
155function(check_compiler_flag RESULT FLAG)
156	check_flag(result "${FLAG}" compiler)
157	set(${RESULT} "${result}" PARENT_SCOPE)
158endfunction(check_compiler_flag)
159
160function(check_linker_flag RESULT FLAG)
161	check_flag(result "${FLAG}" linker)
162	set(${RESULT} "${result}" PARENT_SCOPE)
163endfunction(check_linker_flag)
164
165function(add_cxxflag FLAG)
166
167	check_compiler_flag(RESULT "${FLAG}")
168
169	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RESULT}" PARENT_SCOPE)
170
171	if(NOT DEFINED RESULT OR RESULT STREQUAL "")
172		set(FLAG_FOUND 0 PARENT_SCOPE)
173	else()
174		set(FLAG_FOUND 1 PARENT_SCOPE)
175	endif()
176
177endfunction(add_cxxflag)
178
179function(add_ldflag FLAG)
180
181	check_linker_flag(RESULT "${FLAG}")
182
183	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${RESULT}" PARENT_SCOPE)
184	set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${RESULT}" PARENT_SCOPE)
185	set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${RESULT}" PARENT_SCOPE)
186
187	if(NOT DEFINED RESULT OR RESULT STREQUAL "")
188		set(FLAG_FOUND 0 PARENT_SCOPE)
189	else()
190		set(FLAG_FOUND 1 PARENT_SCOPE)
191	endif()
192
193endfunction(add_ldflag)
194