MSVC: Add abstraction for runtime library selection

Replace our hard-coded defaults for `/MD` and `/MDd` with a first-class
abstraction to select the runtime library from an enumeration of logical
names.  We've long hesitated to do this because the idea of "runtime
library selection" touches on related concepts on several platforms.
Avoid that scope creep by simply defining an abstraction that applies
only when targeting the MSVC ABI on Windows.

Removing the old default flags requires a policy because existing
projects may rely on string processing to edit them and choose a runtime
library under the old behavior.  Add policy CMP0091 to provide
compatibility.

Fixes: #19108
This commit is contained in:
Brad King 2019-04-10 13:38:41 -04:00
parent f621e7fa5d
commit fb3370b6a1
37 changed files with 478 additions and 13 deletions

View File

@ -135,6 +135,7 @@ default values:
* :variable:`CMAKE_ENABLE_EXPORTS`
* :variable:`CMAKE_LINK_SEARCH_START_STATIC`
* :variable:`CMAKE_LINK_SEARCH_END_STATIC`
* :variable:`CMAKE_MSVC_RUNTIME_LIBRARY`
* :variable:`CMAKE_POSITION_INDEPENDENT_CODE`
If :policy:`CMP0056` is set to ``NEW``, then

View File

@ -57,6 +57,7 @@ Policies Introduced by CMake 3.15
.. toctree::
:maxdepth: 1
CMP0091: MSVC runtime library flags are selected by an abstraction. </policy/CMP0091>
CMP0090: export(PACKAGE) does not populate package registry by default. </policy/CMP0090>
CMP0089: Compiler id for IBM Clang-based XL compilers is now XLClang. </policy/CMP0089>

View File

@ -280,6 +280,7 @@ Properties on Targets
/prop_tgt/MACOSX_RPATH
/prop_tgt/MANUALLY_ADDED_DEPENDENCIES
/prop_tgt/MAP_IMPORTED_CONFIG_CONFIG
/prop_tgt/MSVC_RUNTIME_LIBRARY
/prop_tgt/NAME
/prop_tgt/NO_SONAME
/prop_tgt/NO_SYSTEM_FROM_IMPORTED

View File

@ -386,6 +386,7 @@ Variables that Control the Build
/variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG_INIT
/variable/CMAKE_MODULE_LINKER_FLAGS_INIT
/variable/CMAKE_MSVCIDE_RUN_PATH
/variable/CMAKE_MSVC_RUNTIME_LIBRARY
/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX
/variable/CMAKE_NO_BUILTIN_CHRPATH
/variable/CMAKE_NO_SYSTEM_FROM_IMPORTED

47
Help/policy/CMP0091.rst Normal file
View File

