Просмотр исходного кода

Xcode: Add option to specify build system variant

Extend the `-T <toolset>` option to support a `buildsystem=` field with
the Xcode generator.  Add a `CMAKE_XCODE_BUILD_SYSTEM` variable to
inform project code about the selected build system variant.
Brad King 5 лет назад
Родитель
Сommit
2db623f554

+ 21 - 2
Help/generator/Xcode.rst

@@ -5,9 +5,28 @@ Generate Xcode project files.
 
 This supports Xcode 5.0 and above.
 
-Toolset Selection
-^^^^^^^^^^^^^^^^^
+.. _`Xcode Build System Selection`:
+
+Toolset and Build System Selection
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 By default Xcode is allowed to select its own default toolchain.
 The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
 via the :manual:`cmake(1)` ``-T`` option, to specify another toolset.
+
+This generator supports toolset specification using one of these forms:
+
+* ``toolset``
+* ``toolset[,key=value]*``
+* ``key=value[,key=value]*``
+
+The ``toolset`` specifies the toolset name.  The selected toolset name
+is provided in the :variable:`CMAKE_XCODE_PLATFORM_TOOLSET` variable.
+
+The ``key=value`` pairs form a comma-separated list of options to
+specify generator-specific details of the toolset selection.
+Supported pairs are:
+
+``buildsystem=<variant>``
+  Specify the buildsystem variant to use.
+  See the :variable:`CMAKE_XCODE_BUILD_SYSTEM` variable for allowed values.

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -125,6 +125,7 @@ Variables that Provide Information
    /variable/CMAKE_VS_PLATFORM_TOOLSET_VERSION
    /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION
    /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM
+   /variable/CMAKE_XCODE_BUILD_SYSTEM
    /variable/CMAKE_XCODE_PLATFORM_TOOLSET
    /variable/PROJECT-NAME_BINARY_DIR
    /variable/PROJECT-NAME_DESCRIPTION

+ 17 - 0
Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst

@@ -0,0 +1,17 @@
+CMAKE_XCODE_BUILD_SYSTEM
+------------------------
+
+Xcode build system selection.
+
+The :generator:`Xcode` generator defines this variable to indicate which
+variant of the Xcode build system will be used.  The value is the
+version of Xcode in which the corresponding build system first became
+mature enough for use by CMake.  The possible values are:
+
+``1``
+  The original Xcode build system.
+  This is the default.
+
+The ``CMAKE_XCODE_BUILD_SYSTEM`` variable is informational and should not
+be modified by project code.  See the :ref:`Xcode Build System Selection`
+documentation section to select the Xcode build system.

+ 103 - 12
Source/cmGlobalXCodeGenerator.cxx

@@ -8,6 +8,7 @@
 #include <cstring>
 #include <iomanip>
 #include <sstream>
+#include <unordered_set>
 #include <utility>
 
 #include <cm/memory>
@@ -280,32 +281,122 @@ bool cmGlobalXCodeGenerator::SetSystemName(std::string const& s,
   return this->cmGlobalGenerator::SetSystemName(s, mf);
 }
 
