Browse Source

macOS: Add support for linking against .xcframework folders

Issue: #21752
Kyle Edwards 2 years ago
parent
commit
7050ac56a1
94 changed files with 828 additions and 6 deletions
  1. 6 0
      Help/command/target_link_libraries.rst
  2. 6 0
      Help/prop_tgt/IMPORTED_LOCATION.rst
  3. 7 0
      Help/release/dev/xcframework-target-link-libraries.rst
  4. 4 0
      Source/CMakeLists.txt
  5. 88 0
      Source/cmComputeLinkInformation.cxx
  6. 5 0
      Source/cmComputeLinkInformation.h
  7. 41 0
      Source/cmGeneratorTarget.cxx
  8. 2 0
      Source/cmGeneratorTarget.h
  9. 57 6
      Source/cmGlobalXCodeGenerator.cxx
  10. 39 0
      Source/cmLocalGenerator.cxx
  11. 3 0
      Source/cmLocalGenerator.h
  12. 33 0
      Source/cmPlistParser.cxx
  13. 13 0
      Source/cmPlistParser.h
  14. 6 0
      Source/cmSystemTools.cxx
  15. 3 0
      Source/cmSystemTools.h
  16. 20 0
      Source/cmTarget.cxx
  17. 174 0
      Source/cmXcFramework.cxx
  18. 44 0
      Source/cmXcFramework.h
  19. 17 0
      Tests/RunCMake/CMakeLists.txt
  20. 3 0
      Tests/RunCMake/XcFramework/CMakeLists.txt
  21. 83 0
      Tests/RunCMake/XcFramework/RunCMakeTest.cmake
  22. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-ios.cmake
  23. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios.cmake
  24. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-link-phase-macos.cmake
  25. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-link-phase-tvos.cmake
  26. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-link-phase-visionos.cmake
  27. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-link-phase-watchos.cmake
  28. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-macos.cmake
  29. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-tvos.cmake
  30. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-visionos.cmake
  31. 1 0
      Tests/RunCMake/XcFramework/create-executable-framework-watchos.cmake
  32. 1 0
      Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase-result.txt
  33. 11 0
      Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase-stderr.txt
  34. 1 0
      Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase.cmake
  35. 1 0
      Tests/RunCMake/XcFramework/create-executable-incomplete-result.txt
  36. 10 0
      Tests/RunCMake/XcFramework/create-executable-incomplete-stderr.txt
  37. 1 0
      Tests/RunCMake/XcFramework/create-executable-incomplete.cmake
  38. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-ios.cmake
  39. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios.cmake
  40. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-link-phase-macos.cmake
  41. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-link-phase-tvos.cmake
  42. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-link-phase-visionos.cmake
  43. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-link-phase-watchos.cmake
  44. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-macos.cmake
  45. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-tvos.cmake
  46. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-visionos.cmake
  47. 1 0
      Tests/RunCMake/XcFramework/create-executable-library-watchos.cmake
  48. 2 0
      Tests/RunCMake/XcFramework/create-executable-link-phase.cmake
  49. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-ios.cmake
  50. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios.cmake
  51. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-macos.cmake
  52. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-tvos.cmake
  53. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-visionos.cmake
  54. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-watchos.cmake
  55. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-macos.cmake
  56. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-tvos.cmake
  57. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-visionos.cmake
  58. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-framework-watchos.cmake
  59. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase-result.txt
  60. 11 0
      Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase-stderr.txt
  61. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase.cmake
  62. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-incomplete-result.txt
  63. 10 0
      Tests/RunCMake/XcFramework/create-executable-target-incomplete-stderr.txt
  64. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-incomplete.cmake
  65. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-ios.cmake
  66. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios.cmake
  67. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-macos.cmake
  68. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-tvos.cmake
  69. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-visionos.cmake
  70. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-watchos.cmake
  71. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-macos.cmake
  72. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-tvos.cmake
  73. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-visionos.cmake
  74. 1 0
      Tests/RunCMake/XcFramework/create-executable-target-library-watchos.cmake
  75. 2 0
      Tests/RunCMake/XcFramework/create-executable-target-link-phase.cmake
  76. 21 0
      Tests/RunCMake/XcFramework/create-executable-target.cmake
  77. 18 0
      Tests/RunCMake/XcFramework/create-executable.cmake
  78. 1 0
      Tests/RunCMake/XcFramework/create-framework-ios.cmake
  79. 1 0
      Tests/RunCMake/XcFramework/create-framework-macos.cmake
  80. 1 0
      Tests/RunCMake/XcFramework/create-framework-tvos.cmake
  81. 1 0
      Tests/RunCMake/XcFramework/create-framework-visionos.cmake
  82. 1 0
      Tests/RunCMake/XcFramework/create-framework-watchos.cmake
  83. 3 0
      Tests/RunCMake/XcFramework/create-framework.cmake
  84. 12 0
      Tests/RunCMake/XcFramework/create-library-common.cmake
  85. 1 0
      Tests/RunCMake/XcFramework/create-library-ios.cmake
  86. 1 0
      Tests/RunCMake/XcFramework/create-library-macos.cmake
  87. 1 0
      Tests/RunCMake/XcFramework/create-library-tvos.cmake
  88. 1 0
      Tests/RunCMake/XcFramework/create-library-visionos.cmake
  89. 1 0
      Tests/RunCMake/XcFramework/create-library-watchos.cmake
  90. 1 0
      Tests/RunCMake/XcFramework/create-library.cmake
  91. 7 0
      Tests/RunCMake/XcFramework/myexe/myexe.c
  92. 3 0
      Tests/RunCMake/XcFramework/mylib/include/mylib/mylib.h
  93. 3 0
      Tests/RunCMake/XcFramework/mylib/mylib.c
  94. 2 0
      bootstrap

+ 6 - 0
Help/command/target_link_libraries.rst

@@ -66,6 +66,12 @@ Each ``<item>`` may be:
   :ref:`usage requirement <Target Usage Requirements>`.  This has the same
   effect as passing the framework directory as an include directory.
 
+  .. versionadded:: 3.28
+
+    The library file may point to a ``.xcframework`` folder on macOS. If it
+    does, the target will get the selected library's ``Headers`` directory as
+    a usage requirement.
+
   .. versionadded:: 3.8
     On :ref:`Visual Studio Generators` for VS 2010 and above, library files
     ending in ``.targets`` will be treated as MSBuild targets files and