@ -0,0 +1,47 @@
CMP0091
-------
MSVC runtime library flags are selected by an abstraction.
Compilers targeting the MSVC ABI have flags to select the MSVC runtime library.
Runtime library selection typically varies with build configuration because
there is a separate runtime library for Debug builds.
In CMake 3.14 and below, MSVC runtime library selection flags are added to
the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache entries by CMake
automatically. This allows users to edit their cache entries to adjust the
flags. However, the presence of such default flags is problematic for
projects that want to choose a different runtime library programmatically.
In particular, it requires string editing of the
:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variables with knowledge of the
CMake builtin defaults so they can be replaced.
CMake 3.15 and above prefer to leave the MSVC runtime library selection flags
out of the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` values and instead
offer a first-class abstraction. The :variable:`CMAKE_MSVC_RUNTIME_LIBRARY`
variable and :prop_tgt:`MSVC_RUNTIME_LIBRARY` target property may be set to
select the MSVC runtime library.
This policy provides compatibility with projects that have not been updated
to be aware of the abstraction. The policy setting takes effect as of the
first :command:`project` or :command:`enable_language` command that enables
a language whose compiler targets the MSVC ABI.
.. note::
Once the policy has taken effect at the top of a project, that choice
must be used throughout the tree. In projects that have nested projects
in subdirectories, be sure to convert everything together.
The ``OLD`` behavior for this policy is to place MSVC runtime library
flags in the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache
entries and ignore the :variable:`CMAKE_MSVC_RUNTIME_LIBRARY` abstraction.
The ``NEW`` behavior for this policy is to *not* place MSVC runtime
library flags in the default cache entries and use the abstraction instead.
This policy was introduced in CMake version 3.15. Use the
:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
Unlike many policies, CMake version |release| does *not* warn
when this policy is not set and simply uses ``OLD`` behavior.
.. include:: DEPRECATED.txt

View File

@ -0,0 +1,15 @@
``MultiThreaded``
Compile with ``-MT`` or equivalent flag(s) to use a multi-threaded
statically-linked runtime library.
``MultiThreadedDLL``
Compile with ``-MD`` or equivalent flag(s) to use a multi-threaded
dynamically-linked runtime library.
``MultiThreadedDebug``
Compile with ``-MTd`` or equivalent flag(s) to use a multi-threaded
statically-linked runtime library.
``MultiThreadedDebugDLL``
Compile with ``-MDd`` or equivalent flag(s) to use a multi-threaded
dynamically-linked runtime library.
The value is ignored on non-MSVC compilers but an unsupported value will
be rejected as an error when using a compiler targeting the MSVC ABI.

View File

@ -0,0 +1,26 @@
MSVC_RUNTIME_LIBRARY
--------------------
Select the MSVC runtime library for use by compilers targeting the MSVC ABI.
The allowed values are:
.. include:: MSVC_RUNTIME_LIBRARY-VALUES.txt
Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
support per-configuration specification. For example, the code:
.. code-block:: cmake
add_executable(foo foo.c)
set_property(TARGET foo PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
selects for the target ``foo`` a multi-threaded statically-linked runtime
library with or without debug information depending on the configuration.
.. note::
This property has effect only when policy :policy:`CMP0091` is set to ``NEW``
prior to the first :command:`project` or :command:`enable_language` command
that enables a language using a compiler targeting the MSVC ABI.

View File

@ -0,0 +1,7 @@
msvc-runtime-library
--------------------
* The :variable:`CMAKE_MSVC_RUNTIME_LIBRARY` variable and
:prop_tgt:`MSVC_RUNTIME_LIBRARY` target property were introduced to
select the runtime library used by compilers targeting the MSVC ABI.
See policy :policy:`CMP0091`.

View File

@ -0,0 +1,27 @@
CMAKE_MSVC_RUNTIME_LIBRARY
--------------------------
Select the MSVC runtime library for use by compilers targeting the MSVC ABI.
This variable is used to initialize the :prop_tgt:`MSVC_RUNTIME_LIBRARY`
property on all targets as they are created. It is also propagated by
calls to the :command:`try_compile` command into the test project.
The allowed values are:
.. include:: ../prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt
Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
support per-configuration specification. For example, the code:
.. code-block:: cmake
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
selects for all following targets a multi-threaded statically-linked runtime
library with or without debug information depending on the configuration.
.. note::
This variable has effect only when policy :policy:`CMP0091` is set to ``NEW``
prior to the first :command:`project` or :command:`enable_language` command
that enables a language using a compiler targeting the MSVC ABI.

View File

@ -4,8 +4,34 @@ set(_COMPILE_Fortran " /fpp")
set(CMAKE_Fortran_MODDIR_FLAG "-module:")
set(CMAKE_Fortran_STANDARD_LIBRARIES_INIT "user32.lib")
__windows_compiler_intel(Fortran)
string(APPEND CMAKE_Fortran_FLAGS_INIT " /W1 /nologo /fpp /libs:dll /threads")
string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " /Od /debug:full /dbglibs")
if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
set(_LIBSDLL "")
set(_DBGLIBS "")
set(_THREADS "")
else()
set(_LIBSDLL " /libs:dll")
set(_DBGLIBS " /dbglibs")
set(_THREADS " /threads")
endif()
string(APPEND CMAKE_Fortran_FLAGS_INIT " /W1 /nologo /fpp${_LIBSDLL}${_THREADS}")
string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " /Od /debug:full${_DBGLIBS}")
string(APPEND CMAKE_Fortran_FLAGS_MINSIZEREL_INIT " /O1 /DNDEBUG")
string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " /O2 /DNDEBUG")
string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " /O2 /debug:full /DNDEBUG")
unset(_LIBSDLL)
unset(_DBGLIBS)
unset(_THREADS)
set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded -threads -libs:static)
set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL -threads -libs:dll)
set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug -threads -libs:static -dbglibs)
set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -threads -libs:dll -dbglibs)
# Intel Fortran for Windows supports single-threaded RTL but it is
# not implemented by the Visual Studio integration.
if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreaded -libs:static)
set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreadedDLL -libs:dll)
set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreadedDebug -libs:static -dbglibs)
set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreadedDebugDLL -libs:dll -dbglibs)
endif()

View File

@ -298,6 +298,14 @@ endforeach()
string(APPEND CMAKE_STATIC_LINKER_FLAGS_INIT " ${_MACHINE_ARCH_FLAG}")
unset(_MACHINE_ARCH_FLAG)
cmake_policy(GET CMP0091 __WINDOWS_MSVC_CMP0091)
if(__WINDOWS_MSVC_CMP0091 STREQUAL "NEW")
set(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else()
set(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT "")
endif()
unset(__WINDOWS_MSVC_CMP0091)
macro(__windows_compiler_msvc lang)
if(NOT MSVC_VERSION LESS 1400)
# for 2005 make sure the manifest is put in the dll with mt
@ -351,21 +359,35 @@ macro(__windows_compiler_msvc lang)
if("x${lang}" STREQUAL "xC" OR
"x${lang}" STREQUAL "xCXX")
if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
set(_MDd "")
set(_MD "")
else()
set(_MDd " /MDd")
set(_MD " /MD")
endif()
if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "v[0-9]+_clang_.*")
# note: MSVC 14 2015 Update 1 sets -fno-ms-compatibility by default, but this does not allow one to compile many projects
# that include MS's own headers. CMake itself is affected project too.
string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} -fms-extensions -fms-compatibility -D_WINDOWS -Wall${_FLAGS_${lang}}")
string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " /MDd -gline-tables-only -fno-inline -O0 ${_RTC1}")
string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " /MD -O2 -DNDEBUG")
string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " /MD -gline-tables-only -O2 -fno-inline -DNDEBUG")
string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " /MD -DNDEBUG") # TODO: Add '-Os' once VS generator maps it properly for Clang
string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} -gline-tables-only -fno-inline -O0 ${_RTC1}")
string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} -O2 -DNDEBUG")
string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} -gline-tables-only -O2 -fno-inline -DNDEBUG")
string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} -DNDEBUG") # TODO: Add '-Os' once VS generator maps it properly for Clang
else()
string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} /D_WINDOWS /W3${_FLAGS_${lang}}")
string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " /MDd /Zi /Ob0 /Od ${_RTC1}")
string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " /MD /O2 /Ob2 /DNDEBUG")
string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " /MD /Zi /O2 /Ob1 /DNDEBUG")
string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " /MD /O1 /Ob1 /DNDEBUG")
string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} /Zi /Ob0 /Od ${_RTC1}")
string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} /O2 /Ob2 /DNDEBUG")
string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} /Zi /O2 /Ob1 /DNDEBUG")
string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} /O1 /Ob1 /DNDEBUG")
endif()
unset(_MDd)
unset(_MD)
set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded -MT)
set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL -MD)
set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug -MTd)
set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -MDd)
endif()
set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON)
set(CMAKE_NINJA_DEPTYPE_${lang} msvc)

View File

@ -20,6 +20,7 @@
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmVersion.h"
#include "cm_static_string_view.hxx"
#include "cmake.h"
static std::string const kCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN =
@ -42,6 +43,8 @@ static std::string const kCMAKE_LINK_SEARCH_END_STATIC =
"CMAKE_LINK_SEARCH_END_STATIC";
static std::string const kCMAKE_LINK_SEARCH_START_STATIC =
"CMAKE_LINK_SEARCH_START_STATIC";
static std::string const kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT =
"CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT";
static std::string const kCMAKE_OSX_ARCHITECTURES = "CMAKE_OSX_ARCHITECTURES";
static std::string const kCMAKE_OSX_DEPLOYMENT_TARGET =
"CMAKE_OSX_DEPLOYMENT_TARGET";
@ -500,6 +503,13 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def);
}
/* Set MSVC runtime library policy to match our selection. */
if (const char* msvcRuntimeLibraryDefault =
this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) {
fprintf(fout, "cmake_policy(SET CMP0091 %s)\n",
*msvcRuntimeLibraryDefault ? "NEW" : "OLD");
}
std::string projectLangs;
for (std::string const& li : testLangs) {
projectLangs += " " + li;
@ -660,6 +670,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
vars.insert(kCMAKE_SYSROOT_COMPILE);
vars.insert(kCMAKE_SYSROOT_LINK);
vars.insert(kCMAKE_WARN_DEPRECATED);
vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
if (const char* varListStr = this->Makefile->GetDefinition(
kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {

View File

@ -6,6 +6,7 @@
#include "cmComputeLinkInformation.h"
#include "cmCustomCommandGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionEvaluationFile.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
@ -1519,8 +1520,40 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
flagsVar += "_FLAGS";
this->AddConfigVariableFlags(flags, flagsVar, config);
// Placeholder for possible future per-target flags.
static_cast<void>(target);
// Add MSVC runtime library flags. This is activated by the presence
// of a default selection whether or not it is overridden by a property.
const char* msvcRuntimeLibraryDefault =
this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT");
if (msvcRuntimeLibraryDefault && *msvcRuntimeLibraryDefault) {
const char* msvcRuntimeLibraryValue =
target->GetProperty("MSVC_RUNTIME_LIBRARY");
if (!msvcRuntimeLibraryValue) {
msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault;
}
cmGeneratorExpression ge;
std::unique_ptr<cmCompiledGeneratorExpression> cge =
ge.Parse(msvcRuntimeLibraryValue);
std::string const msvcRuntimeLibrary =
cge->Evaluate(this, config, false, target);
if (!msvcRuntimeLibrary.empty()) {
if (const char* msvcRuntimeLibraryOptions =
this->Makefile->GetDefinition(
"CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" +
msvcRuntimeLibrary)) {
this->AppendCompileOptions(flags, msvcRuntimeLibraryOptions);
} else if ((this->Makefile->GetSafeDefinition(
"CMAKE_" + lang + "_COMPILER_ID") == "MSVC" ||
this->Makefile->GetSafeDefinition(
"CMAKE_" + lang + "_SIMULATE_ID") == "MSVC") &&
!cmSystemTools::GetErrorOccuredFlag()) {
// The compiler uses the MSVC ABI so it needs a known runtime library.
this->IssueMessage(MessageType::FATAL_ERROR,
"MSVC_RUNTIME_LIBRARY value '" +
msvcRuntimeLibrary + "' not known for this " +
lang + " compiler.");
}
}
}
}
void cmLocalGenerator::AddLanguageFlagsForLinking(

View File

@ -267,7 +267,10 @@ class cmMakefile;
15, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0090, \
"export(PACKAGE) does not populate package registry by default.", 3, \
15, 0, cmPolicies::WARN)
15, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0091, \
"MSVC runtime library flags are selected by an abstraction.", 3, 15, \
0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \

View File

@ -304,6 +304,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
InitProperty("AUTORCC_OPTIONS", nullptr);
InitProperty("LINK_DEPENDS_NO_SHARED", nullptr);
InitProperty("LINK_INTERFACE_LIBRARIES", nullptr);
InitProperty("MSVC_RUNTIME_LIBRARY", nullptr);
InitProperty("WIN32_EXECUTABLE", nullptr);
InitProperty("MACOSX_BUNDLE", nullptr);
InitProperty("MACOSX_RPATH", nullptr);

View File

@ -1995,6 +1995,10 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
ADD_TEST_MACRO(PrecompiledHeader foo)
endif()
ADD_TEST_MACRO(MSVCRuntimeLibrary)
if(CMAKE_Fortran_COMPILER)
ADD_TEST_MACRO(MSVCRuntimeLibrary.Fortran)
endif()
endif()
if(MSVC OR
"${CMAKE_GENERATOR}" MATCHES "(MSYS|MinGW) Makefiles")

View File

@ -0,0 +1,49 @@
cmake_minimum_required(VERSION 3.14)
cmake_policy(SET CMP0091 NEW)
project(MSVCRuntimeLibrary)
function(verify_combinations threads lang src)
set(verify_tc_config_ Release)
set(verify_tc_config_Debug Debug)
set(verify_def_MultiThreaded -DVERIFY_MT)
set(verify_def_Debug -DVERIFY_DEBUG)
set(verify_def_DLL -DVERIFY_DLL)
foreach(dbg "" Debug)
foreach(dll "" DLL)
# Construct the name of this runtime library combination.
set(rtl "${threads}${dbg}${dll}")
# Test that try_compile builds with this RTL.
set(CMAKE_MSVC_RUNTIME_LIBRARY "${rtl}")
set(CMAKE_TRY_COMPILE_CONFIGURATION "${verify_tc_config_${dbg}}")
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
try_compile(${rtl}_COMPILES
${CMAKE_CURRENT_BINARY_DIR}/try_compile/${rtl}
${CMAKE_CURRENT_SOURCE_DIR}/${src}
COMPILE_DEFINITIONS ${verify_def_${threads}} ${verify_def_${dbg}} ${verify_def_${dll}}
OUTPUT_VARIABLE ${rtl}_OUTPUT
)
if(${rtl}_COMPILES)
message(STATUS "try_compile with ${rtl} worked")
else()
string(REPLACE "\n" "\n " ${rtl}_OUTPUT " ${${rtl}_OUTPUT}")
message(SEND_ERROR "try_compile with ${rtl} failed:\n${${rtl}_OUTPUT}")
endif()
# Test that targets build with this RTL.
set(CMAKE_MSVC_RUNTIME_LIBRARY "$<$<BOOL:$<TARGET_PROPERTY:BOOL_TRUE>>:${rtl}>$<$<BOOL:$<TARGET_PROPERTY:BOOL_FALSE>>:BadContent>")
add_library(${rtl}-${lang} ${src})
set_property(TARGET ${rtl}-${lang} PROPERTY BOOL_TRUE TRUE)
target_compile_definitions(${rtl}-${lang} PRIVATE ${verify_def_${threads}} ${verify_def_${dbg}} ${verify_def_${dll}})
endforeach()
endforeach()
endfunction()
function(verify lang src)
add_library(default-${lang} ${src})
target_compile_definitions(default-${lang} PRIVATE VERIFY_MT VERIFY_DLL "$<$<CONFIG:Debug>:VERIFY_DEBUG>")
verify_combinations(MultiThreaded ${lang} ${src})
endfunction()
verify(C verify.c)
verify(CXX verify.cxx)

View File

@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 3.14)
cmake_policy(SET CMP0091 NEW)
project(MSVCRuntimeLibraryFortran Fortran)
foreach(t MultiThreaded SingleThreaded)
foreach(dbg "" Debug)
foreach(dll "" DLL)
set(var "CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_${t}${dbg}${dll}")
# ifort does not actually define these, so inject them
string(REPLACE "-threads" "-threads;-D_MT" "${var}" "${${var}}")
string(REPLACE "-dbglibs" "-dbglibs;-D_DEBUG" "${var}" "${${var}}")
endforeach()
endforeach()
endforeach()
string(APPEND CMAKE_Fortran_FLAGS " -w")
function(verify_combinations threads lang src)
set(verify_tc_config_ Release)
set(verify_tc_config_Debug Debug)
set(verify_def_MultiThreaded -DVERIFY_MT)
set(verify_def_Debug -DVERIFY_DEBUG)
set(verify_def_DLL -DVERIFY_DLL)
foreach(dbg "" Debug)
foreach(dll "" DLL)
# Construct the name of this runtime library combination.
set(rtl "${threads}${dbg}${dll}")
# Test that targets build with this RTL.
set(CMAKE_MSVC_RUNTIME_LIBRARY "$<$<BOOL:$<TARGET_PROPERTY:BOOL_TRUE>>:${rtl}>$<$<BOOL:$<TARGET_PROPERTY:BOOL_FALSE>>:BadContent>")
add_library(${rtl}-${lang} ${src})
set_property(TARGET ${rtl}-${lang} PROPERTY BOOL_TRUE TRUE)
target_compile_definitions(${rtl}-${lang} PRIVATE ${verify_def_${threads}} ${verify_def_${dbg}} ${verify_def_${dll}})
endforeach()
endforeach()
endfunction()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
function(verify lang src)
add_library(default-${lang} ${src})
target_compile_definitions(default-${lang} PRIVATE VERIFY_MT VERIFY_DLL "$<$<CONFIG:Debug>:VERIFY_DEBUG>")
verify_combinations(MultiThreaded ${lang} ${src})
endfunction()
verify(Fortran verify.F90)
# Intel Fortran for Windows supports single-threaded RTL but it is
# not implemented by the Visual Studio integration.
if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
verify_combinations(SingleThreaded Fortran verify.F90)
endif()

View File

@ -0,0 +1 @@
#include "../verify.h"

View File

@ -0,0 +1 @@
#include "verify.h"

View File

@ -0,0 +1 @@
#include "verify.h"

View File

@ -0,0 +1,29 @@
#ifdef VERIFY_DEBUG
# ifndef _DEBUG
# error "_DEBUG not defined by debug runtime library selection"
# endif
#else
# ifdef _DEBUG
# error "_DEBUG defined by non-debug runtime library selection"
# endif
#endif
#ifdef VERIFY_DLL
# ifndef _DLL
# error "_DLL not defined by DLL runtime library selection"
# endif
#else
# ifdef _DLL
# error "_DLL defined by non-DLL runtime library selection"
# endif
#endif
#ifdef VERIFY_MT
# ifndef _MT
# error "_MT not defined by multi-threaded runtime library selection"
# endif
#else
# ifdef _MT
# error "_MT defined by single-threaded runtime library selection"
# endif
#endif

View File

@ -195,6 +195,9 @@ add_RunCMake_test(LinkStatic)
if(CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|XL|XLClang)$")
add_RunCMake_test(MetaCompileFeatures)
endif()
if(MSVC)
add_RunCMake_test(MSVCRuntimeLibrary)
endif()
add_RunCMake_test(ObjectLibrary)
add_RunCMake_test(ParseImplicitIncludeInfo)
if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,2 @@
^CMake Error in CMakeLists.txt:
MSVC_RUNTIME_LIBRARY value 'BogusValue' not known for this C compiler.$

View File

@ -0,0 +1,2 @@
cmake_policy(SET CMP0091 NEW)
include(CMP0091-common.cmake)

View File

@ -0,0 +1,2 @@
cmake_policy(SET CMP0091 OLD)
include(CMP0091-common.cmake)

View File

@ -0,0 +1,2 @@
include(CMP0091-common.cmake)

View File

@ -0,0 +1,37 @@
enable_language(C)
cmake_policy(GET CMP0091 cmp0091)
if(cmp0091 STREQUAL "NEW")
if(NOT CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
message(SEND_ERROR "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT not set under NEW behavior")
endif()
else()
if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
message(SEND_ERROR "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT is set under OLD behavior")
endif()
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if(CMAKE_C_FLAGS_DEBUG MATCHES "[/-]MDd( |$)")
set(have_MDd 1)
else()
set(have_MDd 0)
endif()
if(CMAKE_C_FLAGS_RELEASE MATCHES "[/-]MD( |$)")
set(have_MD 1)
else()
set(have_MD 0)
endif()
if(cmp0091 STREQUAL "NEW")
if(have_MDd OR have_MD)
message(SEND_ERROR "Have a -MD* flag under NEW behavior.")
endif()
else()
if(NOT (have_MDd AND have_MD))
message(SEND_ERROR "Do not have -MD* flags under OLD behavior.")
endif()
endif()
endif()
set(CMAKE_MSVC_RUNTIME_LIBRARY BogusValue)
add_library(foo empty.c)

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.14)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@ -0,0 +1,5 @@
include(RunCMake)
run_cmake(CMP0091-WARN)
run_cmake(CMP0091-OLD)
run_cmake(CMP0091-NEW)

View File

@ -2,6 +2,7 @@ include(RunCMake)
run_cmake(VsCSharpCompilerOpts)
run_cmake(ExplicitCMakeLists)
run_cmake(RuntimeLibrary)
run_cmake(SourceGroupCMakeLists)
run_cmake(VsConfigurationType)

View File

@ -0,0 +1,34 @@
macro(RuntimeLibrary_check tgt rtl_expect)
set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${tgt}.vcxproj")
if(NOT EXISTS "${vcProjectFile}")
set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not exist.")
return()
endif()
set(HAVE_Runtimelibrary 0)
file(STRINGS "${vcProjectFile}" lines)
foreach(line IN LISTS lines)
if(line MATCHES "^ *<RuntimeLibrary>([^<>]+)</RuntimeLibrary>")
set(rtl_actual "${CMAKE_MATCH_1}")
if(NOT "${rtl_actual}" STREQUAL "${rtl_expect}")
set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has RuntimeLibrary '${rtl_actual}', not '${rtl_expect}'.")
return()
endif()
set(HAVE_Runtimelibrary 1)
break()
endif()
endforeach()
if(NOT HAVE_Runtimelibrary)
set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a RuntimeLibrary field.")
return()
endif()
endmacro()
RuntimeLibrary_check(default-C MultiThreadedDebugDLL)
RuntimeLibrary_check(default-CXX MultiThreadedDebugDLL)
RuntimeLibrary_check(MTd-C MultiThreadedDebug)
RuntimeLibrary_check(MTd-CXX MultiThreadedDebug)
RuntimeLibrary_check(MT-C MultiThreaded)
RuntimeLibrary_check(MT-CXX MultiThreaded)

View File

@ -0,0 +1,16 @@
set(CMAKE_CONFIGURATION_TYPES Debug)
cmake_policy(SET CMP0091 NEW)
enable_language(C)
enable_language(CXX)
add_library(default-C empty.c)
add_library(default-CXX empty.cxx)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebug")
add_library(MTd-C empty.c)
add_library(MTd-CXX empty.cxx)
add_library(MT-C empty.c)
set_property(TARGET MT-C PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
add_library(MT-CXX empty.cxx)
set_property(TARGET MT-CXX PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")

View File

View File