VS: Add [CMAKE_]VS_USE_DEBUG_LIBRARIES options to control UseDebugLibraries

This indicates to MSBuild which configurations are considered debug
configurations.  This is useful for reference both by humans and tools.

Issue: #25327
This commit is contained in:
Brad King
2024-02-16 12:15:23 -05:00
parent 67de5b7b82
commit b814641444
18 changed files with 201 additions and 0 deletions

View File

@@ -445,6 +445,7 @@ Properties on Targets
/prop_tgt/VS_SDK_REFERENCES
/prop_tgt/VS_SOLUTION_DEPLOY
/prop_tgt/VS_SOURCE_SETTINGS_tool
/prop_tgt/VS_USE_DEBUG_LIBRARIES
/prop_tgt/VS_USER_PROPS
/prop_tgt/VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION
/prop_tgt/VS_WINRT_COMPONENT

View File

@@ -137,6 +137,7 @@ Variables that Provide Information
/variable/CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER
/variable/CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION
/variable/CMAKE_VS_TARGET_FRAMEWORK_VERSION
/variable/CMAKE_VS_USE_DEBUG_LIBRARIES
/variable/CMAKE_VS_VERSION_BUILD_NUMBER
/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION
/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION

View File

@@ -0,0 +1,18 @@
Indicate to :ref:`Visual Studio Generators` what configurations are considered
debug configurations. This controls the ``UseDebugLibraries`` setting in
each configuration of a ``.vcxproj`` file.
The "Use Debug Libraries" setting in Visual Studio projects, despite its
specific-sounding name, is a general-purpose indicator of what configurations
are considered debug configurations. In standalone projects, this may affect
MSBuild's default selection of MSVC runtime library, optimization flags,
runtime checks, and similar settings. In CMake projects those settings are
typically generated explicitly based on the project's specification, e.g.,
the MSVC runtime library is controlled by |MSVC_RUNTIME_LIBRARY|. However,
the ``UseDebugLibraries`` indicator is useful for reference by both humans
and tools, and may also affect the behavior of platform-specific SDKs.
Set |VS_USE_DEBUG_LIBRARIES| to a true or false value to indicate whether
each configuration is considered a debug configuration. The value may also
be the empty string (``""``) in which case no ``UseDebugLibraries`` will be
added explicitly by CMake, and MSBuild will use its default value, ``false``.

View File

@@ -0,0 +1,27 @@
VS_USE_DEBUG_LIBRARIES
----------------------
.. versionadded:: 3.30
.. |VS_USE_DEBUG_LIBRARIES| replace:: ``VS_USE_DEBUG_LIBRARIES``
.. |MSVC_RUNTIME_LIBRARY| replace:: :prop_tgt:`MSVC_RUNTIME_LIBRARY`
.. include:: VS_USE_DEBUG_LIBRARIES-PURPOSE.txt
Use :manual:`generator expressions <cmake-generator-expressions(7)>`
for per-configuration specification. For example, the code:
.. code-block:: cmake
add_executable(foo foo.c)
set_property(TARGET foo PROPERTY
VS_USE_DEBUG_LIBRARIES "$<CONFIG:Debug,Custom>")
indicates that target ``foo`` considers its "Debug" and "Custom"
configurations to be debug configurations, and its other configurations
to be non-debug configurations.
The property is initialized from the value of the
:variable:`CMAKE_VS_USE_DEBUG_LIBRARIES` variable, if it is set.
If the property is not set, then CMake does not generate any
``UseDebugLibraries`` indicator.

View File

@@ -0,0 +1,6 @@
vs-UseDebugLibraries
--------------------
* The :variable:`CMAKE_VS_USE_DEBUG_LIBRARIES` variable and corresponding
:prop_tgt:`VS_USE_DEBUG_LIBRARIES` target property were added to explicitly
control ``UseDebugLibraries`` indicators in ``.vcxproj`` files.

View File

