Bläddra i källkod

VS: Support version specification in CMAKE_GENERATOR_INSTANCE

Brad King 4 år sedan
förälder
incheckning
ec8d37b3b1

+ 6 - 1
Help/variable/CMAKE_GENERATOR_INSTANCE.rst

@@ -37,7 +37,12 @@ of the VS installation.
 
 The ``key=value`` pairs form a comma-separated list of options to
 specify details of the instance selection.
-There are no supported pairs: this syntax is reserved for future use.
+Supported pairs are:
+
+``version=<major>.<minor>.<MMMDD>.<BBB>``
+  .. versionadded:: 3.23
+
+  Specify the 4-component VS Build Version.
 
 If the value of ``CMAKE_GENERATOR_INSTANCE`` is not specified explicitly
 by the user or a toolchain file, CMake queries the Visual Studio Installer

+ 63 - 7
Source/cmGlobalVisualStudioVersionedGenerator.cxx

@@ -6,6 +6,7 @@
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
+#include "cmsys/RegularExpression.hxx"
 
 #include "cmAlgorithms.h"
 #include "cmDocumentationEntry.h"
@@ -109,6 +110,30 @@ static const char* VSVersionToToolset(
   return "";
 }
 
+static std::string VSVersionToMajorString(
+  cmGlobalVisualStudioGenerator::VSVersion v)
+{
+  switch (v) {
+    case cmGlobalVisualStudioGenerator::VS9:
+      return "9";
+    case cmGlobalVisualStudioGenerator::VS10:
+      return "10";
+    case cmGlobalVisualStudioGenerator::VS11:
+      return "11";
+    case cmGlobalVisualStudioGenerator::VS12:
+      return "12";
+    case cmGlobalVisualStudioGenerator::VS14:
+      return "14";
+    case cmGlobalVisualStudioGenerator::VS15:
+      return "15";
+    case cmGlobalVisualStudioGenerator::VS16:
+      return "16";
+    case cmGlobalVisualStudioGenerator::VS17:
+      return "17";
+  }
+  return "";
+}
+
 static const char* VSVersionToAndroidToolset(
   cmGlobalVisualStudioGenerator::VSVersion v)
 {
@@ -445,15 +470,21 @@ bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
     return false;
   }
 
