浏览代码

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
Brad King 2 年之前
父节点
当前提交
2f3d945f83

+ 5 - 4
Help/manual/cmake-toolchains.7.rst

@@ -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
 ---------------------------------

+ 7 - 0
Help/release/dev/vs-sdk-selection.rst

@@ -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`.

+ 23 - 1
Help/variable/CMAKE_GENERATOR_PLATFORM.rst

@@ -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.

+ 13 - 5
Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst

@@ -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.
 

+ 2 - 2
Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst

@@ -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`.

+ 9 - 0
Source/cmGlobalVisualStudio10Generator.cxx

@@ -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
 {

+ 2 - 0
Source/cmGlobalVisualStudio10Generator.h

@@ -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);

+ 69 - 2
Source/cmGlobalVisualStudio14Generator.cxx

@@ -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;

+ 10 - 0
Source/cmGlobalVisualStudio14Generator.h

@@ -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;
 };

+ 2 - 1
Source/cmGlobalVisualStudioVersionedGenerator.cxx

@@ -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);
   }

+ 1 - 0
Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-result.txt

@@ -0,0 +1 @@
+1

+ 11 - 0
Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-stderr.txt

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

+ 1 - 0
Tests/RunCMake/GeneratorPlatform/BadVersionEmpty.cmake

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

+ 1 - 0
Tests/RunCMake/GeneratorPlatform/BadVersionMissing-result.txt

@@ -0,0 +1 @@
+1

+ 11 - 0
Tests/RunCMake/GeneratorPlatform/BadVersionMissing-stderr.txt

@@ -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\.$

+ 1 - 0
Tests/RunCMake/GeneratorPlatform/BadVersionMissing.cmake

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

+ 1 - 0
Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-result.txt

@@ -0,0 +1 @@
+1

+ 19 - 0
Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-stderr.txt

@@ -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\.)$

+ 1 - 0
Tests/RunCMake/GeneratorPlatform/BadVersionPlatform.cmake

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

+ 49 - 0
Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake

@@ -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()

+ 9 - 0
Tests/RunCMake/GeneratorPlatform/VersionExists-check.cmake

@@ -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()

+ 1 - 0
Tests/RunCMake/GeneratorPlatform/VersionExists.cmake

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