# Copyright (C) 2011-2020 Daniel Scharrer # # This software is provided 'as-is', without any express or implied # warranty. In no event will the author(s) be held liable for any damages # arising from the use of this software. # # Permission is granted to anyone to use this software for any purpose, # including commercial applications, and to alter it and redistribute it # freely, subject to the following restrictions: # # 1. The origin of this software must not be misrepresented; you must not # claim that you wrote the original software. If you use this software # in a product, an acknowledgment in the product documentation would be # appreciated but is not required. # 2. Altered source versions must be plainly marked as such, and must not be # misrepresented as being the original software. # 3. This notice may not be removed or altered from any source distribution. # Note: In CMake before 3.0 set(var "" PARENT_SCOPE) *unsets* the variable in the # parent scope instead of setting it to the empty string. # This means if(var STREQUAL "") will be false since var is not defined and thus not expanded. function(check_compile RESULT FILE FLAG TYPE) string(REGEX REPLACE "[^a-zA-Z0-9_][^a-zA-Z0-9_]*" "-" cachevar "${TYPE}-${FLAG}") set(cahevar "CHECK_COMPILE_${cahevar}") if(DEFINED ${cachevar}) if(${cachevar}) set(${RESULT} "${FLAG}" PARENT_SCOPE) else() set(${RESULT} "" PARENT_SCOPE) endif() return() endif() string(REGEX REPLACE "[^a-zA-Z0-9]" "\\\\\\0" escaped_flag ${FLAG} ) # CMake already has a check_cxx_compiler_flag macro in CheckCXXCompilerFlag, but # it prints the result variable in the output (which is ugly!) and also uses it # as a key to cache checks - so it would need to be unique for each flag. # Unfortunately it also naively pastes the variable name inside a regexp so # if we tried to use the flag itself in the variable name it will fail for -std=c++11. # But we can at least use the expressions for warnings from that macro (and more): set(fail_regexps "warning:" # general "unrecognized .*option" # GNU "${escaped_flag}.* not supported" # GNU "unknown .*option" # Clang "ignoring unknown option" # MSVC "warning D9002" # MSVC, any lang "warning #[0-9]*:" # Intel "option.*not supported" # Intel "invalid argument .*option" # Intel "ignoring option .*argument required" # Intel "command line warning" # Intel "[Uu]nknown option" # HP "[Ww]arning: [Oo]ption" # SunPro "command option .* is not recognized" # XL "not supported in this configuration; ignored" # AIX "File with unknown suffix passed to linker" # PGI "WARNING: unknown flag:" # Open64 ) # Set the flags to check set(old_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") set(old_CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") set(old_CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") set(old_CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") if(TYPE STREQUAL "linker flag") set(CMAKE_EXE_LINKER_FLAGS "${old_CMAKE_EXE_LINKER_FLAGS} ${FLAG}") set(CMAKE_SHARED_LINKER_FLAGS "${old_CMAKE_SHARED_LINKER_FLAGS} ${FLAG}") set(CMAKE_MODULE_LINKER_FLAGS "${old_CMAKE_MODULE_LINKER_FLAGS} ${FLAG}") elseif(TYPE STREQUAL "compiler flag") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") endif() # Check if we can compile and link a simple file with the new flags try_compile( check_compiler_flag ${PROJECT_BINARY_DIR} ${FILE} CMAKE_FLAGS "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}" "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}" "-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS}" "-DCMAKE_MODULE_LINKER_FLAGS:STRING=${CMAKE_MODULE_LINKER_FLAGS}" OUTPUT_VARIABLE ERRORLOG ) # Restore the old flags set(CMAKE_CXX_FLAGS "${old_CMAKE_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${old_CMAKE_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${old_CMAKE_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${old_CMAKE_MODULE_LINKER_FLAGS}") if(NOT check_compiler_flag) message(STATUS "Checking ${TYPE}: ${FLAG} - unsupported") set(${RESULT} "" PARENT_SCOPE) set("${cachevar}" 0 CACHE INTERNAL "...") else() set(has_warning 0) foreach(expr IN LISTS fail_regexps) if("${ERRORLOG}" MATCHES "${expr}") set(has_warning 1) endif() endforeach() if(has_warning) message(STATUS "Checking ${TYPE}: ${FLAG} - unsupported (warning)") set(${RESULT} "" PARENT_SCOPE) set("${cachevar}" 0 CACHE INTERNAL "...") else() message(STATUS "Checking ${TYPE}: ${FLAG}") set(${RESULT} "${FLAG}" PARENT_SCOPE) set("${cachevar}" 1 CACHE INTERNAL "...") endif() endif() endfunction(check_compile) function(check_flag RESULT FLAG TYPE) set(compile_test_file "${CMAKE_CURRENT_BINARY_DIR}/compile_flag_test.cpp") if(MSVC) file(WRITE ${compile_test_file} "int main(){ return 0; }\n") else() file(WRITE ${compile_test_file} "__attribute__((const)) int main(){ return 0; }\n") endif() check_compile(result "${compile_test_file}" "${FLAG}" "${TYPE} flag") set(${RESULT} "${result}" PARENT_SCOPE) endfunction(check_flag) macro(strip_warning_flags VAR) string(REGEX REPLACE "(^| )\\-(W[^ l][^ ]*|Wl[^,][^ ]*|pedantic)" "" ${VAR} "${${VAR}}") endmacro() function(check_builtin RESULT EXPR) string(REGEX REPLACE "[^a-zA-Z0-9_][^a-zA-Z0-9_]*" "-" check "${EXPR}") string(REGEX REPLACE "_*\\-_*" "-" check "${check}") string(REGEX REPLACE "^[_\\-]+" "" check "${check}") string(REGEX REPLACE "[_\\-]+$" "" check "${check}") set(compile_test_file "${CMAKE_CURRENT_BINARY_DIR}/check-builtin-${check}.cpp") string(REGEX MATCH "[a-zA-Z_][a-zA-Z_0-9]*" type "${EXPR}") file(WRITE ${compile_test_file} "__attribute__((const)) int main(){ (void)(${EXPR}); return 0; }\n") set(old_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") set(old_CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") strip_warning_flags(CMAKE_CXX_FLAGS) strip_warning_flags(CMAKE_EXE_LINKER_FLAGS) check_compile(result "${compile_test_file}" "${type}" "compiler builtin") set(CMAKE_CXX_FLAGS "${old_CMAKE_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${old_CMAKE_EXE_LINKER_FLAGS}") set(${RESULT} "${result}" PARENT_SCOPE) endfunction(check_builtin) function(check_compiler_flag RESULT FLAG) check_flag(result "${FLAG}" compiler) set(${RESULT} "${result}" PARENT_SCOPE) endfunction(check_compiler_flag) function(check_linker_flag RESULT FLAG) check_flag(result "${FLAG}" linker) set(${RESULT} "${result}" PARENT_SCOPE) endfunction(check_linker_flag) function(add_cxxflag FLAG) check_compiler_flag(RESULT "${FLAG}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RESULT}" PARENT_SCOPE) if(NOT DEFINED RESULT OR RESULT STREQUAL "") set(FLAG_FOUND 0 PARENT_SCOPE) else() set(FLAG_FOUND 1 PARENT_SCOPE) endif() endfunction(add_cxxflag) function(add_ldflag FLAG) check_linker_flag(RESULT "${FLAG}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${RESULT}" PARENT_SCOPE) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${RESULT}" PARENT_SCOPE) set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${RESULT}" PARENT_SCOPE) if(NOT DEFINED RESULT OR RESULT STREQUAL "") set(FLAG_FOUND 0 PARENT_SCOPE) else() set(FLAG_FOUND 1 PARENT_SCOPE) endif() endfunction(add_ldflag)