It is impossible to reliably use target_compile_options() on MSVC.
Description of the problem
Please, consider the following CMakeLists.txt:
cmake_minimum_required (VERSION 3.14)
project(Foo VERSION 0.1.0 LANGUAGES CXX)
add_executable(foo)
target_sources(foo PRIVATE Foo.cpp)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(foo PRIVATE -Wall
-Wextra
-Wpedantic
-Werror)
target_compile_options(foo PRIVATE -fno-exceptions
-fno-rtti)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(foo PRIVATE /W4
/WX)
target_compile_options(foo PRIVATE /EHs-c-
/GR-)
endif()
The problem is that building this project on Windows via MSVC, using Ninja or NMake, yields the following warnings:
cl : Command line warning D9025 : overriding '/W3' with '/W4'
cl : Command line warning D9025 : overriding '/EHs' with '/EHs-'
cl : Command line warning D9025 : overriding '/EHc' with '/EHc-'
cl : Command line warning D9025 : overriding '/GR' with '/GR-'
Origin of the problem
The reason why these warnings are reported by MSVC is because compiler options of the foo target
are conflicting with default compiler options provided by CMake:
CMAKE_CXX_FLAGS = /DWIN32 /D_WINDOWS /W3 /GR /EHsc
CMAKE_CXX_FLAGS_DEBUG = /MDd /Zi /Ob0 /Od /RTC1
CMAKE_CXX_FLAGS_RELEASE = /MD /O2 /Ob2 /DNDEBUG
CMAKE_CXX_FLAGS_MINSIZEREL = /MD /O1 /Ob1 /DNDEBUG
CMAKE_CXX_FLAGS_RELWITHDEBINFO = /MD /Zi /O2 /Ob1 /DNDEBUG
Note that, these flags are set here: //CMake/Modules/Platform/Windows-MSVC.cmake
Interestingly, the situation is little bit different on Visual Studio,
since conflicting options will be replaced, except for options controlling C++ exception handling:
cl /c /Zi /W4 /WX /Od /Ob0 /D WIN32 /D _WINDOWS /EHsc /RTC1 /MDd /GS /GR- /EHs-c- Foo.cpp
Scale of the problem
This is nothing new - this problem is known for a very long time and affects almost all CMake developers.
These are just few selected links showing the widespread it is:
- CMake shouldn't add /W3 in Windows-MSVC.cmake
- [CMake] Warning D9025 : overriding '/W3' with '/W4'
- How to set compiler options with CMake in Visual Studio 2017
- How to set warning level in CMake?
- [CMake] [MSVC] Setting warning level on target feels like long-time bug
- CMake overriding my /MDd flag with /MTd
- CMake should work around D9025 when using NMake Makefiles generator
- [CMake] Disabling exceptions and rtti on VS
- CMake should work around D9025 when using NMake Makefiles generator
Lack of proper solution
The only "solution" to this problem, that I've managed to find, is to change CMake global state:
- by either filtering out conflicting compiler options from
CMAKE_CXX_FLAGS
:
string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
- or by overriding CMake default compiler flags via
CMAKE_USER_MAKE_RULES_OVERRIDE
:
set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_LIST_DIR}/MyOverride.cmake")
# MyOverride.cmake
string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT}")
Note that, these approaches are recommended by the official CMake FAQ:
The problem is none of these approaches allow for removal conflicting flags from a single CMake target.
This makes setting compiler options using Modern CMake style almost impossible!
Fiddling with global CMake state breaks ability to locally reason about the build system and
prevents cleanly composing CMake targets into large projects.
Direction to move forward
If there is no way of solving this issue locally, within single CMake target,
what kind of approach fixing this situation would you guys suggest?
Sure, there is a proposal, which would allow developers to manage compiler warnings portably in CMake:
However, mentioned proposal doesn't solve this issue completely,
because controlling these compiler options would still be a problem:
- C++ exceptions (
/EHsc
vs/EHs-c-
) - RTTI (
/GR
/GR-
) - Run-Time Library (
/MD[d]
vs/MT[d]
vs/LD[d]
)
Unfortunately I don't know ideal solution of this problem:
- introducing in CMake portable way of disabling C++ exceptions and RTTI is very tricky,
because different compilers handle this differently: Portable way to disable RTTI and exceptions - changing how CMake handles compiler options would not be backward compatible
- disabling MSVC command-line warning D9025 is undesirable and even not possible
Do you guys have any suggestions how to solve this problem?
Best regards, Mateusz