+ 6 - 0
Help/prop_tgt/IMPORTED_LOCATION.rst

@@ -20,6 +20,12 @@ non-imported targets.
   For frameworks on macOS, this may be the location of the framework folder
   itself.
 
+.. versionadded:: 3.28
+
+  This may be the location of a ``.xcframework`` folder on macOS. If it is,
+  any target that links against it will get the selected library's ``Headers``
+  directory as a usage requirement.
+
 The ``IMPORTED_LOCATION`` target property may be overridden for a
 given configuration ``<CONFIG>`` by the configuration-specific
 :prop_tgt:`IMPORTED_LOCATION_<CONFIG>` target property.  Furthermore,

+ 7 - 0
Help/release/dev/xcframework-target-link-libraries.rst

@@ -0,0 +1,7 @@
+xcframework-target-link-libraries
+---------------------------------
+
+* Targets may now link against an ``.xcframework`` folder in
+  :command:`target_link_libraries`.
+* The :prop_tgt:`IMPORTED_LOCATION` property of a target may now be an
+  ``.xcframework`` folder.

+ 4 - 0
Source/CMakeLists.txt

@@ -371,6 +371,8 @@ add_library(
   cmNewLineStyle.cxx
   cmOrderDirectories.cxx
   cmOrderDirectories.h
+  cmPlistParser.cxx
+  cmPlistParser.h
   cmPolicies.h
   cmPolicies.cxx
   cmProcessOutput.cxx
@@ -451,6 +453,8 @@ add_library(
   cmWorkerPool.h
   cmWorkingDirectory.cxx
   cmWorkingDirectory.h
+  cmXcFramework.cxx
+  cmXcFramework.h
   cmXMLParser.cxx
   cmXMLParser.h
   cmXMLSafe.cxx

+ 88 - 0
Source/cmComputeLinkInformation.cxx

@@ -29,6 +29,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmValue.h"
+#include "cmXcFramework.h"
 #include "cmake.h"
 
 // #define CM_COMPUTE_LINK_INFO_DEBUG
@@ -373,6 +374,10 @@ cmComputeLinkInformation::cmComputeLinkInformation(
   this->LibraryFeatureDescriptors.emplace(
     "__CMAKE_LINK_FRAMEWORK",
     LibraryFeatureDescriptor{ "__CMAKE_LINK_FRAMEWORK", "<LIBRARY>" });
+  // To link xcframework using a full path
+  this->LibraryFeatureDescriptors.emplace(
+    "__CMAKE_LINK_XCFRAMEWORK",
+    LibraryFeatureDescriptor{ "__CMAKE_LINK_XCFRAMEWORK", "<LIBRARY>" });
 
   // Check the platform policy for missing soname case.
   this->NoSONameUsesPath =
@@ -519,6 +524,12 @@ cmComputeLinkInformation::GetFrameworkPathsEmitted() const
   return this->FrameworkPathsEmitted;
 }
 
+std::vector<std::string> const&
+cmComputeLinkInformation::GetXcFrameworkHeaderPaths() const
+{
+  return this->XcFrameworkHeaderPaths;
+}
+
 const std::set<const cmGeneratorTarget*>&
 cmComputeLinkInformation::GetSharedLibrariesLinked() const
 {
@@ -1159,6 +1170,13 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
       } else {
         this->ObjectLibrariesLinked.push_back(entry.Target);
       }
+    } else if (this->GlobalGenerator->IsXcode() &&
+               !tgt->GetImportedXcFrameworkPath(config).empty()) {
+      this->Items.emplace_back(
+        tgt->GetImportedXcFrameworkPath(config), ItemIsPath::Yes, tgt,
+        this->FindLibraryFeature(entry.Feature == DEFAULT
+                                   ? "__CMAKE_LINK_XCFRAMEWORK"
+                                   : entry.Feature));
     } else {
       // Decide whether to use an import library.
       cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
@@ -1198,6 +1216,25 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
         this->AddRuntimeDLL(tgt);
       }
     }
+
+    auto xcFrameworkPath = tgt->GetImportedXcFrameworkPath(config);
+    if (!xcFrameworkPath.empty()) {
+      auto plist = cmParseXcFrameworkPlist(xcFrameworkPath, *this->Makefile,
+                                           item.Backtrace);
+      if (!plist) {
+        return;
+      }
+      if (auto const* library =
+            plist->SelectSuitableLibrary(*this->Makefile, item.Backtrace)) {
+        if (!library->HeadersPath.empty()) {
+          this->AddXcFrameworkHeaderPath(cmStrCat(xcFrameworkPath, '/',
+                                                  library->LibraryIdentifier,
+                                                  '/', library->HeadersPath));
+        }
+      } else {
+        return;
+      }
+    }
   } else {
     // This is not a CMake target.  Use the name given.
     if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) ||
@@ -1206,6 +1243,12 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
          this->Target->IsApple())) {
       // This is a framework.
       this->AddFrameworkItem(entry);
+    } else if (cmHasSuffix(entry.Feature, "XCFRAMEWORK"_s) ||
+               (entry.Feature == DEFAULT &&
+                cmSystemTools::IsPathToXcFramework(item.Value) &&
+                this->Target->IsApple())) {
+      // This is a framework.
+      this->AddXcFrameworkItem(entry);
     } else if (cmSystemTools::FileIsFullPath(item.Value)) {
       if (cmSystemTools::FileIsDirectory(item.Value)) {
         // This is a directory.
@@ -1945,6 +1988,46 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
   }
 }
 
