Introduction

Visual Studio をつかった C++ の開発で一番遭遇率が高いのが

xxxxx.lib(alloc.cpp.obj) : error LNK2038: ‘RuntimeLibrary’ の不一致が検出されました。値 ‘MT_StaticRelease’ が MD_DynamicRelease の値 ‘main.obj’ と一致しません。

のようなエラー。

要するに、「main.obj (を生成したプロジェクト) が /MD でビルドされていて、/MT でビルドされている xxxx.lib と RuntimeLibrary が一致していないよ」ということ。
これ、いつも思うけど、どっちがどっちの設定になっているのか側凄い分かりにくい。げんなりする。

それはともかく、/MT とか /MD は下記の意味。本当に今更過ぎる。

  • /MD
    • MSVCRT.lib を .obj ファイルに挿入。実行時に MSVCRTXXX.dll が必要。
  • /MDd
    • MSVCRTD.lib を .obj ファイルに挿入。実行時に MSVCRTXXXd.dll が必要。
  • /MT
    • LIBCMT.lib を .obj ファイルに挿入。静的リンクされる。
  • /MTd
    • LIBCMTD.lib を .obj ファイルに挿入。静的リンクされる。

なんで、こんな当たり前なことを確認しているかというと、CMake を使う際、これらの設定を CMakeLists.txt で指定していたのだが、いつも使っていた方法が動かなくて時間を無為にしたため。
無論、Visual Studio ならプロパティを設定すればよいが、CMake を使っている以上、手動で毎回変更するのはありえない。

というわけで、もう二度と迷わないようにちゃんと調べた。

How to resolve?

世の中に流れている情報を見ると、下記みたいな記述を使って対処している事例があまりにも多い。

1
2
3
4
set(CMAKE_CXX_FLAGS_RELEASE "/MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MT")
set(CMAKE_CXX_FLAGS_MINSIZEREL "/MT")
set(CMAKE_CXX_FLAGS_DEBUG "/MTd")

かく言う自分も、ずっとこれを使っていたのだが、何故だか知らないがこれが動かない。
Visual Studio のバージョンを古くしてもダメ。
思いつくのは CMake のバージョンだが今更戻すわけにもいかず。
というか、set によって、CMAKE_CXX_FLAGS_XXX を変更するのは非推奨なので、何が起こっても文句は言えないのだが。

ということで、正しく動くのは下記の二通り

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cmake_minimum_required(VERSION 3.0.0)

set(PROJ_NAME Demo)
project(${PROJ_NAME} VERSION 1.0.0)

if(MSVC)
add_compile_options(
$<$<CONFIG:>:/MT>
$<$<CONFIG:Debug>:/MTd>
$<$<CONFIG:RelWithDebInfo>:/MT>
$<$<CONFIG:MinSizeRel>:/MT>
$<$<CONFIG:Release>:/MT>
)
endif()

add_executable(${PROJ_NAME} ${PROJECT_SOURCE_DIR}/main.cpp)

または

1
2
3
4
5
6
7
8
9
10
cmake_minimum_required(VERSION 3.15.0)

set(PROJ_NAME Demo)
project(${PROJ_NAME} VERSION 1.0.0)

add_executable(${PROJ_NAME} ${PROJECT_SOURCE_DIR}/main.cpp)

set_property(TARGET ${PROJ_NAME} PROPERTY
MSVC_RUNTIME_LIBRARY MultiThreaded$<$<CONFIG:Debug>:Debug>
)

add_executable の前後のどちらかに記述するべきなのか迷うのもあるが、後者の MSVC_RUNTIME_LIBRARY は CMake の 3.15.0 以降が必要な点に注意。