+namespace {
+cm::string_view cmXcodeBuildSystemString(cmGlobalXCodeGenerator::BuildSystem b)
+{
+  switch (b) {
+    case cmGlobalXCodeGenerator::BuildSystem::One:
+      return "1"_s;
+  }
+  return {};
+}
+}
+
 bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts,
                                                  bool build, cmMakefile* mf)
 {
-  if (ts.find_first_of(",=") != std::string::npos) {
-    std::ostringstream e;
-    /* clang-format off */
-    e <<
-      "Generator\n"
-      "  " << this->GetName() << "\n"
-      "does not recognize the toolset\n"
-      "  " << ts << "\n"
-      "that was specified.";
-    /* clang-format on */
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  if (!this->ParseGeneratorToolset(ts, mf)) {
     return false;
   }
-  this->GeneratorToolset = ts;
   if (build) {
     return true;
   }
   if (!this->GeneratorToolset.empty()) {
     mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET", this->GeneratorToolset);
   }
+  mf->AddDefinition("CMAKE_XCODE_BUILD_SYSTEM",
+                    cmXcodeBuildSystemString(this->XcodeBuildSystem));
   return true;
 }
 
+bool cmGlobalXCodeGenerator::ParseGeneratorToolset(std::string const& ts,
+                                                   cmMakefile* mf)
+{
+  std::vector<std::string> const fields = cmTokenize(ts, ",");
+  auto fi = fields.cbegin();
+  if (fi == fields.cend()) {
+    return true;
+  }
+
+  // The first field may be the Xcode GCC_VERSION.
+  if (fi->find('=') == fi->npos) {
+    this->GeneratorToolset = *fi;
+    ++fi;
+  }
+
+  std::unordered_set<std::string> handled;
+
+  // The rest of the fields must be key=value pairs.
+  for (; fi != fields.cend(); ++fi) {
+    std::string::size_type pos = fi->find('=');
+    if (pos == fi->npos) {
+      /* clang-format off */
+      std::string const& e = cmStrCat(
+        "Generator\n"
+        "  ", this->GetName(), "\n"
+        "given toolset specification\n"
+        "  ", ts, "\n"
+        "that contains a field after the first ',' with no '='."
+        );
+      /* clang-format on */
+      mf->IssueMessage(MessageType::FATAL_ERROR, e);
+      return false;
+    }
+    std::string const key = fi->substr(0, pos);
+    std::string const value = fi->substr(pos + 1);
+    if (!handled.insert(key).second) {
+      /* clang-format off */
+      std::string const& e = cmStrCat(
+        "Generator\n"
+        "  ", this->GetName(), "\n"
+        "given toolset specification\n"
+        "  ", ts, "\n"
+        "that contains duplicate field key '", key, "'."
+        );
+      /* clang-format on */
+      mf->IssueMessage(MessageType::FATAL_ERROR, e);
+      return false;
+    }
+    if (!this->ProcessGeneratorToolsetField(key, value, mf)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool cmGlobalXCodeGenerator::ProcessGeneratorToolsetField(
+  std::string const& key, std::string const& value, cmMakefile* mf)
+{
+  if (key == "buildsystem") {
+    if (value == "1"_s) {
+      this->XcodeBuildSystem = BuildSystem::One;
+    } else {
+      /* clang-format off */
+      std::string const& e = cmStrCat(
+        "Generator\n"
+        "  ",  this->GetName(), "\n"
+        "toolset specification field\n"
+        "  buildsystem=", value, "\n"
+        "value is unkonwn.  It must be '1'."
+        );
+      /* clang-format on */
+      mf->IssueMessage(MessageType::FATAL_ERROR, e);
+      return false;
+    }
+    return true;
+  }
+  /* clang-format off */
+  std::string const& e = cmStrCat(
+    "Generator\n"
+    "  ", this->GetName(), "\n"
+    "given toolset specification that contains invalid field '", key, "'."
+    );
+  /* clang-format on */
+  mf->IssueMessage(MessageType::FATAL_ERROR, e);
+  return false;
+}
+
 void cmGlobalXCodeGenerator::EnableLanguage(
   std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
 {

+ 11 - 0
Source/cmGlobalXCodeGenerator.h

@@ -113,11 +113,20 @@ public:
                            cmMakefile* mf) override;
   void AppendFlag(std::string& flags, std::string const& flag) const;
 
+  enum class BuildSystem
+  {
+    One = 1,
+  };
+
 protected:
   void AddExtraIDETargets() override;
   void Generate() override;
 
 private:
+  bool ParseGeneratorToolset(std::string const& ts, cmMakefile* mf);
+  bool ProcessGeneratorToolsetField(std::string const& key,
+                                    std::string const& value, cmMakefile* mf);
+
   cmXCodeObject* CreateOrGetPBXGroup(cmGeneratorTarget* gtgt,
                                      cmSourceGroup* sg);
   cmXCodeObject* CreatePBXGroup(cmXCodeObject* parent,
@@ -254,6 +263,8 @@ protected:
   std::vector<std::unique_ptr<cmXCodeObject>> XCodeObjects;
   cmXCodeObject* RootObject;
 
+  BuildSystem XcodeBuildSystem = BuildSystem::One;
+
 private:
   std::string const& GetXcodeBuildCommand();
   std::string FindXcodeBuildCommand();

+ 3 - 0
Tests/RunCMake/CMakeLists.txt

@@ -238,6 +238,9 @@ add_RunCMake_test(GenEx-GENEX_EVAL)
 add_RunCMake_test(GeneratorExpression)
 add_RunCMake_test(GeneratorInstance)
 add_RunCMake_test(GeneratorPlatform)
+if(XCODE_VERSION)
+  set(GeneratorToolset_ARGS -DXCODE_VERSION=${XCODE_VERSION})
+endif()
 add_RunCMake_test(GeneratorToolset)
 add_RunCMake_test(GetPrerequisites)
 add_RunCMake_test(GNUInstallDirs -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME})

+ 1 - 5
Tests/RunCMake/GeneratorToolset/BadToolsetHostArchXcode-stderr.txt

@@ -3,8 +3,4 @@ CMake Error at CMakeLists.txt:[0-9]+ \(project\):
 
     .*
 
-  does not recognize the toolset
-
-    Test Toolset,host=x6[45]
-
-  that was specified\.$
+  given toolset specification that contains invalid field 'host'\.$

+ 1 - 0
Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-result.txt

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

+ 10 - 0
Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-stderr.txt

@@ -0,0 +1,10 @@
+CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+  Generator
+
+    Xcode
+
+  toolset specification field
+
+    buildsystem=bad
+
+  value is unkonwn.  It must be '1'\.$

+ 1 - 0
Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem.cmake

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

+ 4 - 0
Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake

@@ -54,6 +54,10 @@ elseif("${RunCMake_GENERATOR}" STREQUAL "Xcode")
   run_cmake(TestToolset)
   set(RunCMake_GENERATOR_TOOLSET "Test Toolset,host=x64")
   run_cmake(BadToolsetHostArchXcode)
+  set(RunCMake_GENERATOR_TOOLSET "buildsystem=bad")
+  run_cmake(BadToolsetXcodeBuildSystem)
+  set(RunCMake_GENERATOR_TOOLSET "Test Toolset")
+  run_cmake(TestToolsetXcodeBuildSystemDefault1)
 else()
   set(RunCMake_GENERATOR_TOOLSET "Bad Toolset")
   run_cmake(BadToolset)

+ 1 - 0
Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-result.txt

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

+ 4 - 0
Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TestToolsetXcodeBuildSystemDefault1.cmake:[0-9]+ \(message\):
+  CMAKE_GENERATOR_TOOLSET is "Test Toolset" as expected.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$

+ 1 - 0
Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stdout.txt

@@ -0,0 +1 @@
+CMAKE_XCODE_BUILD_SYSTEM='1'

+ 8 - 0
Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1.cmake

@@ -0,0 +1,8 @@
+message(STATUS "CMAKE_XCODE_BUILD_SYSTEM='${CMAKE_XCODE_BUILD_SYSTEM}'")
+if(CMAKE_GENERATOR_TOOLSET STREQUAL "Test Toolset")
+  message(FATAL_ERROR "CMAKE_GENERATOR_TOOLSET is \"Test Toolset\" as expected.")
+else()
+  message(FATAL_ERROR
+    "CMAKE_GENERATOR_TOOLSET is \"${CMAKE_GENERATOR_TOOLSET}\" "
+    "but should be \"Test Toolset\"!")
+endif()