VS: Add CMAKE_GENERATOR_PLATFORM field to control Windows SDK selection

Add a `version=` field to explicitly control the SDK version selection
without relying on `CMAKE_SYSTEM_VERSION`.

Fixes: #16713
This commit is contained in:
Brad King
2023-04-03 10:11:49 -04:00
parent f0a67b6291
commit 2f3d945f83
22 changed files with 248 additions and 15 deletions

View File

@@ -273,7 +273,7 @@ supported out of the box. Other versions may require one to set
Cross Compiling for Windows 10 Universal Applications
-----------------------------------------------------
A toolchain file to configure a Visual Studio generator for a
A toolchain file to configure :ref:`Visual Studio Generators` for a
Windows 10 Universal Application may look like this:
.. code-block:: cmake
@@ -283,9 +283,10 @@ Windows 10 Universal Application may look like this:
A Windows 10 Universal Application targets both Windows Store and
Windows Phone. Specify the :variable:`CMAKE_SYSTEM_VERSION` variable
to be ``10.0`` to build with the latest available Windows 10 SDK.
Specify a more specific version (e.g. ``10.0.10240.0`` for RTM)
to build with the corresponding SDK.
to be ``10.0`` or higher.
CMake selects a Windows SDK as described by documentation of the
:variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` variable.
Cross Compiling for Windows Phone
---------------------------------

View File

@@ -0,0 +1,7 @@
vs-sdk-selection
----------------
* The :ref:`Visual Studio Generators` for VS 2015 and above learned to
select the Windows SDK version explicitly using a ``version=`` field
in the :variable:`CMAKE_GENERATOR_PLATFORM` variable.
See :ref:`Visual Studio Platform Selection`.

View File

@@ -26,6 +26,8 @@ Platform specification is supported only on specific generators:
See native build system documentation for allowed platform names.
.. _`Visual Studio Platform Selection`:
Visual Studio Platform Selection
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -42,4 +44,24 @@ name is provided in the :variable:`CMAKE_VS_PLATFORM_NAME` variable.
The ``key=value`` pairs form a comma-separated list of options to
specify generator-specific details of the platform selection.
There are no supported pairs: this syntax is reserved for future use.
Supported pairs are:
``version=<version>``
.. versionadded:: 3.27
Specify the Windows SDK version to use. This is supported by VS 2015 and
above when targeting Windows 10.0+ or Windows Store. CMake will set the
:variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` variable to the
selected SDK version.
The ``<version>`` may be one of:
``10.0.<build>.<increment>``
Specify the exact 4-component SDK version, e.g., ``10.0.19041.0``.
The specified version of the SDK must be installed. It may not exceed
the value of :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM`,
if that variable is set.
If the ``version`` field is not specified, CMake selects a version as
described in the :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION`
variable documentation.

View File

@@ -5,11 +5,19 @@ CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION
Visual Studio Windows Target Platform Version.
When targeting Windows 10 and above Visual Studio 2015 and above support
specification of a target Windows version to select a corresponding SDK.
The :variable:`CMAKE_SYSTEM_VERSION` variable may be set to specify a
version. Otherwise CMake computes a default version based on the Windows
SDK versions available. The chosen Windows target version number is provided
When targeting Windows 10 and above, :ref:`Visual Studio Generators` for
VS 2015 and above support specification of a Windows SDK version:
* If :variable:`CMAKE_GENERATOR_PLATFORM` specifies a ``version=`` field,
as documented by :ref:`Visual Studio Platform Selection`, that SDK
version is selected.
* Otherwise, if :variable:`CMAKE_SYSTEM_VERSION` is set to an available
SDK version, that version is selected.
* Otherwise, CMake uses the latest Windows SDK version available.
The chosen Windows target version number is provided
in ``CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION``. If no Windows 10 SDK
is available this value will be empty.

View File

@@ -10,5 +10,5 @@ be set to a false value (e.g. ``OFF``, ``FALSE``, or ``0``) or the SDK version
to use as the maximum (e.g. ``10.0.14393.0``). If unset, the default depends
on which version of Visual Studio is targeted by the current generator.
This can be used in conjunction with :variable:`CMAKE_SYSTEM_VERSION`, which
CMake uses to select :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION`.
This can be used to exclude Windows SDK versions from consideration for
:variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION`.

View File

