summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorKyle Edwards <kyle.edwards@kitware.com>2023-05-17 17:50:32 (GMT)
committerKyle Edwards <kyle.edwards@kitware.com>2023-07-26 21:00:01 (GMT)
commit7050ac56a11768c90f55654aa3f63d02bb549243 (patch)
treea436ee5be767bfd776dc93d747abd122a94f3a34 /Source
parent93ed53790cb1e2d5f25f26156ee5c6590b0d3150 (diff)
downloadCMake-7050ac56a11768c90f55654aa3f63d02bb549243.zip
CMake-7050ac56a11768c90f55654aa3f63d02bb549243.tar.gz
CMake-7050ac56a11768c90f55654aa3f63d02bb549243.tar.bz2
macOS: Add support for linking against .xcframework folders
Issue: #21752
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt4
-rw-r--r--Source/cmComputeLinkInformation.cxx88
-rw-r--r--Source/cmComputeLinkInformation.h5
-rw-r--r--Source/cmGeneratorTarget.cxx41
-rw-r--r--Source/cmGeneratorTarget.h2
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx63
-rw-r--r--Source/cmLocalGenerator.cxx39
-rw-r--r--Source/cmLocalGenerator.h3
-rw-r--r--Source/cmPlistParser.cxx33
-rw-r--r--Source/cmPlistParser.h13
-rw-r--r--Source/cmSystemTools.cxx6
-rw-r--r--Source/cmSystemTools.h3
-rw-r--r--Source/cmTarget.cxx20
-rw-r--r--Source/cmXcFramework.cxx174
-rw-r--r--Source/cmXcFramework.h44
15 files changed, 532 insertions, 6 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 708aec7..a067766 100644
--- a/Source/CMakeLists.txt
+++ b/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
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index a50ce11..be73fa3 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/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
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 8393a29..e75423f 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/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();
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 42be082..cff1574 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/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);
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index a03513d..753d6f3 100644
--- a/Source/cmGeneratorTarget.h
+++ b/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);
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 0472631..247d4fc 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/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()) {
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index cf1eb96..acefedc 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/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,
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index c811408..7734cbe 100644
--- a/Source/cmLocalGenerator.h
+++ b/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);
diff --git a/Source/cmPlistParser.cxx b/Source/cmPlistParser.cxx
new file mode 100644
index 0000000..ce3c171
--- /dev/null
+++ b/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);
+}
diff --git a/Source/cmPlistParser.h b/Source/cmPlistParser.h
new file mode 100644
index 0000000..2ace254
--- /dev/null
+++ b/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);
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 1fb0079..f014de9 100644
--- a/Source/cmSystemTools.cxx
+++ b/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) &&
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 7d55d4b..6592f84 100644
--- a/Source/cmSystemTools.h
+++ b/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);
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 1281bc6..76a14b8 100644
--- a/Source/cmTarget.cxx
+++ b/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:
diff --git a/Source/cmXcFramework.cxx b/Source/cmXcFramework.cxx
new file mode 100644
index 0000000..c91e7f2
--- /dev/null
+++ b/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;
+}
diff --git a/Source/cmXcFramework.h b/Source/cmXcFramework.h
new file mode 100644
index 0000000..c35df11
--- /dev/null
+++ b/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{});