-  if (!this->GeneratorInstance.empty()) {
-    if (!this->vsSetupAPIHelper.SetVSInstance(this->GeneratorInstance)) {
+  if (!this->GeneratorInstanceVersion.empty()) {
+    std::string const majorStr = VSVersionToMajorString(this->Version);
+    cmsys::RegularExpression versionRegex(
+      cmStrCat("^", majorStr, "\\.[0-9]+\\.[0-9]+\\.[0-9]+$"));
+    if (!versionRegex.find(this->GeneratorInstanceVersion)) {
       std::ostringstream e;
       /* clang-format off */
       e <<
         "Generator\n"
         "  " << this->GetName() << "\n"
-        "could not find specified instance of Visual Studio:\n"
-        "  " << i;
+        "given instance specification\n"
+        "  " << i << "\n"
+        "but the version field is not 4 integer components"
+        " starting in " << majorStr << "."
+        ;
       /* clang-format on */
       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
       return false;
@@ -461,7 +492,29 @@ bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
   }
 
   std::string vsInstance;
-  if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
+  if (!i.empty()) {
+    vsInstance = i;
+    if (!this->vsSetupAPIHelper.SetVSInstance(
+          this->GeneratorInstance, this->GeneratorInstanceVersion)) {
+      std::ostringstream e;
+      /* clang-format off */
+      e <<
+        "Generator\n"
+        "  " << this->GetName() << "\n"
+        "could not find specified instance of Visual Studio:\n"
+        "  " << i;
+      /* clang-format on */
+      if (!this->GeneratorInstance.empty() &&
+          this->GeneratorInstanceVersion.empty() &&
+          cmSystemTools::FileIsDirectory(this->GeneratorInstance)) {
+        e << "\n"
+             "The directory exists, but the instance is not known to the "
+             "Visual Studio Installer.";
+      }
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return false;
+    }
+  } else if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
     std::ostringstream e;
     /* clang-format off */
     e <<
@@ -493,6 +546,7 @@ bool cmGlobalVisualStudioVersionedGenerator::ParseGeneratorInstance(
   std::string const& is, cmMakefile* mf)
 {
   this->GeneratorInstance.clear();
+  this->GeneratorInstanceVersion.clear();
 
   std::vector<std::string> const fields = cmTokenize(is, ",");
   std::vector<std::string>::const_iterator fi = fields.begin();
@@ -563,8 +617,10 @@ bool cmGlobalVisualStudioVersionedGenerator::ParseGeneratorInstance(
 bool cmGlobalVisualStudioVersionedGenerator::ProcessGeneratorInstanceField(
   std::string const& key, std::string const& value)
 {
-  static_cast<void>(key);
-  static_cast<void>(value);
+  if (key == "version") {
+    this->GeneratorInstanceVersion = value;
+    return true;
+  }
   return false;
 }
 

+ 1 - 0
Source/cmGlobalVisualStudioVersionedGenerator.h

@@ -83,5 +83,6 @@ private:
   bool ParseGeneratorInstance(std::string const& is, cmMakefile* mf);
 
   std::string GeneratorInstance;
+  std::string GeneratorInstanceVersion;
   cm::optional<std::string> LastGeneratorInstanceString;
 };

+ 12 - 1
Source/cmVSSetupHelper.cxx

@@ -102,10 +102,12 @@ cmVSSetupAPIHelper::~cmVSSetupAPIHelper()
     CoUninitialize();
 }
 
-bool cmVSSetupAPIHelper::SetVSInstance(std::string const& vsInstallLocation)
+bool cmVSSetupAPIHelper::SetVSInstance(std::string const& vsInstallLocation,
+                                       std::string const& vsInstallVersion)
 {
   this->SpecifiedVSInstallLocation = vsInstallLocation;
   cmSystemTools::ConvertToUnixSlashes(this->SpecifiedVSInstallLocation);
+  this->SpecifiedVSInstallVersion = vsInstallVersion;
   chosenInstanceInfo = VSInstanceInfo();
   return this->EnumerateAndChooseVSInstance();
 }
@@ -366,6 +368,15 @@ bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
         std::string currentVSLocation = instanceInfo.GetInstallLocation();
         if (cmSystemTools::ComparePath(currentVSLocation,
                                        this->SpecifiedVSInstallLocation)) {
+          if (this->SpecifiedVSInstallVersion.empty() ||
+              instanceInfo.Version == this->SpecifiedVSInstallVersion) {
+            chosenInstanceInfo = instanceInfo;
+            return true;
+          }
+        }
+      } else if (!this->SpecifiedVSInstallVersion.empty()) {
+        // We are looking for a specific version.
+        if (instanceInfo.Version == this->SpecifiedVSInstallVersion) {
           chosenInstanceInfo = instanceInfo;
           return true;
         }

+ 3 - 1
Source/cmVSSetupHelper.h

@@ -99,7 +99,8 @@ public:
   cmVSSetupAPIHelper(unsigned int version);
   ~cmVSSetupAPIHelper();
 
-  bool SetVSInstance(std::string const& vsInstallLocation);
+  bool SetVSInstance(std::string const& vsInstallLocation,
+                     std::string const& vsInstallVersion);
 
   bool IsVSInstalled();
   bool GetVSInstanceInfo(std::string& vsInstallLocation);
@@ -132,4 +133,5 @@ private:
   bool IsEWDKEnabled();
 
   std::string SpecifiedVSInstallLocation;
+  std::string SpecifiedVSInstallVersion;
 };

+ 1 - 0
Tests/RunCMake/GeneratorInstance/BadFieldDuplicate-result.txt

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

+ 11 - 0
Tests/RunCMake/GeneratorInstance/BadFieldDuplicate-stderr.txt

@@ -0,0 +1,11 @@
+^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+  Generator
+
+    Visual Studio [^
+]+
+
+  given instance specification
+
+    Test Instance,version=1\.2\.3\.4,version=1\.2\.3\.4
+
+  that contains duplicate field key 'version'\.$

+ 1 - 0
Tests/RunCMake/GeneratorInstance/BadFieldDuplicate.cmake

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

+ 1 - 0
Tests/RunCMake/GeneratorInstance/BadVersionFormat1-result.txt

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

+ 11 - 0
Tests/RunCMake/GeneratorInstance/BadVersionFormat1-stderr.txt

@@ -0,0 +1,11 @@
+^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+  Generator
+
+    Visual Studio [^
+]+
+
+  given instance specification
+
+    version=1\.2\.3
+
+  but the version field is not 4 integer components starting in [0-9]+\.$

+ 1 - 0
Tests/RunCMake/GeneratorInstance/BadVersionFormat1.cmake

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

+ 1 - 0
Tests/RunCMake/GeneratorInstance/BadVersionFormat2-result.txt

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

+ 11 - 0
Tests/RunCMake/GeneratorInstance/BadVersionFormat2-stderr.txt

@@ -0,0 +1,11 @@
+^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+  Generator
+
+    Visual Studio [^
+]+
+
+  given instance specification
+
+    version=1\.2\.3\.x
+
+  but the version field is not 4 integer components starting in [0-9]+\.$

+ 1 - 0
Tests/RunCMake/GeneratorInstance/BadVersionFormat2.cmake

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

+ 1 - 0
Tests/RunCMake/GeneratorInstance/BadVersionNumber-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorInstance/BadVersionNumber-stderr.txt

@@ -0,0 +1,9 @@
+^CMake Error at CMakeLists.txt:[0-9] \(project\):
+  Generator
+
+    Visual Studio [^
+]+
+
+  could not find specified instance of Visual Studio:
+
+    version=[0-9]+\.999\.99999\.999$

+ 1 - 0
Tests/RunCMake/GeneratorInstance/BadVersionNumber.cmake

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

+ 1 - 0
Tests/RunCMake/GeneratorInstance/DefaultInstance.cmake

@@ -11,3 +11,4 @@ elseif(NOT IS_DIRECTORY "${CMAKE_GENERATOR_INSTANCE}")
     "  ${CMAKE_GENERATOR_INSTANCE}\n"
     "which is not an existing directory.")
 endif()
+file(WRITE "${CMAKE_BINARY_DIR}/instance.txt" "${CMAKE_GENERATOR_INSTANCE}")

+ 19 - 1
Tests/RunCMake/GeneratorInstance/RunCMakeTest.cmake

@@ -1,8 +1,14 @@
 include(RunCMake)
 
-if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio 1[56789]")
+if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio (1[56789])")
+  set(vs_major "${CMAKE_MATCH_1}")
+
   set(RunCMake_GENERATOR_INSTANCE "")
   run_cmake(DefaultInstance)
+  set(instance_txt "${RunCMake_BINARY_DIR}/DefaultInstance-build/instance.txt")
+  if(EXISTS "${instance_txt}")
+    file(READ "${instance_txt}" default_instance)
+  endif()
 
   set(RunCMake_GENERATOR_INSTANCE "${RunCMake_SOURCE_DIR}/instance_does_not_exist")
   run_cmake(MissingInstance)
@@ -14,6 +20,18 @@ if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio 1[56789]")
   run_cmake(BadFieldNoComma)
   set(RunCMake_GENERATOR_INSTANCE "Test Instance,unknown=")
   run_cmake(BadFieldUnknown)
+  set(RunCMake_GENERATOR_INSTANCE "Test Instance,version=1.2.3.4,version=1.2.3.4")
+  run_cmake(BadFieldDuplicate)
+  set(RunCMake_GENERATOR_INSTANCE "version=1.2.3")
+  run_cmake(BadVersionFormat1)
+  set(RunCMake_GENERATOR_INSTANCE "version=1.2.3.x")
+  run_cmake(BadVersionFormat2)
+  set(RunCMake_GENERATOR_INSTANCE "version=${vs_major}.999.99999.999")
+  run_cmake(BadVersionNumber)
+  if(IS_DIRECTORY "${default_instance}")
+    set(RunCMake_GENERATOR_INSTANCE "${default_instance},version=${vs_major}.999.99999.999")
+    run_cmake(WrongVersion)
+  endif()
 else()
   set(RunCMake_GENERATOR_INSTANCE "")
   run_cmake(NoInstance)

+ 1 - 0
Tests/RunCMake/GeneratorInstance/WrongVersion-result.txt

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

+ 10 - 0
Tests/RunCMake/GeneratorInstance/WrongVersion-stderr.txt

@@ -0,0 +1,10 @@
+^CMake Error at CMakeLists.txt:[0-9] \(project\):
+  Generator
+
+    Visual Studio [^
+]+
+
+  could not find specified instance of Visual Studio:
+
+    [^,
+]+,version=[0-9]+\.999\.99999\.999$

+ 1 - 0
Tests/RunCMake/GeneratorInstance/WrongVersion.cmake

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