@@ -0,0 +1,28 @@
CMAKE_VS_USE_DEBUG_LIBRARIES
----------------------------
.. versionadded:: 3.30
.. |VS_USE_DEBUG_LIBRARIES| replace:: ``CMAKE_VS_USE_DEBUG_LIBRARIES``
.. |MSVC_RUNTIME_LIBRARY| replace:: :variable:`CMAKE_MSVC_RUNTIME_LIBRARY`
.. include:: ../prop_tgt/VS_USE_DEBUG_LIBRARIES-PURPOSE.txt
Use :manual:`generator expressions <cmake-generator-expressions(7)>`
for per-configuration specification. For example, the code:
.. code-block:: cmake
set(CMAKE_VS_USE_DEBUG_LIBRARIES "$<CONFIG:Debug,Custom>")
indicates that all following targets consider their "Debug" and "Custom"
configurations to be debug configurations, and their other configurations
to be non-debug configurations.
This variable is used to initialize the :prop_tgt:`VS_USE_DEBUG_LIBRARIES`
property on all targets as they are created. It is also propagated by
calls to the :command:`try_compile` command into its test project.
If this variable is not set then the :prop_tgt:`VS_USE_DEBUG_LIBRARIES`
property will not be set automatically. If that property is not set
then CMake does not generate any ``UseDebugLibraries`` indicator.

View File

@@ -1151,6 +1151,7 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s);
vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s);
vars.emplace("CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS"_s);
vars.emplace("CMAKE_VS_USE_DEBUG_LIBRARIES"_s);
if (cmValue varListStr = this->Makefile->GetDefinition(
kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {

View File

@@ -404,6 +404,7 @@ TargetProperty const StaticTargetProperties[] = {
{ "VS_DEBUGGER_COMMAND_ARGUMENTS"_s, IC::ExecutableTarget },
{ "VS_DEBUGGER_ENVIRONMENT"_s, IC::ExecutableTarget },
{ "VS_DEBUGGER_WORKING_DIRECTORY"_s, IC::ExecutableTarget },
{ "VS_USE_DEBUG_LIBRARIES"_s, IC::NonImportedTarget },
// ---- OpenWatcom
{ "WATCOM_RUNTIME_LIBRARY"_s, IC::CanCompileSources },
// -- Language

View File

@@ -1622,6 +1622,25 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValuesCommon(
} else if (const char* toolset = gg->GetPlatformToolset()) {
e1.Element("PlatformToolset", toolset);
}
cm::optional<bool> maybeUseDebugLibraries;
if (cmValue useDebugLibrariesProp =
this->GeneratorTarget->GetProperty("VS_USE_DEBUG_LIBRARIES")) {
// The project explicitly specified a value for this target.
// An empty string suppresses generation of the setting altogether.
std::string const useDebugLibraries = cmGeneratorExpression::Evaluate(
*useDebugLibrariesProp, this->LocalGenerator, config);
if (!useDebugLibraries.empty()) {
maybeUseDebugLibraries = cmIsOn(useDebugLibraries);
}
}
if (maybeUseDebugLibraries) {
if (*maybeUseDebugLibraries) {
e1.Element("UseDebugLibraries", "true");
} else {
e1.Element("UseDebugLibraries", "false");
}
}
}
//----------------------------------------------------------------------------

View File

@@ -731,6 +731,7 @@ if("${CMAKE_GENERATOR}" MATCHES "Visual Studio ([^9]|9[0-9])")
-DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
-DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}
)
add_RunCMake_test(VS10ProjectUseDebugLibraries)
if( vs12 AND wince )
add_RunCMake_test( VS10ProjectWinCE "-DRunCMake_GENERATOR_PLATFORM=${wince_sdk}")
endif()

View File

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

View File

@@ -0,0 +1,8 @@
include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake)
UseDebugLibraries_check(default "" "")
UseDebugLibraries_check(defaultCLR "" "")
UseDebugLibraries_check(defaultUtil "" "")
UseDebugLibraries_check(defaultRTL "" "")
UseDebugLibraries_check(ALL_BUILD "" "")
UseDebugLibraries_check(ZERO_CHECK "" "")

View File