+void cmComputeLinkInformation::AddXcFrameworkItem(LinkEntry const& entry)
+{
+  auto plist = cmParseXcFrameworkPlist(entry.Item.Value, *this->Makefile,
+                                       entry.Item.Backtrace);
+  if (!plist) {
+    return;
+  }
+
+  if (auto const* lib =
+        plist->SelectSuitableLibrary(*this->Makefile, entry.Item.Backtrace)) {
+    if (this->GlobalGenerator->IsXcode()) {
+      this->Items.emplace_back(
+        entry.Item.Value, ItemIsPath::Yes, nullptr,
+        this->FindLibraryFeature(entry.Feature == DEFAULT
+                                   ? "__CMAKE_LINK_XCFRAMEWORK"
+                                   : entry.Feature));
+    } else {
+      auto libraryPath = cmStrCat(
+        entry.Item.Value, '/', lib->LibraryIdentifier, '/', lib->LibraryPath);
+      LinkEntry libraryEntry(
+        BT<std::string>(libraryPath, entry.Item.Backtrace), entry.Target);
+
+      if (cmSystemTools::IsPathToFramework(libraryPath) &&
+          this->Target->IsApple()) {
+        // This is a framework.
+        this->AddFrameworkItem(libraryEntry);
+      } else {
+        this->Depends.push_back(libraryPath);
+        this->AddFullItem(libraryEntry);
+        this->AddLibraryRuntimeInfo(libraryPath);
+        if (!lib->HeadersPath.empty()) {
+          this->AddXcFrameworkHeaderPath(cmStrCat(entry.Item.Value, '/',
+                                                  lib->LibraryIdentifier, '/',
+                                                  lib->HeadersPath));
+        }
+      }
+    }
+  }
+}
+
 void cmComputeLinkInformation::DropDirectoryItem(BT<std::string> const& item)
 {
   // A full path to a directory was found as a link item.  Warn the
@@ -1982,6 +2065,11 @@ void cmComputeLinkInformation::AddFrameworkPath(std::string const& p)
   }
 }
 
+void cmComputeLinkInformation::AddXcFrameworkHeaderPath(std::string const& p)
+{
+  this->XcFrameworkHeaderPaths.push_back(p);
+}
+
 bool cmComputeLinkInformation::CheckSharedLibNoSOName(LinkEntry const& entry)
 {
   // This platform will use the path to a library as its soname if the

+ 5 - 0
Source/cmComputeLinkInformation.h

@@ -88,6 +88,7 @@ public:
   std::vector<std::string> const& GetDepends() const;
   std::vector<std::string> const& GetFrameworkPaths() const;
   std::set<std::string> const& GetFrameworkPathsEmitted() const;
+  std::vector<std::string> const& GetXcFrameworkHeaderPaths() const;
   std::string GetLinkLanguage() const { return this->LinkLanguage; }
   std::vector<std::string> const& GetRuntimeSearchPath() const;
   std::string const& GetRuntimeFlag() const { return this->RuntimeFlag; }
@@ -132,6 +133,7 @@ private:
   std::vector<std::string> Directories;
   std::vector<std::string> Depends;
   std::vector<std::string> FrameworkPaths;
+  std::vector<std::string> XcFrameworkHeaderPaths;
   std::vector<std::string> RuntimeSearchPath;
   std::set<cmGeneratorTarget const*> SharedLibrariesLinked;
   std::vector<cmGeneratorTarget const*> ObjectLibrariesLinked;
@@ -204,6 +206,7 @@ private:
   bool CheckImplicitDirItem(LinkEntry const& entry);
   void AddUserItem(LinkEntry const& entry, bool pathNotKnown);
   void AddFrameworkItem(LinkEntry const& entry);
+  void AddXcFrameworkItem(LinkEntry const& entry);
   void DropDirectoryItem(BT<std::string> const& item);
   bool CheckSharedLibNoSOName(LinkEntry const& entry);
   void AddSharedLibNoSOName(LinkEntry const& entry);
@@ -214,6 +217,8 @@ private:
   void AddFrameworkPath(std::string const& p);
   std::set<std::string> FrameworkPathsEmitted;
 
+  void AddXcFrameworkHeaderPath(std::string const& p);
+
   // Linker search path computation.
   std::unique_ptr<cmOrderDirectories> OrderLinkerSearchPath;
   bool FinishLinkerSearchDirectories();

+ 41 - 0
Source/cmGeneratorTarget.cxx

@@ -8874,6 +8874,47 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
   return filename;
 }
 
+std::string cmGeneratorTarget::GetImportedXcFrameworkPath(
+  const std::string& config) const
+{
+  if (!(this->IsApple() && this->IsImported() &&
+        (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+         this->GetType() == cmStateEnums::STATIC_LIBRARY ||
+         this->GetType() == cmStateEnums::UNKNOWN_LIBRARY))) {
+    return {};
+  }
+
+  std::string desiredConfig = config;
+  if (config.empty()) {
+    desiredConfig = "NOCONFIG";
+  }
+
+  std::string result;
+
+  cmValue loc = nullptr;
+  cmValue imp = nullptr;
+  std::string suffix;
+
+  if (this->Target->GetMappedConfig(desiredConfig, loc, imp, suffix)) {
+    if (loc) {
+      result = *loc;
+    } else {
+      std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
+      if (cmValue configLocation = this->GetProperty(impProp)) {
+        result = *configLocation;
+      } else if (cmValue location = this->GetProperty("IMPORTED_LOCATION")) {
+        result = *location;
+      }
+    }
+
+    if (cmSystemTools::IsPathToXcFramework(result)) {
+      return result;
+    }
+  }
+
+  return {};
+}
+
 bool cmGeneratorTarget::HaveFortranSources(std::string const& config) const
 {
   auto sources = cmGeneratorTarget::GetSourceFiles(config);

+ 2 - 0
Source/cmGeneratorTarget.h

@@ -926,6 +926,8 @@ public:
     cmSourceFile& source, const std::string& dir,
     cm::optional<std::set<std::string>>& languages) const;
 
+  std::string GetImportedXcFrameworkPath(const std::string& config) const;
+
 private:
   void AddSourceCommon(const std::string& src, bool before = false);
 

+ 57 - 6
Source/cmGlobalXCodeGenerator.cxx

@@ -53,6 +53,7 @@
 #include "cmXCodeObject.h"
 #include "cmXCodeScheme.h"
 #include "cmXMLWriter.h"
+#include "cmXcFramework.h"
 #include "cmake.h"
 
 #if !defined(CMAKE_BOOTSTRAP) && defined(__APPLE__)
