protocolbuffers/protobuf

Protobuf overrides user-specified CMAKE_MSVC_RUNTIME_LIBRARY on MSVC, making it impossible to force /MD in Debug

Fermée

#23 173 ouverte le 16 août 2025

 (2 commentaires) (0 réaction) (0 personne assignée)C++ (16 128 forks)batch import
cmakehelp wanted

Métriques du dépôt

Stars
 (71 223 étoiles)
Métriques de merge PR
 (Merge moyen 2j 11h) (185 PRs mergées en 30 j)

Description

When building Protobuf as a static library on Windows with MSVC via CMake, Protobuf’s CMakeLists.txt unconditionally sets CMAKE_MSVC_RUNTIME_LIBRARY (e.g., to MultiThreaded$<$CONFIG:Debug:Debug>DLL), overriding a value explicitly set by the parent project. This makes it impossible to enforce /MD in all configurations (including Debug) from the parent, and leads to runtime-library mismatches (e.g., LNK2038 MDd_DynamicDebug vs MD_DynamicRelease) when the consumer must use /MD even for Debug.

Some SDKs/libraries ship only /MD-compatible binaries (even for Debug). Currently I need to force /MD globally. If Protobuf overrides CMAKE_MSVC_RUNTIME_LIBRARY, Debug builds of libprotobuf/Abseil end up with /MDd, causing link errors.

Expected behavior If the parent project explicitly sets CMAKE_MSVC_RUNTIME_LIBRARY (e.g., via cache/toolchain or before add_subdirectory/FetchContent/CPM), Protobuf should not change it. Protobuf should only set a default when the variable is unset.

Actual behavior Protobuf sets CMAKE_MSVC_RUNTIME_LIBRARY inside its CMakeLists.txt based on protobuf_MSVC_STATIC_RUNTIME and configuration type, overriding the parent’s explicit value and leading to /MDd in Debug for Protobuf/Abseil targets.

cmake_minimum_required(VERSION 3.20)
project(repro LANGUAGES CXX)
cmake_policy(SET CMP0091 NEW)

# Parent wants /MD in ALL configs (no /MDd)
set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL CACHE STRING "" FORCE)

include(FetchContent)
set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "" FORCE)

FetchContent_Declare(protobuf
  GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git
  GIT_TAG v32.0.0   # example
)
FetchContent_MakeAvailable(protobuf)

add_executable(app main.cpp)
target_link_libraries(app PRIVATE protobuf::libprotobuf)

What version of protobuf and what language are you using? Version: main Language: C++

What operating system (Linux, Windows, ...) and version? Windows 11

What runtime / compiler are you using (e.g., python version or gcc version) Visual Studio 2022

Workarounds tried:

  • Toolchain file + CMAKE_PROJECT_INCLUDE_BEFORE to re-force /MD after Protobuf config
  • Patching Protobuf’s CMakeLists.txt locally to avoid resetting CMAKE_MSVC_RUNTIME_LIBRARY

Both work, but are ugly and require plumbing or maintaining a patch.

One solution could be to only set CMAKE_MSVC_RUNTIME if it hasn't been set before:

if(NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
  if (protobuf_MSVC_STATIC_RUNTIME)
    set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$<CONFIG:Debug>:Debug>)
  else()
    set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$<CONFIG:Debug>:Debug>DLL)
  endif()
endif()

Another solution could be to introduce a variable similar to protobuf_MSVC_STATIC_RUNTIME but instead of just switching between static and dynamic it could be used as a user defined overwrite for CMAKE_MSVC_RUNTIME_LIBRARY.

I know this might be an edge case, but it would really help me with project maintenance.

Thanks.

Guide contributeur