@@ -0,0 +1,10 @@
set(CMAKE_CONFIGURATION_TYPES Debug Release)
enable_language(CXX)
# Test several generator code paths covering different target types.
add_library(default empty.cxx)
add_library(defaultCLR empty.cxx)
set_property(TARGET defaultCLR PROPERTY COMMON_LANGUAGE_RUNTIME "")
add_library(defaultRTL empty.cxx)
set_property(TARGET defaultRTL PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
add_custom_target(defaultUtil)

View File

@@ -0,0 +1,10 @@
include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake)
UseDebugLibraries_check(empty "" "")
UseDebugLibraries_check(emptyCLR "" "")
UseDebugLibraries_check(emptyUtil "" "")
UseDebugLibraries_check(genex "true" "false")
UseDebugLibraries_check(genexCLR "true" "false")
UseDebugLibraries_check(genexUtil "true" "false")
UseDebugLibraries_check(ALL_BUILD "false" "false")
UseDebugLibraries_check(ZERO_CHECK "false" "false")

View File

@@ -0,0 +1,20 @@
set(CMAKE_CONFIGURATION_TYPES Debug Release)
enable_language(CXX)
# An empty string suppresses generation of the setting.
set(CMAKE_VS_USE_DEBUG_LIBRARIES "")
add_library(empty empty.cxx)
add_library(emptyCLR empty.cxx)
set_property(TARGET emptyCLR PROPERTY COMMON_LANGUAGE_RUNTIME "")
add_custom_target(emptyUtil)
# A generator expression can encode per-config values.
set(CMAKE_VS_USE_DEBUG_LIBRARIES "$<CONFIG:Debug>")
add_library(genex empty.cxx)
add_library(genexCLR empty.cxx)
set_property(TARGET genexCLR PROPERTY COMMON_LANGUAGE_RUNTIME "")
add_custom_target(genexUtil)
# The last setting in the top-level directcory affects
# the builtin targets like ALL_BUILD and ZERO_CHECK.
set(CMAKE_VS_USE_DEBUG_LIBRARIES 0)

View File

@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.29)
include(RunCMake)
run_cmake(Default)
run_cmake(Explicit)

View File

@@ -0,0 +1,42 @@
cmake_policy(SET CMP0140 NEW)
function(UseDebugLibraries_check tgt udl_expect_debug udl_expect_release)
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_udl_debug 0)
set(have_udl_release 0)
set(inConfig "")
file(STRINGS "${vcProjectFile}" lines)
foreach(line IN LISTS lines)
if(line MATCHES [[^ *<PropertyGroup Condition="'\$\(Configuration\)\|\$\(Platform\)'=='([^"|]+)\|[^"]+" Label="Configuration">.*$]])
string(TOLOWER "${CMAKE_MATCH_1}" inConfig)
elseif(inConfig)
if(line MATCHES "^ *</PropertyGroup>.*$")
set(inConfig "")
elseif(line MATCHES "^ *<UseDebugLibraries>([^<>]+)</UseDebugLibraries>")
set(udl_actual "${CMAKE_MATCH_1}")
set(have_udl_${inConfig} 1)
if (NOT "${udl_expect_${inConfig}}" STREQUAL "")
if(NOT "${udl_actual}" STREQUAL "${udl_expect_${inConfig}}")
string(APPEND RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has ${inConfig} UseDebugLibraries '${udl_actual}', not '${udl_expect_${inConfig}}'.\n")
endif()
else()
string(APPEND RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has ${inConfig} UseDebugLibraries '${udl_actual}', but should not have one.\n")
endif()
unset(udl_actual)
endif()
endif()
endforeach()
if(NOT have_udl_debug AND NOT "${udl_expect_debug}" STREQUAL "")
string(APPEND RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a debug UseDebugLibraries field, but should have one.\n")
endif()
if(NOT have_udl_release AND NOT "${udl_expect_release}" STREQUAL "")
string(APPEND RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a release UseDebugLibraries field, but should have one.\n")
endif()
return(PROPAGATE RunCMake_TEST_FAILED)
endfunction()