@@ -1061,12 +1062,14 @@ bool IsLinkPhaseLibraryExtension(const std::string& fileExt)
 {
   // Empty file extension is a special case for paths to framework's
   // internal binary which could be MyFw.framework/Versions/*/MyFw
-  return (fileExt == ".framework" || fileExt == ".a" || fileExt == ".o" ||
-          fileExt == ".dylib" || fileExt == ".tbd" || fileExt.empty());
+  return (fileExt == ".framework" || fileExt == ".xcframework" ||
+          fileExt == ".a" || fileExt == ".o" || fileExt == ".dylib" ||
+          fileExt == ".tbd" || fileExt.empty());
 }
 bool IsLibraryType(const std::string& fileType)
 {
-  return (fileType == "wrapper.framework" || fileType == "archive.ar" ||
+  return (fileType == "wrapper.framework" ||
+          fileType == "wrapper.xcframework" || fileType == "archive.ar" ||
           fileType == "compiled.mach-o.objfile" ||
           fileType == "compiled.mach-o.dylib" ||
           fileType == "compiled.mach-o.executable" ||
@@ -1079,6 +1082,9 @@ std::string GetDirectoryValueFromFileExtension(const std::string& dirExt)
   if (ext == "framework") {
     return "wrapper.framework";
   }
+  if (ext == "xcframework") {
+    return "wrapper.xcframework";
+  }
   if (ext == "xcassets") {
     return "folder.assetcatalog";
   }
@@ -3607,6 +3613,8 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
         bool canUseLinkPhase = !libItem.HasFeature() ||
           libItem.GetFeatureName() == "__CMAKE_LINK_FRAMEWORK"_s ||
           libItem.GetFeatureName() == "FRAMEWORK"_s ||
+          libItem.GetFeatureName() == "__CMAKE_LINK_XCFRAMEWORK"_s ||
+          libItem.GetFeatureName() == "XCFRAMEWORK"_s ||
           libItem.GetFeatureName() == "WEAK_FRAMEWORK"_s ||
           libItem.GetFeatureName() == "WEAK_LIBRARY"_s;
         if (canUseLinkPhase) {
@@ -3917,12 +3925,14 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
           if (cmSystemTools::FileIsFullPath(cleanPath)) {
             cleanPath = cmSystemTools::CollapseFullPath(cleanPath);
           }
-          bool isFramework =
+          bool isXcFramework =
+            cmHasSuffix(libName.GetFeatureName(), "XCFRAMEWORK"_s);
+          bool isFramework = !isXcFramework &&
             cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s);
           if (isFramework) {
             const auto fwDescriptor = this->SplitFrameworkPath(
               cleanPath, cmGlobalGenerator::FrameworkFormat::Extended);
-            if (!fwDescriptor->Directory.empty() &&
+            if (isFramework && !fwDescriptor->Directory.empty() &&
                 emitted.insert(fwDescriptor->Directory).second) {
               // This is a search path we had not added before and it isn't
               // an implicit search path, so we need it
@@ -3940,13 +3950,54 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
                                fwDescriptor->GetLinkName()))
                              .Value);
             }
+          } else if (isXcFramework) {
+            auto plist = cmParseXcFrameworkPlist(
+              cleanPath, *this->Makefiles.front(), libName.Value.Backtrace);
+            if (!plist) {
+              return;
+            }
+            if (auto const* library = plist->SelectSuitableLibrary(
+                  *this->Makefiles.front(), libName.Value.Backtrace)) {
+              auto libraryPath =
+                cmStrCat(cleanPath, '/', library->LibraryIdentifier, '/',
+                         library->LibraryPath);
+              if (auto const fwDescriptor = this->SplitFrameworkPath(
+                    libraryPath,
+                    cmGlobalGenerator::FrameworkFormat::Relaxed)) {
+                if (!fwDescriptor->Directory.empty() &&
+                    emitted.insert(fwDescriptor->Directory).second) {
+                  // This is a search path we had not added before and it
+                  // isn't an implicit search path, so we need it
+                  fwSearchPaths.Add(
+                    this->XCodeEscapePath(fwDescriptor->Directory));
+                }
+                libPaths.Add(cmStrCat(
+                  "-framework ",
+                  this->XCodeEscapePath(fwDescriptor->GetLinkName())));
+              } else {
+                libPaths.Add(
+                  libName.GetFormattedItem(this->XCodeEscapePath(libraryPath))
+                    .Value);
+                if (!library->HeadersPath.empty()) {
+                  this->AppendBuildSettingAttribute(
+                    target, "HEADER_SEARCH_PATHS",
+                    this->CreateString(this->XCodeEscapePath(
+                      cmStrCat(cleanPath, '/', library->LibraryIdentifier, '/',
+                               library->HeadersPath))),
+                    configName);
+                }
+              }
+            } else {
+              return;
+            }
           } else {
             libPaths.Add(
               libName.GetFormattedItem(this->XCodeEscapePath(cleanPath))
                 .Value);
           }
           if ((!libName.Target || libName.Target->IsImported()) &&
-              (isFramework || IsLinkPhaseLibraryExtension(cleanPath))) {
+              (isFramework || isXcFramework ||
+               IsLinkPhaseLibraryExtension(cleanPath))) {
             // Create file reference for embedding
             auto it = this->ExternalLibRefs.find(cleanPath);
             if (it == this->ExternalLibRefs.end()) {

+ 39 - 0
Source/cmLocalGenerator.cxx

@@ -1659,6 +1659,8 @@ std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags(
   this->AppendFlags(compileFlags, mf->GetDefineFlags());
   this->AppendFlags(compileFlags,
                     this->GetFrameworkFlags(lang, config, target));
+  this->AppendFlags(compileFlags,
+                    this->GetXcFrameworkFlags(lang, config, target));
 
   if (!compileFlags.empty()) {
     flags.emplace_back(std::move(compileFlags));
@@ -1724,6 +1726,43 @@ std::string cmLocalGenerator::GetFrameworkFlags(std::string const& lang,
   return flags;
 }
 
+std::string cmLocalGenerator::GetXcFrameworkFlags(std::string const& lang,
+                                                  std::string const& config,
+                                                  cmGeneratorTarget* target)
+{
+  cmLocalGenerator* lg = target->GetLocalGenerator();
+  cmMakefile* mf = lg->GetMakefile();
+
+  if (!target->IsApple()) {
+    return std::string();
+  }
+
+  cmValue includeSearchFlag =
+    mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
+  cmValue sysIncludeSearchFlag =
+    mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang));
+
+  if (!includeSearchFlag && !sysIncludeSearchFlag) {
+    return std::string{};
+  }
+
+  std::string flags;
+  if (cmComputeLinkInformation* cli = target->GetLinkInformation(config)) {
+    std::vector<std::string> const& paths = cli->GetXcFrameworkHeaderPaths();
+    for (std::string const& path : paths) {
+      if (sysIncludeSearchFlag &&
+          target->IsSystemIncludeDirectory(path, config, lang)) {
+        flags += *sysIncludeSearchFlag;
+      } else {
+        flags += *includeSearchFlag;
+      }
+      flags += lg->ConvertToOutputFormat(path, cmOutputConverter::SHELL);
+      flags += " ";
+    }
+  }
+  return flags;
+}
+
 void cmLocalGenerator::GetTargetDefines(cmGeneratorTarget const* target,
                                         std::string const& config,
                                         std::string const& lang,

+ 3 - 0
Source/cmLocalGenerator.h

@@ -520,6 +520,9 @@ public:
   std::string GetFrameworkFlags(std::string const& l,
                                 std::string const& config,
                                 cmGeneratorTarget* target);
+  std::string GetXcFrameworkFlags(std::string const& l,
+                                  std::string const& config,
+                                  cmGeneratorTarget* target);
   virtual std::string GetTargetFortranFlags(cmGeneratorTarget const* target,
                                             std::string const& config);
 

+ 33 - 0
Source/cmPlistParser.cxx

@@ -0,0 +1,33 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmPlistParser.h"
+
+#include <cm3p/json/reader.h>
+#include <cm3p/json/value.h>
+
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
+
+cm::optional<Json::Value> cmParsePlist(const std::string& filename)
+{
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(
+    { "/usr/bin/plutil", "-convert", "json", "-o", "-", filename });
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+  auto chain = builder.Start();
+  chain.Wait();
+
+  auto const& status = chain.GetStatus(0);
+  if (status.ExitStatus != 0) {
+    return cm::nullopt;
+  }
+
+  Json::Reader reader;
+  Json::Value value;
+  cmUVPipeIStream outputStream(chain.GetLoop(), chain.OutputStream());
+  if (!reader.parse(outputStream, value)) {
+    return cm::nullopt;
+  }
+  return cm::optional<Json::Value>(value);
+}

+ 13 - 0
Source/cmPlistParser.h

@@ -0,0 +1,13 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <string>
+
+#include <cm/optional>
+
+namespace Json {
+class Value;
+}
+
+cm::optional<Json::Value> cmParsePlist(const std::string& filename);

+ 6 - 0
Source/cmSystemTools.cxx

@@ -1900,6 +1900,12 @@ bool cmSystemTools::IsPathToFramework(const std::string& path)
           cmHasLiteralSuffix(path, ".framework"));
 }
 
+bool cmSystemTools::IsPathToXcFramework(const std::string& path)
+{
+  return (cmSystemTools::FileIsFullPath(path) &&
+          cmHasLiteralSuffix(path, ".xcframework"));
+}
+
 bool cmSystemTools::IsPathToMacOSSharedLibrary(const std::string& path)
 {
   return (cmSystemTools::FileIsFullPath(path) &&

+ 3 - 0
Source/cmSystemTools.h

@@ -111,6 +111,9 @@ public:
   //! Return true if the path is a framework
   static bool IsPathToFramework(const std::string& path);
 
+  //! Return true if the path is a xcframework
+  static bool IsPathToXcFramework(const std::string& path);
+
   //! Return true if the path is a macOS non-framework shared library (aka
   //! .dylib)
   static bool IsPathToMacOSSharedLibrary(const std::string& path);

+ 20 - 0
Source/cmTarget.cxx

@@ -40,6 +40,7 @@
 #include "cmSystemTools.h"
 #include "cmTargetPropertyComputer.h"
 #include "cmValue.h"
+#include "cmXcFramework.h"
 #include "cmake.h"
 
 template <>
@@ -2800,6 +2801,25 @@ std::string cmTarget::ImportedGetFullPath(
             }
           }
         }
+        if (this->IsApple() &&
+            (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
+             this->impl->TargetType == cmStateEnums::STATIC_LIBRARY ||
+             this->impl->TargetType == cmStateEnums::UNKNOWN_LIBRARY) &&
+            cmSystemTools::IsPathToXcFramework(result)) {
+          auto plist = cmParseXcFrameworkPlist(result, *this->impl->Makefile,
+                                               this->impl->Backtrace);
+          if (!plist) {
+            return "";
+          }
+          auto const* library = plist->SelectSuitableLibrary(
+            *this->impl->Makefile, this->impl->Backtrace);
+          if (library) {
+            result = cmStrCat(result, '/', library->LibraryIdentifier, '/',
+                              library->LibraryPath);
+          } else {
+            return "";
+          }
+        }
         break;
 
       case cmStateEnums::ImportLibraryArtifact:

+ 174 - 0
Source/cmXcFramework.cxx

@@ -0,0 +1,174 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmXcFramework.h"
+
+#include <functional>
+#include <string>
+
+#include <cmext/string_view>
+
+#include <cm3p/json/value.h>
+
+#include "cmJSONHelpers.h"
+#include "cmJSONState.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmPlistParser.h"
+#include "cmStringAlgorithms.h"
+#include "cmake.h"
+
+namespace {
+struct PlistMetadata
+{
+  std::string CFBundlePackageType;
+  std::string XCFrameworkFormatVersion;
+};
+
+auto const PlistMetadataHelper =
+  cmJSONHelperBuilder::Object<PlistMetadata>{}
+    .Bind("CFBundlePackageType"_s, &PlistMetadata::CFBundlePackageType,
+          cmJSONHelperBuilder::String())
+    .Bind("XCFrameworkFormatVersion"_s,
+          &PlistMetadata::XCFrameworkFormatVersion,
+          cmJSONHelperBuilder::String());
+
+bool PlistSupportedPlatformHelper(
+  cmXcFrameworkPlistSupportedPlatform& platform, const Json::Value* value,
+  cmJSONState* /*state*/)
+{
+  if (!value) {
+    return false;
+  }
+
+  if (!value->isString()) {
+    return false;
+  }
+
+  if (value->asString() == "macos") {
+    platform = cmXcFrameworkPlistSupportedPlatform::macOS;
+    return true;
+  }
+  if (value->asString() == "ios") {
+    platform = cmXcFrameworkPlistSupportedPlatform::iOS;
+    return true;
+  }
+  if (value->asString() == "tvos") {
+    platform = cmXcFrameworkPlistSupportedPlatform::tvOS;
+    return true;
+  }
+  if (value->asString() == "watchos") {
+    platform = cmXcFrameworkPlistSupportedPlatform::watchOS;
+    return true;
+  }
+  if (value->asString() == "xros") {
+    platform = cmXcFrameworkPlistSupportedPlatform::visionOS;
+    return true;
+  }
+
+  return false;
+}
+
+auto const PlistLibraryHelper =
+  cmJSONHelperBuilder::Object<cmXcFrameworkPlistLibrary>{}
+    .Bind("LibraryIdentifier"_s, &cmXcFrameworkPlistLibrary::LibraryIdentifier,
+          cmJSONHelperBuilder::String())
+    .Bind("LibraryPath"_s, &cmXcFrameworkPlistLibrary::LibraryPath,
+          cmJSONHelperBuilder::String())
+    .Bind("HeadersPath"_s, &cmXcFrameworkPlistLibrary::HeadersPath,
+          cmJSONHelperBuilder::String(), false)
+    .Bind("SupportedArchitectures"_s,
+          &cmXcFrameworkPlistLibrary::SupportedArchitectures,
+          cmJSONHelperBuilder::Vector<std::string>(
+            JsonErrors::EXPECTED_TYPE("array"), cmJSONHelperBuilder::String()))
+    .Bind("SupportedPlatform"_s, &cmXcFrameworkPlistLibrary::SupportedPlatform,
+          PlistSupportedPlatformHelper);
+
+auto const PlistHelper =
+  cmJSONHelperBuilder::Object<cmXcFrameworkPlist>{}.Bind(
+    "AvailableLibraries"_s, &cmXcFrameworkPlist::AvailableLibraries,
+    cmJSONHelperBuilder::Vector<cmXcFrameworkPlistLibrary>(
+      JsonErrors::EXPECTED_TYPE("array"), PlistLibraryHelper));
+}
+
+cm::optional<cmXcFrameworkPlist> cmParseXcFrameworkPlist(
+  const std::string& xcframeworkPath, const cmMakefile& mf,
+  const cmListFileBacktrace& bt)
+{
+  std::string plistPath = cmStrCat(xcframeworkPath, "/Info.plist");
+
+  auto value = cmParsePlist(plistPath);
+  if (!value) {
+    mf.GetCMakeInstance()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Unable to parse plist file:\n  ", plistPath), bt);
+    return cm::nullopt;
+  }
+
+  cmJSONState state;
+
+  PlistMetadata metadata;
+  if (!PlistMetadataHelper(metadata, &*value, &state)) {
+    mf.GetCMakeInstance()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Invalid xcframework .plist file:\n  ", plistPath), bt);
+    return cm::nullopt;
+  }
+  if (metadata.CFBundlePackageType != "XFWK" ||
+      metadata.XCFrameworkFormatVersion != "1.0") {
+    mf.GetCMakeInstance()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Expected:\n  ", plistPath,
+               "\nto have CFBundlePackageType \"XFWK\" and "
+               "XCFrameworkFormatVersion \"1.0\""),
+      bt);
+    return cm::nullopt;
+  }
+
+  cmXcFrameworkPlist plist;
+  if (!PlistHelper(plist, &*value, &state)) {
+    mf.GetCMakeInstance()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Invalid xcframework .plist file:\n  ", plistPath), bt);
+    return cm::nullopt;
+  }
+  plist.Path = plistPath;
+  return cm::optional<cmXcFrameworkPlist>(plist);
+}
+
+const cmXcFrameworkPlistLibrary* cmXcFrameworkPlist::SelectSuitableLibrary(
+  const cmMakefile& mf, const cmListFileBacktrace& bt) const
+{
+  auto systemName = mf.GetSafeDefinition("CMAKE_SYSTEM_NAME");
+
+  for (auto const& lib : this->AvailableLibraries) {
+    std::string supportedSystemName;
+    switch (lib.SupportedPlatform) {
+      case cmXcFrameworkPlistSupportedPlatform::macOS:
+        supportedSystemName = "Darwin";
+        break;
+      case cmXcFrameworkPlistSupportedPlatform::iOS:
+        supportedSystemName = "iOS";
+        break;
+      case cmXcFrameworkPlistSupportedPlatform::tvOS:
+        supportedSystemName = "tvOS";
+        break;
+      case cmXcFrameworkPlistSupportedPlatform::watchOS:
+        supportedSystemName = "watchOS";
+        break;
+      case cmXcFrameworkPlistSupportedPlatform::visionOS:
+        supportedSystemName = "visionOS";
+        break;
+    }
+
+    if (systemName == supportedSystemName) {
+      return &lib;
+    }
+  }
+
+  mf.GetCMakeInstance()->IssueMessage(
+    MessageType::FATAL_ERROR,
+    cmStrCat("Unable to find suitable library in:\n  ", this->Path,
+             "\nfor system name \"", systemName, '"'),
+    bt);
+  return nullptr;
+}

