Setting the MSVC runtime in CMake
Asked Answered
R

4

52

I'm following the instructions in the CMake FAQ entry "How can I build my MSVC application with a static runtime?" to centralize selection of the MSVC runtime for a bunch of nested CMake projects (they are pulled in as Git submodules and added to the master project using CMake's find_package() directive).

So, I wrote this CMake macro:

macro(configure_msvc_runtime)
  if(MSVC)

    # Default to statically-linked runtime.
    if("${MSVC_RUNTIME}" STREQUAL "")
      set(MSVC_RUNTIME "static")
    endif()

    # Set compiler options.
    set(variables
      CMAKE_C_FLAGS_DEBUG
      CMAKE_C_FLAGS_MINSIZEREL
      CMAKE_C_FLAGS_RELEASE
      CMAKE_C_FLAGS_RELWITHDEBINFO
      CMAKE_CXX_FLAGS_DEBUG
      CMAKE_CXX_FLAGS_MINSIZEREL
      CMAKE_CXX_FLAGS_RELEASE
      CMAKE_CXX_FLAGS_RELWITHDEBINFO
    )
    if(${MSVC_RUNTIME} STREQUAL "static")
      message(STATUS
        "MSVC -> forcing use of statically-linked runtime."
      )
      foreach(variable ${variables})
        if(${variable} MATCHES "/MD")
          string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
        endif()
      endforeach()
    else()
      message(STATUS
        "MSVC -> forcing use of dynamically-linked runtime."
      )
      foreach(variable ${variables})
        if(${variable} MATCHES "/MT")
          string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
        endif()
      endforeach()
    endif()
  endif()
endmacro()

I call this macro at the beginning of my root CMakeLists.txt (before any add_library() or add_executable() call is made) and add a little bit of debugging prints:

configure_msvc_runtime()
set(variables
  CMAKE_C_FLAGS_DEBUG
  CMAKE_C_FLAGS_MINSIZEREL
  CMAKE_C_FLAGS_RELEASE
  CMAKE_C_FLAGS_RELWITHDEBINFO
  CMAKE_CXX_FLAGS_DEBUG
  CMAKE_CXX_FLAGS_MINSIZEREL
  CMAKE_CXX_FLAGS_RELEASE
  CMAKE_CXX_FLAGS_RELWITHDEBINFO
)
message(STATUS "Initial build flags:")
foreach(variable ${variables})
  message(STATUS "  '${variable}': ${${variable}}")
endforeach()
message(STATUS "")

Then, I run CMake to generate a Visual Studio solution like so:

cmake -G "Visual Studio 9 2008" ..\.. -DMSVC_RUNTIME=dynamic

and I get the following outputs:

-- MSVC -> forcing use of dynamically-linked runtime.
-- Initial build flags:
--   'CMAKE_C_FLAGS_DEBUG': /D_DEBUG /MDd /Zi  /Ob0 /Od /RTC1
--   'CMAKE_C_FLAGS_MINSIZEREL': /MD /O1 /Ob1 /D NDEBUG
--   'CMAKE_C_FLAGS_RELEASE': /MD /O2 /Ob2 /D NDEBUG
--   'CMAKE_C_FLAGS_RELWITHDEBINFO': /MD /Zi /O2 /Ob1 /D NDEBUG
--   'CMAKE_CXX_FLAGS_DEBUG': /D_DEBUG /MDd /Zi /Ob0 /Od /RTC1
--   'CMAKE_CXX_FLAGS_MINSIZEREL': /MD /O1 /Ob1 /D NDEBUG
--   'CMAKE_CXX_FLAGS_RELEASE': /MD /O2 /Ob2 /D NDEBUG
--   'CMAKE_CXX_FLAGS_RELWITHDEBINFO': /MD /Zi /O2 /Ob1 /D NDEBUG

Now, the thing is that when I start Visual Studio and examine the project properties under "C/C++, Code Generation", I see that the "Runtime Library" setting is not consistent with the options printed in the shell. Under the "Release", "MinSizeRel" and "RelWithDebInfo" configurations, I get the expected results ("Multi-threaded DLL /MD", but the "Debug" configuration still displays "Multi-threaded /MT").

Also, when I force use of the statically-linked runtime, I get similar results. If I run

cmake -G "Visual Studio 9 2008" ..\.. -DMSVC_RUNTIME=static

I get the following outputs:

-- MSVC -> forcing use of statically-linked runtime.
-- Initial build flags:
--   'CMAKE_C_FLAGS_DEBUG': /D_DEBUG /MTd /Zi  /Ob0 /Od /RTC1
--   'CMAKE_C_FLAGS_MINSIZEREL': /MT /O1 /Ob1 /D NDEBUG
--   'CMAKE_C_FLAGS_RELEASE': /MT /O2 /Ob2 /D NDEBUG
--   'CMAKE_C_FLAGS_RELWITHDEBINFO': /MT /Zi /O2 /Ob1 /D NDEBUG
--   'CMAKE_CXX_FLAGS_DEBUG': /D_DEBUG /MTd /Zi /Ob0 /Od /RTC1
--   'CMAKE_CXX_FLAGS_MINSIZEREL': /MT /O1 /Ob1 /D NDEBUG
--   'CMAKE_CXX_FLAGS_RELEASE': /MT /O2 /Ob2 /D NDEBUG
--   'CMAKE_CXX_FLAGS_RELWITHDEBINFO': /MT /Zi /O2 /Ob1 /D NDEBUG

