Some Visual Studio 2017 compiler and linker flags are undocumented and seem impossible to change
Summary: even pulling every trick I can find to clear all of the CMAKE_C[etc]_FLAGS
for every build type/config, building Visual Studio 2017 targets always has certain flags present in the cl.exe
and link.exe
command line. Some of these are relatively harmless diagnostics, but some affect code generation eg. /fp:precise
.
I'm using CMake 3.12rc1 on Windows 10. The generator in question is Visual Studio 2017.
I have created a very minimal project for Visual Studio 2017 - it's on Gitlab if you want to check it out and test it. The program itself does nothing. The CMakeLists.txt
started off looking like:
cmake_minimum_required(VERSION 3.8)
add_executable(main "main.c")
I then generated the build files for Visual Studio 2017 (mkdir build
/cd build
/cmake ..
) and ran "cmake --build .". The cl.exe
command contained a number of flags, presumably sensible defaults for VS 2017. Just to be clear: I have absolutely no problem with sensible defaults for generators, it makes perfect sense. But I'm in a position where I need total control over the whole compilation chain. I figured the first step would be to try to start from a clean slate.
target_compile_options()
won't work, it appends to those defaults, it doesn't replace them. So I tried adding this:
set_target_properties(main PROPERTIES COMPILER_OPTIONS "")
I still saw a bunch of flags (listed later). I tried:
set(CMAKE_C_FLAGS "")
set(CMAKE_C_FLAGS_DEBUG "")
set(CMAKE_C_FLAGS_RELEASE "")
The flags are still there. Finally, I try a trick I saw in an answer on Stackoverflow: How to set compiler options with CMake in Visual Studio 2017. I create a CompilerOptions.cmake
file containing:
# log all *_INIT variables
get_cmake_property(_varNames VARIABLES)
list (REMOVE_DUPLICATES _varNames)
list (SORT _varNames)
foreach (_varName ${_varNames})
if (_varName MATCHES "_INIT$")
message(STATUS "${_varName}=${${_varName}}")
endif()
endforeach()
if (MSVC)
set(CMAKE_C_FLAGS_INIT "")
set(CMAKE_CXX_FLAGS_INIT "")
set(CMAKE_C_FLAGS_DEBUG_INIT "")
set(CMAKE_CXX_FLAGS_DEBUG_INIT "")
set(CMAKE_C_FLAGS_RELEASE_INIT "")
set(CMAKE_CXX_FLAGS_RELEASE_INIT "")
set(CMAKE_C_FLAGS "")
set(CMAKE_CXX_FLAGS "")
set(CMAKE_C_FLAGS_DEBUG "")
set(CMAKE_CXX_FLAGS_DEBUG "")
set(CMAKE_C_FLAGS_RELEASE "")
set(CMAKE_CXX_FLAGS_RELEASE "")
set(CMAKE_C_STANDARD_LIBRARIES_INIT "")
set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "")
set(CMAKE_C_STANDARD_LIBRARIES "")
set(CMAKE_CXX_STANDARD_LIBRARIES "")
endif()
The flags are still there, specifically: /c /nologo /W1 /WX- /diagnostics:classic /O2 /Oy- [...macros...] /Gm- /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline [...essential args...]" /Gd /TC /analyze- /FC /errorReport:queue
To my surprise, even dumping every related variable I can think of, those persistent flags don't actually appear anywhere eg. /fp:precise
and /WX-
are totally absent from any of these variables. But they are in the generated solutions. My final CMakeLists.txt
has some unholy mess of variable setting and clearing at multiple points to try and fix this, but... nothing seems to work.
Rather than dump 150 lines of generate and build output here, I've posted it as a Gitlab snippet. I can copy it here if you like.
Regarding the flags themselves:
- Some are essential for building the particular target I've created. Okay.
- Some affect diagnostic output only. While they won't affect code generation I wouldn't say they're harmless - some places have policies on what warnings to show or not, some people have their CI system set up to react in a particular way to warnings. But not catastrophic.
- Some of these options affect code generation eg.
/fp:precise
can change thewidths of thecode generated for floating point arithmetic (depending on host and target architecture) compared todouble
andfloat
data types/fp:fast
. Others (I think) can change the width of certain data types (disastrous if you're trying to achieve compatibility with an ABI). - Some of these options can be cancelled out by later options. However, default behaviour is difficult to restore in this case. Some options cancel out, but issue warnings if they're in conflict. Not all options can be overridden.
- Quite aside from whether these options can be overridden or not, I would expect this to be documented... somewhere. (Maybe it is? But I spent days looking and couldn't find anything.) Both where the flags come from and how to turn them off. For some projects, sensible defaults make total sense as a basis for further tweaking. But for some projects, it's necessary to account for everything that affects code generation.
So the issue is two-fold:
- I want to turn them off! Ideally I want to turn them off for specific targets, but failing that, I will happily turn them off everywhere and put the defaults back in for targets that use them.
- Something about this should be documented eg. "here is where your compiler flags can come from". If this already exists, it should be linked to from... maybe the relevant function and property pages?