+ 44 - 0
Source/cmXcFramework.h

@@ -0,0 +1,44 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <cm/optional>
+
+#include "cmListFileCache.h"
+
+class cmMakefile;
+
+enum class cmXcFrameworkPlistSupportedPlatform
+{
+  macOS,
+  iOS,
+  tvOS,
+  watchOS,
+  visionOS,
+};
+
+struct cmXcFrameworkPlistLibrary
+{
+  std::string LibraryIdentifier;
+  std::string LibraryPath;
+  std::string HeadersPath;
+  std::vector<std::string> SupportedArchitectures;
+  cmXcFrameworkPlistSupportedPlatform SupportedPlatform;
+};
+
+struct cmXcFrameworkPlist
+{
+  std::string Path;
+  std::vector<cmXcFrameworkPlistLibrary> AvailableLibraries;
+
+  const cmXcFrameworkPlistLibrary* SelectSuitableLibrary(
+    const cmMakefile& mf,
+    const cmListFileBacktrace& bt = cmListFileBacktrace{}) const;
+};
+
+cm::optional<cmXcFrameworkPlist> cmParseXcFrameworkPlist(
+  const std::string& xcframeworkPath, const cmMakefile& mf,
+  const cmListFileBacktrace& bt = cmListFileBacktrace{});