And yet all configurations produce the "Multi-threaded /MT" value for the "Runtime Library" setting.

What am I doing wrong, or if this is a bug in CMake (2.8.7) or something?


For what it's worth, if I generate Visual Studio 2010 project files, I get a different value for the "Debug" configuration, but still not the one I selected.

In all cases, the setting appears in regular font for the "Debug" configuration whereas it appears in bolded font for the other configurations, hinting that those are overrides. Moreover,if I open the XML project files, I find that the "Debug" configuration has no setting for the "RuntimeLibrary" attribute of the "Tool" element with the "Name=VCCLCompilerTool" attribute. All other configurations have an explicit setting.

Rarefy answered 11/4, 2012 at 19:58 Comment(0)
R
13

It seems all the while I was working on this, I forgot to remove the bad CMake configuration I'm trying to replace.

Further down the build script, I had left this little bugger:

set(CMAKE_CXX_FLAGS_DEBUG
  "/DWIN32 /D_WINDOWS /EHsc /WX /wd4355 /wd4251 /wd4250 /wd4996"
  CACHE STRING "Debug compiler flags" FORCE
)

Basically, I was overriding the results of by configure_msvc_runtime() macro with build flags that did not set the MSVC runtime.

Rarefy answered 12/4, 2012 at 4:37 Comment(1)
You really saved me with your solution to force static runtime libraries!Germ
F
19

This functionality will be improved with the release of cmake-3.15.

It should be a matter of setting CMAKE_MSVC_RUNTIME_LIBRARY, for example (from docs) to set "multi-threaded statically-linked runtime library with or without debug information depending on the configuration":

set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
Fatso answered 26/6, 2019 at 15:18 Comment(2)
Not sure about this! I have cmake version 3.17.3! And the way you described does not work!Epos
though the approach with setting CMAKE_MSVC_RUNTIME_LIBRARY works, VS2019 (and probably other generators) produce the warning like D9025: overriding '/MDd' with '/MTd' (for Debug config) and D9025: overriding '/MD' with '/MT' (for Release config)Munday
R
13

It seems all the while I was working on this, I forgot to remove the bad CMake configuration I'm trying to replace.

Further down the build script, I had left this little bugger:

set(CMAKE_CXX_FLAGS_DEBUG
  "/DWIN32 /D_WINDOWS /EHsc /WX /wd4355 /wd4251 /wd4250 /wd4996"
  CACHE STRING "Debug compiler flags" FORCE
)

Basically, I was overriding the results of by configure_msvc_runtime() macro with build flags that did not set the MSVC runtime.

Rarefy answered 12/4, 2012 at 4:37 Comment(1)
You really saved me with your solution to force static runtime libraries!Germ
D
9

Here is a solution that I came up with, which should work for CMake versions before and after 3.15.

# This logic needs to be considered before project()
set(_change_MSVC_flags FALSE)
if(WIN32)
  if(CMAKE_VERSION VERSION_LESS 3.15.0)
    set(_change_MSVC_flags TRUE)
  else()
    # Set MSVC runtime to MultiThreaded (/MT)
    cmake_policy(SET CMP0091 NEW)
    set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
  endif()
endif()

project(MyProj ....)

if(_change_MSVC_flags)
  # Modify compile flags to change MSVC runtime from /MD to /MT
  set(_re_match "([\\/\\-]M)D")
  set(_re_replace "\\1T")
  string(REGEX REPLACE ${_re_match} ${_re_replace}
    CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
  string(REGEX REPLACE ${_re_match} ${_re_replace}
    CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
  string(REGEX REPLACE ${_re_match} ${_re_replace}
    CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
  string(REGEX REPLACE ${_re_match} ${_re_replace}
    CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}")
  string(REGEX REPLACE ${_re_match} ${_re_replace}
    CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
endif()

If other languages are used (i.e. C), then these would also need to be added too.

Dyandyana answered 4/5, 2020 at 23:27 Comment(1)
1. You could use a loop to process all variables (including C language). 2. The solution does not work for me because there is no such flag in my case: "CMAKE_CXX_FLAGS: /DWIN32 /D_WINDOWS /GR /EHsc"Underproduction
F
6

I took your code and generalized it to work for every existing configuration and not just for Debug/Release/RelWithDebInfo/MinSizeRel.

Also I made it to work with gcc too - check it out here

Forwardlooking answered 10/2, 2016 at 23:54 Comment(2)
is it possible to specify (or override if it's specified already) static or dynamic CRT linking from command line? Just to avoid modifying 3rd-party libs build scriptsFronde
@AndyT you could make a proxy CMakeLists.txt which add_subdirectoryies the third party lib - and you can set the runtime there - but if the third party lib sets the runtime on it's own then it should have an option for that...Forwardlooking

© 2022 - 2024 — McMap. All rights reserved.