Search code examples
visual-c++cmakevisual-studio-2017

How to set compiler options with CMake in Visual Studio 2017


Visual Studio 2017 comes with full CMake integration. To learn about this combination, I was starting with this basic sample:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(foo)
add_executable(foo foo.cpp)

and

// foo.cpp
int main() {}

This properly generates build scripts, and compiles and links with no issues. That was easy.

Trying to set compiler options, on the other hand, turned out to be anything but trivial. In my case I was attempting to set the warning level to 4.

The obvious solution

add_compile_options("/W4")

didn't pan out as expected. The command line passed to the compiler now contains both /W4 (as intended) as well as /W3 (picked up from somewhere else), producing the following warning:

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

To work around this, I would need to replace any incompatible compiler option(s) instead of just adding one. CMake does not provide any immediate support for this, and the standard solution (as this Q&A suggests) seems to be:

if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
    string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()

This, however, has two issues:

  • It sets the global CMAKE_CXX_FLAGS, applying to all C++ targets. This may not be intended (not an issue for me right now).
  • It doesn't scale. For every compiler option to add, you would have to read up on incompatible options, and manually strip those first. This will inevitably fail1.

My question is two-fold:

  1. Where does the CMake integration pick up default settings from, and can this be controlled?
  2. How do you set compiler options in general? (If this is too broad a topic, I'd be happy for help on setting the warning level only.)


1 Incidentally, the solution I replicated fails to account for the /Wall option, that is incompatible with /W4 as well.


Solution

  • 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.