+ 17 - 0
Tests/RunCMake/CMakeLists.txt

@@ -703,6 +703,23 @@ endif()
 if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
     AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.0)
   add_RunCMake_test(Framework)
+  if(NOT DEFINED CMake_TEST_XcFramework)
+    set(CMake_TEST_XcFramework ON)
+  endif()
+  if(CMake_TEST_XcFramework AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 11.0)
+    set(XcFramework_ARGS -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION})
+    add_RunCMake_test(XcFramework)
+
+    # This test can take a very long time due to lots of combinations.
+    # Use a long default timeout and provide an option to customize it.
+    if(NOT DEFINED CMake_TEST_XcFramework_TIMEOUT)
+      set(CMake_TEST_XcFramework_TIMEOUT 3000)
+    endif()
+    set_tests_properties(RunCMake.XcFramework PROPERTIES
+      TIMEOUT "${CMake_TEST_XcFramework_TIMEOUT}"
+      RUN_SERIAL TRUE
+      )
+  endif()
 endif()
 
 add_RunCMake_test(File_Archive)

+ 3 - 0
Tests/RunCMake/XcFramework/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.26)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 83 - 0
Tests/RunCMake/XcFramework/RunCMakeTest.cmake

@@ -0,0 +1,83 @@
+include(RunCMake)
+
+function(create_library type platform system_name archs)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/create-${type}-${platform}-build)
+  run_cmake_with_options(create-${type}-${platform} -DCMAKE_SYSTEM_NAME=${system_name} -DCMAKE_OSX_ARCHITECTURES=${archs} -DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_BINARY_DIR}/install)
+
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(create-${type}-${platform}-build ${CMAKE_COMMAND} --build . --config Release)
+  run_cmake_command(create-${type}-${platform}-install ${CMAKE_COMMAND} --install . --config Release)
+endfunction()
+
+function(create_libraries type)
+  create_library(${type} macos Darwin "${macos_archs_2}")
+  create_library(${type} ios iOS "arm64")
+  create_library(${type} tvos tvOS "arm64")
+  create_library(${type} watchos watchOS "armv7k\\\\;arm64_32")
+  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15)
+    create_library(${type} visionos visionOS "arm64")
+  endif()
+endfunction()
+
+function(create_xcframework name type platforms)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/create-xcframework-${name}-build)
+  set(args)
+  foreach(platform IN LISTS platforms)
+    if(type STREQUAL "framework")
+      list(APPEND args -framework ${RunCMake_BINARY_DIR}/create-${type}-${platform}-build/install/lib/mylib.framework)
+    else()
+      list(APPEND args -library ${RunCMake_BINARY_DIR}/create-${type}-${platform}-build/install/lib/libmylib.a -headers ${RunCMake_SOURCE_DIR}/mylib/include)
+    endif()
+  endforeach()
+  run_cmake_command(create-xcframework-${name} xcodebuild -create-xcframework ${args} -output ${RunCMake_TEST_BINARY_DIR}/mylib.xcframework)
+endfunction()
+
+function(create_executable name xcfname system_name archs)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/create-executable-${name}-build)
+  run_cmake_with_options(create-executable-${name} -DCMAKE_SYSTEM_NAME=${system_name} -DCMAKE_OSX_ARCHITECTURES=${archs} -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-${xcfname}-build/mylib.xcframework)
+
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(create-executable-${name}-build ${CMAKE_COMMAND} --build . --config Release)
+endfunction()
+
+function(create_executables name type)
+  create_executable(${name}-macos ${type} Darwin "${macos_archs_2}")
+  create_executable(${name}-ios ${type} iOS "arm64")
+  create_executable(${name}-tvos ${type} tvOS "arm64")
+  create_executable(${name}-watchos ${type} watchOS "armv7k\\\\;arm64_32")
+  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15)
+    create_executable(${name}-visionos ${type} visionOS "arm64")
+  endif()
+endfunction()
+
+set(xcframework_platforms macos ios tvos watchos)
+if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15)
+  list(APPEND xcframework_platforms visionos)
+endif()
+if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12)
+  set(macos_archs_1 "x86_64\\;arm64")
+  set(macos_archs_2 "x86_64\\\\;arm64")
+else()
+  set(macos_archs_1 "x86_64")
+  set(macos_archs_2 "x86_64")
+endif()
+
+create_libraries(library)
+create_libraries(framework)
+create_xcframework(library library "${xcframework_platforms}")
+create_xcframework(framework framework "${xcframework_platforms}")
+create_xcframework(incomplete framework "tvos;watchos")
+create_executables(library library)
+create_executables(framework framework)
+run_cmake_with_options(create-executable-incomplete -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework)
+create_executables(target-library library)
+create_executables(target-framework framework)
+run_cmake_with_options(create-executable-target-incomplete -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework)
+if(RunCMake_GENERATOR STREQUAL "Xcode" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12)
+  create_executables(library-link-phase library)
+  create_executables(framework-link-phase framework)
+  run_cmake_with_options(create-executable-incomplete-link-phase -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework)
+  create_executables(target-library-link-phase library)
+  create_executables(target-framework-link-phase framework)
+  run_cmake_with_options(create-executable-target-incomplete-link-phase -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework)
+endif()

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-ios.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-link-phase-macos.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-link-phase-tvos.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-link-phase-visionos.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-link-phase-watchos.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-macos.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-tvos.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-visionos.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-framework-watchos.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase-result.txt

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

