CMake - compile with /MT instead of /MD

ksl picture ksl · Dec 27, 2013 · Viewed 12.2k times · Source

I'm a newbie to cmake (2.8.12.1) and I'm using it on Windows to generate the project files to build cpp-netlib using Visual Studio 2012.

By default it compiles with the /MDd compiler switch. I want to change it so that it uses /MTd.

I followed the advice given here https://stackoverflow.com/a/14172871 but it isn't working for me.

Specifically, I added the second line shown below in the if statement to CmakeLists.txt.

if (MSVC)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
endif()

When I open the Visual Studio sln file I can see that the /MDd option is still set. Furthermore, I see the following in CMakeCache.txt:

//Flags used by the compiler during debug builds.
CMAKE_CXX_FLAGS_DEBUG:STRING=/D_DEBUG /MDd /Zi /Ob0 /Od /RTC1

I've also tried setting the flags from scratch like this:

set(CMAKE_CXX_FLAGS_DEBUG "/MTd")

but that doesn't work either.

If I pass the option via the command line like this:

-DCMAKE_CXX_FLAGS_DEBUG="/MTd"

the option is successfully set in the Visual Studio projects.

Can anyone tell me what I'm doing wrong?

I would also appreciate it if someone could enlighten me as to where the values in the cache come from that I don't specify on the command line or aren't in CmakeLists.txt.

Adding CMakeList.txt as requested. I've never posted before so apologies if I've not done this right.

# Original from cpp-netlib.org with my edits

cmake_minimum_required(VERSION 2.8)
project(CPP-NETLIB)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTI_THREADED ON)
find_package( Boost 1.45.0 REQUIRED unit_test_framework system regex date_time thread filesystem program_options chrono )
find_package( OpenSSL )
find_package( Threads )
set(CMAKE_VERBOSE_MAKEFILE true)

if (CMAKE_BUILD_TYPE MATCHES Debug)
    add_definitions(-DBOOST_NETWORK_DEBUG)
endif()

if (OPENSSL_FOUND)
    add_definitions(-DBOOST_NETWORK_ENABLE_HTTPS)
    include_directories(${OPENSSL_INCLUDE_DIR})
endif()

if (${CMAKE_CXX_COMPILER_ID} MATCHES GNU)
  set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
endif()

if (Boost_FOUND)
    ################# added #################
    add_definitions(-DBOOST_ALL_NO_LIB)
    #########################################
    if (MSVC)
      add_definitions(-D_SCL_SECURE_NO_WARNINGS)
    endif(MSVC)
    if (WIN32)
      add_definitions(-D_WIN32_WINNT=0x0501)
    endif(WIN32)
    include_directories(${Boost_INCLUDE_DIRS})
    enable_testing()
    add_subdirectory(libs/network/src)
    add_subdirectory(libs/network/test)
    if (NOT MSVC)
      add_subdirectory(libs/mime/test)
    endif(NOT MSVC)
    add_subdirectory(libs/network/example)
endif(Boost_FOUND)

if (MSVC)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
    ################# added #################
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
    #########################################
endif()

enable_testing()

Answer

Fraser picture Fraser · Dec 27, 2013

Adding the code inside your if block should work fine. However, since it doesn't produce the required effect in your .sln, either the code inside the if block isn't being executed or the flags are being replaced later in the CMakeLists.txt.

As to where the cached values for these flags come from; CMake applies and caches what are considered to be useful default values for each default build type.

Have you added your changes to the flags before the project call? project sets up a lot of things, including the compiler flags. If you set these flags without caching them (i.e. calling set as you have shown), project will overwrite them. If you set the flags and cache them (e.g. using set(... CACHE), or by adding them via the command line arg -D as you have shown), then project won't overwrite them.

Also, since calling set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") doesn't use the CACHE option of set, then this change to the variable CMAKE_CXX_FLAGS won't appear in your CMakeCache.txt file.

In order to get this into the cache, you'd have to also use the FORCE option of set, since you'd be telling CMake to replace an already-cached value (CMake itself caches these flag variables by default). I wouldn't recommend this however - I can't see why you'd need to do this, and you'd be best to look for a better way than appending the flag(s) you want, since every time CMake runs the appended flags would be re-appended.

If this doesn't allow you to resolve your issue, perhaps you could show a full (small) CMakeLists.txt which exhibits the problem?

UPDATE:

OK - The problem is a scoping issue. The changes to the compiler flags will be applied to all targets (exes/libs) which are defined in that CMakeLists.txt, but only to subdirectory targets which are added after the flags are modified.

The add_subdirectory command introduces a new scope (although this isn't mentioned in the docs) which inherits a copy of the parent's variables. However, subsequent changes to the variables in the parent scope don't affect the child copies, and likewise changing the child copies doesn't affect the value of the parent's copies (unless you force it using set(... PARENT_SCOPE)).

So, to fix your issue, just move the if(MSVC) block to before all the add_subdirectory calls.