@@ -531,6 +531,9 @@ bool cmGlobalVisualStudio10Generator::InitializePlatform(cmMakefile* mf)
if (!this->InitializePlatformWindows(mf)) {
return false;
}
} else if (!this->SystemName.empty() &&
!this->VerifyNoGeneratorPlatformVersion(mf)) {
return false;
}
return this->cmGlobalVisualStudio8Generator::InitializePlatform(mf);
}
@@ -540,6 +543,12 @@ bool cmGlobalVisualStudio10Generator::InitializePlatformWindows(cmMakefile*)
return true;
}
bool cmGlobalVisualStudio10Generator::VerifyNoGeneratorPlatformVersion(
cmMakefile*, cm::optional<std::string>) const
{
return true;
}
bool cmGlobalVisualStudio10Generator::SelectWindowsPhoneToolset(
std::string& toolset) const
{

View File

@@ -185,6 +185,8 @@ protected:
bool InitializePlatform(cmMakefile* mf) override;
virtual bool InitializePlatformWindows(cmMakefile* mf);
virtual bool VerifyNoGeneratorPlatformVersion(
cmMakefile* mf, cm::optional<std::string> reason = cm::nullopt) const;
virtual bool ProcessGeneratorToolsetField(std::string const& key,
std::string const& value);

View File

@@ -142,7 +142,31 @@ bool cmGlobalVisualStudio14Generator::InitializePlatformWindows(cmMakefile* mf)
if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
return this->SelectWindows10SDK(mf);
}
return true;
return this->VerifyNoGeneratorPlatformVersion(mf);
}
bool cmGlobalVisualStudio14Generator::VerifyNoGeneratorPlatformVersion(
cmMakefile* mf, cm::optional<std::string> reason) const
{
if (!this->GeneratorPlatformVersion) {
return true;
}
std::ostringstream e;
/* clang-format off */
e <<
"Generator\n"
" " << this->GetName() << "\n"
"given platform specification containing a\n"
" version=" << *this->GeneratorPlatformVersion << "\n"
"field. The version field is not supported when targeting\n"
" " << this->SystemName << " " << this->SystemVersion << "\n"
;
/* clang-format on */
if (reason) {
e << *reason << ".";
}
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
return false;
}
bool cmGlobalVisualStudio14Generator::InitializeWindowsStore(cmMakefile* mf)
@@ -170,12 +194,42 @@ bool cmGlobalVisualStudio14Generator::InitializeAndroid(cmMakefile*)
return true;
}
bool cmGlobalVisualStudio14Generator::ProcessGeneratorPlatformField(
std::string const& key, std::string const& value)
{
if (key == "version") {
this->GeneratorPlatformVersion = value;
return true;
}
return false;
}
bool cmGlobalVisualStudio14Generator::SelectWindows10SDK(cmMakefile* mf)
{
if (this->GeneratorPlatformVersion &&
this->GeneratorPlatformVersion->empty()) {
mf->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Generator\n ", this->GetName(),
"\ngiven platform specification with empty\n version=\n"
"field."));
return false;
}
// Find the default version of the Windows 10 SDK.
std::string const version = this->GetWindows10SDKVersion(mf);
if (version.empty()) {
if (this->GeneratorPlatformVersion) {
mf->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Generator\n ", this->GetName(),
"\ngiven platform specification with\n version=",
*this->GeneratorPlatformVersion,
"\nfield, but no Windows SDK with that version was found."));
return false;
}
if (this->SystemName == "WindowsStore") {
mf->IssueMessage(
MessageType::FATAL_ERROR,
@@ -359,7 +413,20 @@ std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion(
// Sort the results to make sure we select the most recent one.
std::sort(sdks.begin(), sdks.end(), cmSystemTools::VersionCompareGreater);
// Look for a SDK exactly matching the requested target version.
// Look for a SDK exactly matching the requested version, if any.
if (this->GeneratorPlatformVersion) {
for (std::string const& i : sdks) {
if (cmSystemTools::VersionCompareEqual(
i, *this->GeneratorPlatformVersion)) {
return i;
}
}
// An exact version was requested but not found.
// Our caller will issue the error message.
return std::string();
}
// Look for a SDK exactly matching the target Windows version.
for (std::string const& i : sdks) {
if (cmSystemTools::VersionCompareEqual(i, this->SystemVersion)) {
return i;

View File

@@ -7,6 +7,8 @@
#include <memory>
#include <string>
#include <cm/optional>
#include "cmGlobalVisualStudio12Generator.h"
class cmGlobalGeneratorFactory;
@@ -39,6 +41,12 @@ protected:
bool IsWindowsStoreToolsetInstalled() const;
bool InitializePlatformWindows(cmMakefile* mf) override;
bool VerifyNoGeneratorPlatformVersion(
cmMakefile* mf,
cm::optional<std::string> reason = cm::nullopt) const override;
bool ProcessGeneratorPlatformField(std::string const& key,
std::string const& value) override;
// Used to adjust the max-SDK-version calculation to accommodate user
// configuration.
@@ -62,4 +70,6 @@ protected:
private:
class Factory;
friend class Factory;
cm::optional<std::string> GeneratorPlatformVersion;
};

View File

@@ -895,7 +895,8 @@ bool cmGlobalVisualStudioVersionedGenerator::InitializePlatformWindows(
if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 &&
!cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) {
this->SetWindowsTargetPlatformVersion("8.1", mf);
return true;
return this->VerifyNoGeneratorPlatformVersion(
mf, "with the Windows 8.1 SDK installed");
}
return cmGlobalVisualStudio14Generator::InitializePlatformWindows(mf);
}

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,11 @@
^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
Generator
Visual Studio [^
]+
given platform specification with empty
version=
field\.$

View File

@@ -0,0 +1 @@
message(FATAL_ERROR "This should not be reached!")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,11 @@
^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
Generator
Visual Studio [^
]+
given platform specification with
version=1\.2\.3\.4
field, but no Windows SDK with that version was found\.$

View File

@@ -0,0 +1 @@
message(FATAL_ERROR "This should not be reached!")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,19 @@
^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
Generator
Visual Studio [^
]+
given platform specification (containing a
version=8\.1
field\. The version field is not supported when targeting
Windows 8\.1(
with the Windows 8\.1 SDK installed\.)?|with
version=8\.1
field, but no Windows SDK with that version was found\.)$

View File

@@ -0,0 +1 @@
message(FATAL_ERROR "This should not be reached!")

View File

@@ -32,4 +32,53 @@ if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio (1[4567])( 20[0-9][0-9])?$")
run_cmake(BadFieldNoComma)
set(RunCMake_GENERATOR_PLATFORM "Test Platform,unknown=")
run_cmake(BadFieldUnknown)
set(RunCMake_GENERATOR_PLATFORM "version=")
run_cmake(BadVersionEmpty)
set(RunCMake_GENERATOR_PLATFORM "version=1.2.3.4")
run_cmake(BadVersionMissing)
set(RunCMake_GENERATOR_PLATFORM "version=8.1")
run_cmake_with_options(BadVersionPlatform -DCMAKE_SYSTEM_VERSION=8.1)
set(kits "")
cmake_host_system_information(RESULT kitsRoot10
QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Microsoft/Windows Kits/Installed Roots"
VALUE "KitsRoot10"
VIEW 64_32
ERROR_VARIABLE kitsRoot10Error
)
if(NOT kitsRoot10Error AND IS_DIRECTORY "${kitsRoot10}/include")
cmake_path(SET kitsInclude "${kitsRoot10}/include")
file(GLOB kits RELATIVE "${kitsInclude}" "${kitsInclude}/*/um/windows.h")
list(TRANSFORM kits REPLACE "/.*" "")
endif()
if(kits)
message(STATUS "Available Kits: ${kits}")
if(RunCMake_GENERATOR MATCHES "^Visual Studio 14 ")
set(kitMax 10.0.14393.0)
else()
set(kitMax "")
endif()
if(kitMax)
set(kitsIn "${kits}")
set(kits "")
foreach(kit IN LISTS kitsIn)
if(kit VERSION_LESS_EQUAL "${kitMax}")
list(APPEND kits "${kit}")
else()
message(STATUS "Excluding Kit ${kit} > ${kitMax}")
endif()
endforeach()
endif()
elseif(NOT RunCMake_GENERATOR MATCHES "^Visual Studio 14 ")
message(FATAL_ERROR "Could not find any Windows SDKs to drive test cases.")
endif()
if(kits)
foreach(expect_version IN LISTS kits)
set(RunCMake_GENERATOR_PLATFORM "version=${expect_version}")
set(RunCMake_TEST_VARIANT_DESCRIPTION "-${expect_version}")
run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=10.0)
unset(RunCMake_GENERATOR_PLATFORM)
endforeach()
endif()
endif()

View File

@@ -0,0 +1,9 @@
if(actual_stdout MATCHES "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION='([^']+)'")
set(actual_version "${CMAKE_MATCH_1}")
if(NOT "${actual_version}" STREQUAL "${expect_version}")
set(RunCMake_TEST_FAILED "Actual SDK version '${actual_version}' did not match expected '${expect_version}'")
return()
endif()
else()
set(RunCMake_TEST_FAILED "No CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION found in output.")
endif()

View File

@@ -0,0 +1 @@
message(STATUS "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION='${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}'")