Browse Source

Merge topic 'vs-toolset-version'

30c835428f VS: Accept and translate '-T version=' values with three components
58a50a3a0a VS: Fix '-T version=14.28' under VS 16.9
09f59da7f0 cmGlobalVisualStudioVersionedGenerator: Clarify local variable name

Acked-by: Kitware Robot <[email protected]>
Merge-request: !5903
Brad King 4 years ago
parent
commit
3d661c6c3a

+ 8 - 0
Help/release/3.19.rst

@@ -432,3 +432,11 @@ Changes made since CMake 3.19.0 include the following.
   ``CMakePresets.json`` or ``CMakeUserPresets.json`` files.
   This was mistakenly allowed by the implementation in CMake 3.19.0 through
   CMake 3.19.5, and was not documented.
+
+3.19.7
+------
+
+* With :ref:`Visual Studio Generators` for VS 2017 and higher, the
+  :variable:`CMAKE_GENERATOR_TOOLSET` field ``version=`` now accepts
+  three-component MSVC toolset versions such as ``14.28.29910``.
+  See the :variable:`CMAKE_VS_PLATFORM_TOOLSET_VERSION` variable.

+ 20 - 0
Help/variable/CMAKE_VS_PLATFORM_TOOLSET_VERSION.rst

@@ -11,3 +11,23 @@ may be specified by a field in :variable:`CMAKE_GENERATOR_TOOLSET` of
 the form ``version=14.11``. If none is specified CMake will choose a default
 toolset. The value may be empty if no minor version was selected and the
 default is used.
+
+If the value is not empty, it is the version number that MSBuild uses in
+its ``Microsoft.VCToolsVersion.*.props`` file names.
+
+.. versionadded:: 3.19.7
+
+   VS 16.9's toolset may also be specified as ``14.28.16.9`` because
+   VS 16.10 uses the file name ``Microsoft.VCToolsVersion.14.28.16.9.props``.
+
+Three-Component MSVC Toolset Versions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.19.7
+
+The ``version=`` field may be given a three-component toolset version
+such as ``14.28.29910``, and CMake will convert it to the name used by
+MSBuild ``Microsoft.VCToolsVersion.*.props`` files.  This is useful
+to distinguish between VS 16.8's ``14.28.29333`` toolset and VS 16.9's
+``14.28.29910`` toolset.  It also matches ``vcvarsall``'s ``-vcvars_ver=``
+behavior.

+ 32 - 41
Source/cmGlobalVisualStudio10Generator.cxx

@@ -3,6 +3,7 @@
 #include "cmGlobalVisualStudio10Generator.h"
 
 #include <algorithm>
+#include <utility>
 
 #include <cm/memory>
 
