ソースを参照

Merge topic 'xcode-global-attribute-variant'

d8bc26a0 Xcode: Parse variant and genex for CMAKE_XCODE_ATTRIBUTE (#14947)
dc0ddb9e Xcode: Store configuration name along with XcodeObject (#14947)
28f98cee Xcode: Make CMAKE_XCODE_ATTRIBUTE calculation last step (#14947)
28db2268 Xcode: Factor out XCODE_ATTRIBUTE_ variant filter (#14947)
Brad King 9 年 前
コミット
070f09f91e

+ 6 - 0
Help/variable/CMAKE_XCODE_ATTRIBUTE_an-attribute.rst

@@ -8,3 +8,9 @@ in the generated Xcode project.  Ignored on other generators.
 
 See the :prop_tgt:`XCODE_ATTRIBUTE_<an-attribute>` target property
 to set attributes on a specific target.
+
+Contents of ``CMAKE_XCODE_ATTRIBUTE_<an-attribute>`` may use
+"generator expressions" with the syntax ``$<...>``.  See the
+:manual:`cmake-generator-expressions(7)` manual for available
+expressions.  See the :manual:`cmake-buildsystem(7)` manual
+for more on defining buildsystem properties.

+ 83 - 53
Source/cmGlobalXCodeGenerator.cxx

@@ -709,6 +709,15 @@ cmXCodeObject* cmGlobalXCodeGenerator
   return obj;
 }
 
+//----------------------------------------------------------------------------
+cmXCodeObject* cmGlobalXCodeGenerator
+::CreateFlatClone(cmXCodeObject* orig)
+{
+  cmXCodeObject* obj = this->CreateObject(orig->GetType());
+  obj->CopyAttributes(orig);
+  return obj;
+}
+
 //----------------------------------------------------------------------------
 std::string
 GetGroupMapKeyFromPath(cmGeneratorTarget* target, const std::string& fullpath)
@@ -1656,6 +1665,46 @@ std::string cmGlobalXCodeGenerator::ExtractFlagRegex(const char* exp,
   return retFlag;
 }
 
+ //----------------------------------------------------------------------------
+// This function strips off Xcode attributes that do not target the current
+// configuration
+void
+cmGlobalXCodeGenerator
+::FilterConfigurationAttribute(std::string const& configName,
+                               std::string& attribute)
+{
+  // Handle [variant=<config>] condition explicitly here.
+  std::string::size_type beginVariant = attribute.find("[variant=");
+  if (beginVariant == std::string::npos)
+    {
+    // There is no variant in this attribute.
+    return;
+    }
+
+  std::string::size_type endVariant = attribute.find("]", beginVariant+9);
+  if (endVariant == std::string::npos)
+    {
+    // There is no terminating bracket.
+    return;
+    }
+
+  // Compare the variant to the configuration.
+  std::string variant =
+    attribute.substr(beginVariant+9, endVariant-beginVariant-9);
+  if (variant == configName)
+    {
+    // The variant matches the configuration so use this
+    // attribute but drop the [variant=<config>] condition.
+    attribute.erase(beginVariant, endVariant-beginVariant+1);
+    }
+  else
+    {
+    // The variant does not match the configuration so
+    // do not use this attribute.
+    attribute.clear();
+    }
+}
+
 //----------------------------------------------------------------------------
 void
 cmGlobalXCodeGenerator::AddCommandsToBuildPhase(cmXCodeObject* buildphase,
@@ -2491,33 +2540,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
     if(i->find("XCODE_ATTRIBUTE_") == 0)
       {
       std::string attribute = i->substr(16);
-      // Handle [variant=<config>] condition explicitly here.
-      std::string::size_type beginVariant =
-        attribute.find("[variant=");
-      if (beginVariant != std::string::npos)
-        {
-        std::string::size_type endVariant =
-          attribute.find("]", beginVariant+9);
-        if (endVariant != std::string::npos)
-          {
-          // Compare the variant to the configuration.
-          std::string variant =
-            attribute.substr(beginVariant+9, endVariant-beginVariant-9);
-          if (variant == configName)
-            {
-            // The variant matches the configuration so use this
-            // attribute but drop the [variant=<config>] condition.
-            attribute.erase(beginVariant, endVariant-beginVariant+1);
-            }
-          else
-            {
-            // The variant does not match the configuration so
-            // do not use this attribute.
-            attribute.clear();
-            }
-          }
-        }
-
+      this->FilterConfigurationAttribute(configName, attribute);
       if (!attribute.empty())
         {
         cmGeneratorExpression ge;
@@ -3419,18 +3442,19 @@ bool cmGlobalXCodeGenerator
     this->CreateObject(cmXCodeObject::XCConfigurationList);
   cmXCodeObject* buildConfigurations =
     this->CreateObject(cmXCodeObject::OBJECT_LIST);
-  std::vector<cmXCodeObject*> configs;
+  typedef std::vector<std::pair<std::string, cmXCodeObject*> > Configs;
+  Configs configs;
   const char *defaultConfigName = "Debug";
   if(this->XcodeVersion == 15)
     {
     cmXCodeObject* configDebug =
       this->CreateObject(cmXCodeObject::XCBuildConfiguration);
     configDebug->AddAttribute("name", this->CreateString("Debug"));
-    configs.push_back(configDebug);
+    configs.push_back(std::make_pair("Debug", configDebug));
     cmXCodeObject* configRelease =
       this->CreateObject(cmXCodeObject::XCBuildConfiguration);
     configRelease->AddAttribute("name", this->CreateString("Release"));
-    configs.push_back(configRelease);
+    configs.push_back(std::make_pair("Release", configRelease));
     }
   else
     {
@@ -3444,13 +3468,12 @@ bool cmGlobalXCodeGenerator
       cmXCodeObject* config =
         this->CreateObject(cmXCodeObject::XCBuildConfiguration);
       config->AddAttribute("name", this->CreateString(name));
-      configs.push_back(config);
+      configs.push_back(std::make_pair(name, config));
       }
     }
-  for(std::vector<cmXCodeObject*>::iterator c = configs.begin();
-      c != configs.end(); ++c)
+  for(Configs::iterator c = configs.begin(); c != configs.end(); ++c)
     {
-    buildConfigurations->AddObject(*c);
+    buildConfigurations->AddObject(c->second);
     }
   configlist->AddAttribute("buildConfigurations", buildConfigurations);
 
@@ -3506,30 +3529,37 @@ bool cmGlobalXCodeGenerator
       this->CreateString(this->GeneratorToolset.c_str()));
     }
 
-  // Put this last so it can override existing settings
-  // Convert "CMAKE_XCODE_ATTRIBUTE_*" variables directly.
-  {
-    std::vector<std::string> vars = this->CurrentMakefile->GetDefinitions();
-    for(std::vector<std::string>::const_iterator i = vars.begin();
-        i != vars.end(); ++i)
-    {
-      if(i->find("CMAKE_XCODE_ATTRIBUTE_") == 0)
-      {
-        buildSettings->AddAttribute(i->substr(22).c_str(),
-          this->CreateString(
-            this->CurrentMakefile->GetDefinition(i->c_str())));
-      }
-    }
-  }
-
   std::string symroot = root->GetCurrentBinaryDirectory();
   symroot += "/build";
   buildSettings->AddAttribute("SYMROOT", this->CreateString(symroot.c_str()));
 
-  for( std::vector<cmXCodeObject*>::iterator i = configs.begin();
-       i != configs.end(); ++i)
+  for(Configs::iterator i = configs.begin(); i != configs.end(); ++i)
     {
-    (*i)->AddAttribute("buildSettings", buildSettings);
+    cmXCodeObject* buildSettingsForCfg = this->CreateFlatClone(buildSettings);
+
+    // Put this last so it can override existing settings
+    // Convert "CMAKE_XCODE_ATTRIBUTE_*" variables directly.
+    std::vector<std::string> vars = this->CurrentMakefile->GetDefinitions();
+    for(std::vector<std::string>::const_iterator d = vars.begin();
+        d != vars.end(); ++d)
+      {
+      if(d->find("CMAKE_XCODE_ATTRIBUTE_") == 0)
+        {
+        std::string attribute = d->substr(22);
+        this->FilterConfigurationAttribute(i->first, attribute);
+        if(!attribute.empty())
+          {
+          cmGeneratorExpression ge;
+          std::string processed =
+            ge.Parse(this->CurrentMakefile->GetDefinition(*d))
+              ->Evaluate(this->CurrentLocalGenerator, i->first);
+          buildSettingsForCfg->AddAttribute(attribute,
+            this->CreateString(processed));
+          }
+        }
+      }
+    // store per-config buildSettings into configuration object
+    i->second->AddAttribute("buildSettings", buildSettingsForCfg);
     }
 
   this->RootObject->AddAttribute("buildConfigurationList",

+ 3 - 0
Source/cmGlobalXCodeGenerator.h

@@ -130,6 +130,7 @@ private:
   cmXCodeObject* CreateObject(cmXCodeObject::Type type);
   cmXCodeObject* CreateString(const std::string& s);
   cmXCodeObject* CreateObjectReference(cmXCodeObject*);
+  cmXCodeObject* CreateFlatClone(cmXCodeObject*);
   cmXCodeObject* CreateXCodeTarget(cmGeneratorTarget *gtgt,
                                    cmXCodeObject* buildPhases);
   void ForceLinkerLanguages();
@@ -152,6 +153,8 @@ private:
   std::string ExtractFlag(const char* flag, std::string& flags);
   std::string ExtractFlagRegex(const char* exp, int matchIndex,
                                std::string& flags);
+  void FilterConfigurationAttribute(std::string const& configName,
+                                    std::string& attribute);
   void SortXCodeObjects();
   // delete all objects in the this->XCodeObjects vector.
   void ClearXCodeObjects();

+ 48 - 0
Tests/RunCMake/XcodeProject/XcodeAttributeGenex-check.cmake

@@ -1,3 +1,5 @@
+# per target attribute with genex
+
 set(expect "TEST_HOST = \"[^;\"]*Tests/RunCMake/XcodeProject/XcodeAttributeGenex-build/[^;\"/]*/some\"")
 file(STRINGS ${RunCMake_TEST_BINARY_DIR}/XcodeAttributeGenex.xcodeproj/project.pbxproj actual
      REGEX "TEST_HOST = .*;" LIMIT_COUNT 1)
@@ -5,3 +7,49 @@ if(NOT "${actual}" MATCHES "${expect}")
   message(SEND_ERROR "The actual project contains the line:\n ${actual}\n"
     "which does not match expected regex:\n ${expect}\n")
 endif()
+
+# per target attribute with variant
+
+file(STRINGS ${RunCMake_TEST_BINARY_DIR}/XcodeAttributeGenex.xcodeproj/project.pbxproj actual
+     REGEX "CONFIG_SPECIFIC = .*;")
+list(REMOVE_DUPLICATES actual)
+
+set(expect "CONFIG_SPECIFIC = general")
+if(NOT "${actual}" MATCHES "${expect}")
+  message(SEND_ERROR "The actual project contains the line:\n ${actual}\n"
+    "which does not match expected regex:\n ${expect}\n")
+endif()
+
+set(expect "CONFIG_SPECIFIC = release")
+if(NOT "${actual}" MATCHES "${expect}")
+  message(SEND_ERROR "The actual project contains the line:\n ${actual}\n"
+    "which does not match expected regex:\n ${expect}\n")
+endif()
+
+# global attribute with genex
+
+set(expect "ANOTHER_GLOBAL = \"[^;\"]*Tests/RunCMake/XcodeProject/XcodeAttributeGenex-build/[^;\"/]*/another\"")
+file(STRINGS ${RunCMake_TEST_BINARY_DIR}/XcodeAttributeGenex.xcodeproj/project.pbxproj actual
+     REGEX "ANOTHER_GLOBAL = .*;" LIMIT_COUNT 1)
+if(NOT "${actual}" MATCHES "${expect}")
+  message(SEND_ERROR "The actual project contains the line:\n ${actual}\n"
+    "which does not match expected regex:\n ${expect}\n")
+endif()
+
+# global attribute with variant
+
+file(STRINGS ${RunCMake_TEST_BINARY_DIR}/XcodeAttributeGenex.xcodeproj/project.pbxproj actual
+     REGEX "ANOTHER_CONFIG = .*;" LIMIT_COUNT 4)
+list(REMOVE_DUPLICATES actual)
+
+set(expect "ANOTHER_CONFIG = general")
+if(NOT "${actual}" MATCHES "${expect}")
+  message(SEND_ERROR "The actual project contains the line:\n ${actual}\n"
+    "which does not match expected regex:\n ${expect}\n")
+endif()
+
+set(expect "ANOTHER_CONFIG = debug")
+if(NOT "${actual}" MATCHES "${expect}")
+  message(SEND_ERROR "The actual project contains the line:\n ${actual}\n"
+    "which does not match expected regex:\n ${expect}\n")
+endif()

+ 13 - 1
Tests/RunCMake/XcodeProject/XcodeAttributeGenex.cmake

@@ -1,4 +1,16 @@
 enable_language(C)
 add_executable(some main.c)
 add_executable(another main.c)
-set_property(TARGET another PROPERTY XCODE_ATTRIBUTE_TEST_HOST "$<TARGET_FILE:some>")
+set_target_properties(another PROPERTIES
+  # per target attribute with genex
+  XCODE_ATTRIBUTE_TEST_HOST "$<TARGET_FILE:some>"
+  # per target attribute with variant
+  XCODE_ATTRIBUTE_CONFIG_SPECIFIC[variant=Release] "release"
+  XCODE_ATTRIBUTE_CONFIG_SPECIFIC "general")
+
+# global attribute with genex
+set(CMAKE_XCODE_ATTRIBUTE_ANOTHER_GLOBAL "$<TARGET_FILE:another>")
+
+# global attribute with variant
+set(CMAKE_XCODE_ATTRIBUTE_ANOTHER_CONFIG "general")
+set(CMAKE_XCODE_ATTRIBUTE_ANOTHER_CONFIG[variant=Debug] "debug")