How to set compiler options with CMake in Visual Studio 2017

Turning my comment into an answer

CMake does come with some compiler switches preset. For visual studio those are mainly standard link libraries, warning levels, optimization levels, exception handling, debug information and platform specific defines.

What you now have to differentiate when you want to change a CMake generated compiler settings are the following use cases:

  1. Additional compiler flags CMake does not define vs. changing CMake's preset settings
  2. Project default settings vs. project user defined settings

So let's discuss common solutions for those cases.


User changes/adds to Project/CMake Compiler Flags Defaults

The standard way would be to modify the cached compiler flags variables by using tools shipped with CMake like cmake-gui and ccmake.

To achieve this in Visual Studio you would have to:

  • CMake / Cache / View CMakeCache
  • Manually change e.g. CMAKE_CXX_FLAGS to /Wall

    CMakeCache.txt

    //Flags used by the compiler during all build types.
    CMAKE_CXX_FLAGS:STRING= /DWIN32 /D_WINDOWS /Wall /GR /EHsc
    
  • CMake / Cache / Generate


Or you preset the CMAKE_CXX_FLAGS cache variable via a CMakeSettings.json file:

  • CMake / Change CMake Settings

    Force the cache entry with -DCMAKE_CXX_FLAGS:STRING=... in cmakeCommandArgs

    CMakeSettings.json

    {
        // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
        "configurations": [
            {
                "name": "x86-Debug (all warnings)",
                "generator": "Visual Studio 15 2017",
                "configurationType": "Debug",
                "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
                "cmakeCommandArgs": "-DCMAKE_CXX_FLAGS:STRING=\"/DWIN32 /D_WINDOWS /Wall /GR /EHsc\"",
                "buildCommandArgs": "-m -v:minimal"
            }
        ]
    }
    
  • If you deliver this CMakeSettings.json file with your CMake project it gets permanent


Project changes to CMake Compiler Flags Defaults

If you want to keep most of CMake's compiler flags in place, @sakra's answer is definitely the way to go.

For my VS projects I've put the CXX flag settings into a toolchain file coming with the project itself. Mainly to freeze those settings and don't have a dependency the CMake version used or any environment variables set.

Taking the example from above that would look like:

VS2017Toolchain.cmake

set(CMAKE_CXX_FLAGS "/DWIN32 /D_WINDOWS /Wall /GR /EHsc" CACHE INTERNAL "")

CMakeSettings.json

    {
        // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
        "configurations": [
            {
                "name": "x86-Debug (all warnings)",
                "generator": "Visual Studio 15 2017",
                "configurationType": "Debug",
                "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
                "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=\"${projectDir}\\VS2017Toolchain.cmake\"",
                "buildCommandArgs": "-m -v:minimal"
            }
        ]
    }

References

  • Visual C++ Team Blog: CMake support in Visual Studio – the Visual Studio 2017 RC update
  • set diagnostics:caret from CMakeLists.txt
  • Is Cmake set variable recursive?
  • Passing compiler options cmake

The default settings for the compiler are picked up from standard module files located in the Modules directory of the CMake installation. The actual module file used depends on both the platform and the compiler. E.g., for Visual Studio 2017, CMake will load the default settings from the file Windows-MSVC.cmake and language specific settings from Windows-MSVC-C.cmake or Windows-MSVC-CXX.cmake.

To inspect the default settings, create a file CompilerOptions.cmake in the project directory with the following contents:

# 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()

Then initialize the CMAKE_USER_MAKE_RULES_OVERRIDE variable in your CMakeLists.txt:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
set (CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_LIST_DIR}/CompilerOptions.cmake")
project(foo)
add_executable(foo foo.cpp)

When the project is configured upon opening the directory using Open Folder in Visual Studio 2017, the following information will be shown in the IDE's output window:

 ...
 -- CMAKE_CXX_FLAGS_DEBUG_INIT= /MDd /Zi /Ob0 /Od /RTC1
 -- CMAKE_CXX_FLAGS_INIT= /DWIN32 /D_WINDOWS /W3 /GR /EHsc
 -- CMAKE_CXX_FLAGS_MINSIZEREL_INIT= /MD /O1 /Ob1 /DNDEBUG
 -- CMAKE_CXX_FLAGS_RELEASE_INIT= /MD /O2 /Ob2 /DNDEBUG
 -- CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT= /MD /Zi /O2 /Ob1 /DNDEBUG
 ...

So the warning setting /W3 is picked up from the CMake variable CMAKE_CXX_FLAGS_INIT which then applies to all CMake targets generated in the project.

To control the warning level on the CMake project or target level, one can alter the CMAKE_CXX_FLAGS_INIT variable in the CompilerOptions.cmake by adding the following lines to the file:

if (MSVC)
    # remove default warning level from CMAKE_CXX_FLAGS_INIT
    string (REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT}")
endif()

The warning flags can then be controlled by setting the target compile options in CMakeLists.txt:

...
add_executable(foo foo.cpp)
target_compile_options(foo PRIVATE "/W4")

For most CMake projects it makes sense to control the default compiler options in a rules override file instead of manually tweaking variables like CMAKE_CXX_FLAGS.

When making changes to the CompilerOptions.cmake file, it is necessary to recreate the build folder. When using Visual Studio 2017 in Open Folder mode, choose the command Cache ... -> Delete Cache Folders from the CMake menu and then Cache ... -> Generate from the CMake menu to recreate the build folder.


In CMake 3.15, CMake introduced a fix for this MSVC-specific warning:

cl : Command line warning D9025: overriding '/W3' with '/W4'

and the compiler warning flags (like /W3) are no longer automatically added. So by upgrading to CMake 3.15 or greater, this warning should no longer appear. From the docs:

When using MSVC-like compilers in CMake 3.14 and below, warning flags like /W3 are added to CMAKE_<LANG>_FLAGS by default. This is problematic for projects that want to choose a different warning level programmatically. In particular, it requires string editing of the CMAKE_<LANG>_FLAGS variables with knowledge of the CMake builtin defaults so they can be replaced.

CMake 3.15 and above prefer to leave out warning flags from the value of CMAKE_<LANG>_FLAGS by default.

Along with this fix, CMake introduced policy CMP0092, which allows you to switch back to the OLD behavior (adding the warning flags by default) if necessary.