+ 11 - 0
Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase-stderr.txt

@@ -0,0 +1,11 @@
+CMake Error at create-executable\.cmake:[0-9]+ \(target_link_libraries\):
+  Unable to find suitable library in:
+
+    [^
+]*/Tests/RunCMake/XcFramework/create-xcframework-incomplete-build/mylib\.xcframework/Info\.plist
+
+  for system name "Darwin"
+Call Stack \(most recent call first\):
+  create-executable-link-phase\.cmake:[0-9]+ \(include\)
+  create-executable-incomplete-link-phase\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-incomplete-result.txt

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

+ 10 - 0
Tests/RunCMake/XcFramework/create-executable-incomplete-stderr.txt

@@ -0,0 +1,10 @@
+CMake Error at create-executable\.cmake:[0-9]+ \(target_link_libraries\):
+  Unable to find suitable library in:
+
+    [^
+]*/Tests/RunCMake/XcFramework/create-xcframework-incomplete-build/mylib\.xcframework/Info\.plist
+
+  for system name "Darwin"
+Call Stack \(most recent call first\):
+  create-executable-incomplete\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-incomplete.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-ios.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-link-phase-macos.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-link-phase-tvos.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-link-phase-visionos.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-link-phase-watchos.cmake

@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-macos.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-tvos.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-visionos.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-library-watchos.cmake