@@ -302,16 +303,16 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset(
     // If a specific minor version of the toolset was requested, verify that it
     // is compatible to the major version and that is exists on disk.
     // If not clear the value.
-    std::string version = this->GeneratorToolsetVersion;
+    std::string versionToolset = this->GeneratorToolsetVersion;
     cmsys::RegularExpression regex("[0-9][0-9]\\.[0-9][0-9]");
-    if (regex.find(version)) {
-      version = "v" + version.erase(2, 1);
+    if (regex.find(versionToolset)) {
+      versionToolset = "v" + versionToolset.erase(2, 1);
     } else {
       // Version not recognized. Clear it.
-      version.clear();
+      versionToolset.clear();
     }
 
-    if (!cmHasPrefix(version, this->GetPlatformToolsetString())) {
+    if (!cmHasPrefix(versionToolset, this->GetPlatformToolsetString())) {
       std::ostringstream e;
       /* clang-format off */
       e <<
@@ -329,15 +330,20 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset(
       this->GeneratorToolsetVersion.clear();
     }
 
-    bool const isDefaultToolset =
-      this->IsDefaultToolset(this->GeneratorToolsetVersion);
-    if (isDefaultToolset) {
-      // If the given version is the default toolset, remove the setting
-      this->GeneratorToolsetVersion.clear();
-    } else {
-      std::string const toolsetPath = this->GetAuxiliaryToolset();
-      if (!toolsetPath.empty() && !cmSystemTools::FileExists(toolsetPath)) {
-
+    std::string auxProps;
+    switch (this->FindAuxToolset(this->GeneratorToolsetVersion, auxProps)) {
+      case AuxToolset::None:
+        this->GeneratorToolsetVersionProps = {};
+        break;
+      case AuxToolset::Default:
+        // The given version is the default toolset.  Remove the setting.
+        this->GeneratorToolsetVersion.clear();
+        this->GeneratorToolsetVersionProps = {};
+        break;
+      case AuxToolset::PropsExist:
+        this->GeneratorToolsetVersionProps = std::move(auxProps);
+        break;
+      case AuxToolset::PropsMissing: {
         std::ostringstream e;
         /* clang-format off */
         e <<
@@ -347,22 +353,24 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset(
           "  " << this->GetPlatformToolsetString() << ",version=" <<
           this->GeneratorToolsetVersion << "\n"
           "does not seem to be installed at\n" <<
-          "  " << toolsetPath;
+          "  " << auxProps;
         ;
         /* clang-format on */
         mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
 
         // Clear the configured tool-set
         this->GeneratorToolsetVersion.clear();
-      }
+        this->GeneratorToolsetVersionProps = {};
+      } break;
     }
   }
 
   if (const char* toolset = this->GetPlatformToolset()) {
     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET", toolset);
   }
-  if (const char* version = this->GetPlatformToolsetVersion()) {
-    mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_VERSION", version);
+  if (!this->GeneratorToolsetVersion.empty()) {
+    mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_VERSION",
+                      this->GeneratorToolsetVersion);
   }
   if (const char* hostArch = this->GetPlatformToolsetHostArchitecture()) {
     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE", hostArch);
@@ -719,23 +727,10 @@ std::string const& cmGlobalVisualStudio10Generator::GetPlatformToolsetString()
   return empty;
 }
 
-const char* cmGlobalVisualStudio10Generator::GetPlatformToolsetVersion() const
-{
-  std::string const& version = this->GetPlatformToolsetVersionString();
-  if (version.empty()) {
-    return nullptr;
-  }
-  return version.c_str();
-}
-
 std::string const&
-cmGlobalVisualStudio10Generator::GetPlatformToolsetVersionString() const
+cmGlobalVisualStudio10Generator::GetPlatformToolsetVersionProps() const
 {
-  if (!this->GeneratorToolsetVersion.empty()) {
-    return this->GeneratorToolsetVersion;
-  }
-  static std::string const empty;
-  return empty;
+  return this->GeneratorToolsetVersionProps;
 }
 
 const char*
@@ -792,15 +787,11 @@ cmGlobalVisualStudio10Generator::GetPlatformToolsetCudaCustomDirString() const
   return this->GeneratorToolsetCudaCustomDir;
 }
 
-bool cmGlobalVisualStudio10Generator::IsDefaultToolset(
-  const std::string&) const
+cmGlobalVisualStudio10Generator::AuxToolset
+cmGlobalVisualStudio10Generator::FindAuxToolset(std::string&,
+                                                std::string&) const
 {
-  return true;
-}
-
-std::string cmGlobalVisualStudio10Generator::GetAuxiliaryToolset() const
-{
-  return {};
+  return AuxToolset::None;
 }
 
 bool cmGlobalVisualStudio10Generator::FindMakeProgram(cmMakefile* mf)

+ 15 - 7
Source/cmGlobalVisualStudio10Generator.h

@@ -63,9 +63,8 @@ public:
   const char* GetPlatformToolset() const;
   std::string const& GetPlatformToolsetString() const;
 
-  /** The toolset version.  */
-  const char* GetPlatformToolsetVersion() const;
-  std::string const& GetPlatformToolsetVersionString() const;
+  /** The toolset version props file, if any.  */
+  std::string const& GetPlatformToolsetVersionProps() const;
 
   /** The toolset host architecture name (e.g. x64 for 64-bit host tools).  */
   const char* GetPlatformToolsetHostArchitecture() const;
@@ -122,9 +121,6 @@ public:
   std::string Encoding() override;
   const char* GetToolsVersion() const;
 
-  virtual bool IsDefaultToolset(const std::string& version) const;
-  virtual std::string GetAuxiliaryToolset() const;
-
   bool GetSupportsUnityBuilds() const { return this->SupportsUnityBuilds; }
 
   bool FindMakeProgram(cmMakefile* mf) override;
@@ -170,6 +166,16 @@ protected:
   virtual bool SelectWindowsPhoneToolset(std::string& toolset) const;
   virtual bool SelectWindowsStoreToolset(std::string& toolset) const;
 
+  enum class AuxToolset
+  {
+    None,
+    Default,
+    PropsExist,
+    PropsMissing
+  };
+  virtual AuxToolset FindAuxToolset(std::string& version,
+                                    std::string& props) const;
+
   std::string const& GetMSBuildCommand();
 
   cmIDEFlagTable const* LoadFlagTable(std::string const& toolSpecificName,
@@ -177,7 +183,7 @@ protected:
                                       std::string const& table) const;
 
   std::string GeneratorToolset;
-  std::string GeneratorToolsetVersion;
+  std::string GeneratorToolsetVersionProps;
   std::string GeneratorToolsetHostArchitecture;
   std::string GeneratorToolsetCustomVCTargetsDir;
   std::string GeneratorToolsetCuda;
@@ -230,6 +236,8 @@ private:
   std::string FindDevEnvCommand() override;
   std::string GetVSMakeProgram() override { return this->GetMSBuildCommand(); }
 
+  std::string GeneratorToolsetVersion;
+
   bool PlatformToolsetNeedsDebugEnum;
 
   bool ParseGeneratorToolset(std::string const& ts, cmMakefile* mf);

+ 87 - 40
Source/cmGlobalVisualStudioVersionedGenerator.cxx

@@ -2,6 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalVisualStudioVersionedGenerator.h"
 
+#include <cmext/string_view>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/Glob.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmDocumentationEntry.h"
 #include "cmLocalVisualStudio10Generator.h"
@@ -392,27 +397,6 @@ bool cmGlobalVisualStudioVersionedGenerator::GetVSInstanceVersion(
   return vsSetupAPIHelper.GetVSInstanceVersion(vsInstanceVersion);
 }
 
-bool cmGlobalVisualStudioVersionedGenerator::IsDefaultToolset(
-  const std::string& version) const
-{
-  if (version.empty()) {
-    return true;
-  }
-
-  std::string vcToolsetVersion;
-  if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {
-
-    cmsys::RegularExpression regex("[0-9][0-9]\\.[0-9]+");
-    if (regex.find(version) && regex.find(vcToolsetVersion)) {
-      const auto majorMinorEnd = vcToolsetVersion.find('.', 3);
-      const auto majorMinor = vcToolsetVersion.substr(0, majorMinorEnd);
-      return version == majorMinor;
-    }
-  }
-
-  return false;
-}
-
 bool cmGlobalVisualStudioVersionedGenerator::IsStdOutEncodingSupported() const
 {
   // Supported from Visual Studio 16.7 Preview 3.
@@ -447,29 +431,92 @@ cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision()
   return "";
 }
 
-std::string cmGlobalVisualStudioVersionedGenerator::GetAuxiliaryToolset() const
+cmGlobalVisualStudioVersionedGenerator::AuxToolset
+cmGlobalVisualStudioVersionedGenerator::FindAuxToolset(
+  std::string& version, std::string& props) const
 {
-  const char* version = this->GetPlatformToolsetVersion();
-  if (version) {
-    std::string instancePath;
-    GetVSInstance(instancePath);
-    std::string toolsetDir = instancePath + "/VC/Auxiliary/Build";
-    char sep = '/';
-    if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) {
-      std::string toolsetDot =
-        cmStrCat(toolsetDir, '.', version, "/Microsoft.VCToolsVersion.",
-                 version, ".props");
-      if (cmSystemTools::PathExists(toolsetDot)) {
-        sep = '.';
+  if (version.empty()) {
+    return AuxToolset::None;
+  }
+
+  std::string instancePath;
+  this->GetVSInstance(instancePath);
+  cmSystemTools::ConvertToUnixSlashes(instancePath);
+
+  // Translate three-component format accepted by "vcvarsall -vcvars_ver=".
+  cmsys::RegularExpression threeComponent(
+    "^([0-9]+\\.[0-9]+)\\.[0-9][0-9][0-9][0-9][0-9]$");
+  if (threeComponent.find(version)) {
+    // Load "VC/Auxiliary/Build/*/Microsoft.VCToolsVersion.*.txt" files
+    // with two matching components to check their three-component version.
+    std::string const& twoComponent = threeComponent.match(1);
+    std::string pattern =
+      cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent,
+               "*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s);
+    cmsys::Glob glob;
+    glob.SetRecurseThroughSymlinks(false);
+    if (glob.FindFiles(pattern)) {
+      for (std::string const& txt : glob.GetFiles()) {
+        std::string ver;
+        cmsys::ifstream fin(txt.c_str());
+        if (fin && std::getline(fin, ver)) {
+          // Strip trailing whitespace.
+          ver = ver.substr(0, ver.find_first_not_of("0123456789."));
+          // If the three-component version matches, translate it to
+          // that used by the "Microsoft.VCToolsVersion.*.txt" file name.
+          if (ver == version) {
+            cmsys::RegularExpression extractVersion(
+              "VCToolsVersion\\.([0-9.]+)\\.txt$");
+            if (extractVersion.find(txt)) {
+              version = extractVersion.match(1);
+              break;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) {
+    props = cmStrCat(instancePath, "/VC/Auxiliary/Build."_s, version,
+                     "/Microsoft.VCToolsVersion."_s, version, ".props"_s);
+    if (cmSystemTools::PathExists(props)) {
+      return AuxToolset::PropsExist;
+    }
+  }
+  props = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, version,
+                   "/Microsoft.VCToolsVersion."_s, version, ".props"_s);
+  if (cmSystemTools::PathExists(props)) {
+    return AuxToolset::PropsExist;
+  }
+
+  // Accept the toolset version that is default in the current VS version
+  // by matching the name later VS versions will use for the SxS props files.
+  std::string vcToolsetVersion;
+  if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {
+    // Accept an exact-match (three-component version).
+    if (version == vcToolsetVersion) {
+      return AuxToolset::Default;
+    }
+
+    // Accept known SxS props file names using four version components
+    // in VS versions later than the current.
+    if (version == "14.28.16.9" && vcToolsetVersion == "14.28.29910") {
+      return AuxToolset::Default;
+    }
+
+    // The first two components of the default toolset version typically
+    // match the name used by later VS versions for the SxS props files.
+    cmsys::RegularExpression twoComponent("^([0-9]+\\.[0-9]+)");
+    if (twoComponent.find(version)) {
+      std::string const versionPrefix = cmStrCat(twoComponent.match(1), '.');
+      if (cmHasPrefix(vcToolsetVersion, versionPrefix)) {
+        return AuxToolset::Default;
       }
     }
-    std::string toolsetPath =
-      cmStrCat(toolsetDir, sep, version, "/Microsoft.VCToolsVersion.", version,
-               ".props");
-    cmSystemTools::ConvertToUnixSlashes(toolsetPath);
-    return toolsetPath;
   }
-  return {};
+
+  return AuxToolset::PropsMissing;
 }
 
 bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)

+ 2 - 2
Source/cmGlobalVisualStudioVersionedGenerator.h

@@ -30,8 +30,8 @@ public:
 
   bool GetVSInstanceVersion(unsigned long long& vsInstanceVersion) const;
 
-  bool IsDefaultToolset(const std::string& version) const override;
-  std::string GetAuxiliaryToolset() const override;
+  AuxToolset FindAuxToolset(std::string& version,
+                            std::string& props) const override;
 
   bool IsStdOutEncodingSupported() const override;
 

+ 6 - 6
Source/cmVisualStudio10TargetGenerator.cxx

@@ -623,14 +623,14 @@ void cmVisualStudio10TargetGenerator::Generate()
     }
 
     switch (this->ProjectType) {
-      case vcxproj:
-        if (this->GlobalGenerator->GetPlatformToolsetVersion()) {
-          Elem(e0, "Import")
-            .Attribute("Project",
-                       this->GlobalGenerator->GetAuxiliaryToolset());
+      case vcxproj: {
+        std::string const& props =
+          this->GlobalGenerator->GetPlatformToolsetVersionProps();
+        if (!props.empty()) {
+          Elem(e0, "Import").Attribute("Project", props);
         }
         Elem(e0, "Import").Attribute("Project", VS10_CXX_DEFAULT_PROPS);
-        break;
+      } break;
       case csproj:
         Elem(e0, "Import")
           .Attribute("Project", VS10_CSharp_DEFAULT_PROPS)