@@ -0,0 +1 @@
+include(create-executable.cmake)

+ 2 - 0
Tests/RunCMake/XcFramework/create-executable-link-phase.cmake

@@ -0,0 +1,2 @@
+include(create-executable.cmake)
+set_property(TARGET myexe PROPERTY XCODE_LINK_BUILD_PHASE_MODE "KNOWN_LOCATION")

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-ios.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-macos.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-tvos.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-visionos.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-watchos.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-macos.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-tvos.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-visionos.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-framework-watchos.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase-result.txt

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

+ 11 - 0
Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase-stderr.txt

@@ -0,0 +1,11 @@
+CMake Error at create-executable-target\.cmake:[0-9]+ \(target_link_libraries\):
+  Unable to find suitable library in:
+
+    [^
+]*/Tests/RunCMake/XcFramework/create-xcframework-incomplete-build/mylib\.xcframework/Info\.plist
+
+  for system name "Darwin"
+Call Stack \(most recent call first\):
+  create-executable-target-link-phase\.cmake:[0-9]+ \(include\)
+  create-executable-target-incomplete-link-phase\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-incomplete-result.txt

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

+ 10 - 0
Tests/RunCMake/XcFramework/create-executable-target-incomplete-stderr.txt

@@ -0,0 +1,10 @@
+CMake Error at create-executable-target\.cmake:[0-9]+ \(target_link_libraries\):
+  Unable to find suitable library in:
+
+    [^
+]*/Tests/RunCMake/XcFramework/create-xcframework-incomplete-build/mylib\.xcframework/Info\.plist
+
+  for system name "Darwin"
+Call Stack \(most recent call first\):
+  create-executable-target-incomplete\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-incomplete.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-ios.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-macos.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-tvos.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-visionos.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-watchos.cmake

@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-macos.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-tvos.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-visionos.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-executable-target-library-watchos.cmake

@@ -0,0 +1 @@
+include(create-executable-target.cmake)

+ 2 - 0
Tests/RunCMake/XcFramework/create-executable-target-link-phase.cmake

@@ -0,0 +1,2 @@
+include(create-executable-target.cmake)
+set_property(TARGET myexe PROPERTY XCODE_LINK_BUILD_PHASE_MODE "KNOWN_LOCATION")

+ 21 - 0
Tests/RunCMake/XcFramework/create-executable-target.cmake

@@ -0,0 +1,21 @@
+enable_language(C)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
+endif()
+
+add_library(mylib IMPORTED STATIC)
+set_property(TARGET mylib PROPERTY IMPORTED_LOCATION ${MYLIB_LIBRARY})
+
+add_executable(myexe myexe/myexe.c)
+target_link_libraries(myexe PRIVATE mylib)

+ 18 - 0
Tests/RunCMake/XcFramework/create-executable.cmake

@@ -0,0 +1,18 @@
+enable_language(C)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
+endif()
+
+add_executable(myexe myexe/myexe.c)
+target_link_libraries(myexe PRIVATE ${MYLIB_LIBRARY})

+ 1 - 0
Tests/RunCMake/XcFramework/create-framework-ios.cmake

@@ -0,0 +1 @@
+include(create-framework.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-framework-macos.cmake

@@ -0,0 +1 @@
+include(create-framework.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-framework-tvos.cmake

@@ -0,0 +1 @@
+include(create-framework.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-framework-visionos.cmake

@@ -0,0 +1 @@
+include(create-framework.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-framework-watchos.cmake

@@ -0,0 +1 @@
+include(create-framework.cmake)

+ 3 - 0
Tests/RunCMake/XcFramework/create-framework.cmake

@@ -0,0 +1,3 @@
+set(CMAKE_FRAMEWORK ON)
+include(create-library-common.cmake)
+install(FILES mylib/include/mylib/mylib.h DESTINATION lib/mylib.framework/Headers)

+ 12 - 0
Tests/RunCMake/XcFramework/create-library-common.cmake

@@ -0,0 +1,12 @@
+enable_language(C)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
+endif()
+
+add_library(mylib STATIC mylib/mylib.c)
+install(TARGETS mylib DESTINATION lib)

+ 1 - 0
Tests/RunCMake/XcFramework/create-library-ios.cmake

@@ -0,0 +1 @@
+include(create-library.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-library-macos.cmake

@@ -0,0 +1 @@
+include(create-library.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-library-tvos.cmake

@@ -0,0 +1 @@
+include(create-library.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-library-visionos.cmake

@@ -0,0 +1 @@
+include(create-library.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-library-watchos.cmake

@@ -0,0 +1 @@
+include(create-library.cmake)

+ 1 - 0
Tests/RunCMake/XcFramework/create-library.cmake

@@ -0,0 +1 @@
+include(create-library-common.cmake)

+ 7 - 0
Tests/RunCMake/XcFramework/myexe/myexe.c

@@ -0,0 +1,7 @@
+#include <mylib/mylib.h>
+
+int main(void)
+{
+  mylib();
+  return 0;
+}

+ 3 - 0
Tests/RunCMake/XcFramework/mylib/include/mylib/mylib.h

@@ -0,0 +1,3 @@
+#pragma once
+
+extern void mylib(void);

+ 3 - 0
Tests/RunCMake/XcFramework/mylib/mylib.c

@@ -0,0 +1,3 @@
+void mylib(void)
+{
+}

+ 2 - 0
bootstrap

@@ -451,6 +451,7 @@ CMAKE_CXX_SOURCES="\
   cmGccDepfileReader \
   cmReturnCommand \
   cmPlaceholderExpander \
+  cmPlistParser \
   cmRulePlaceholderExpander \
   cmRuntimeDependencyArchive \
   cmScriptGenerator \
@@ -500,6 +501,7 @@ CMAKE_CXX_SOURCES="\
   cmWhileCommand \
   cmWindowsRegistry \
   cmWorkingDirectory \
+  cmXcFramework \
   cmake  \
   cmakemain \
   cmcmd  \