Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmXcFramework.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmXcFramework.h"
4
5
#include <string>
6
7
#include <cm/string_view>
8
#include <cmext/string_view>
9
10
#include <cm3p/json/value.h>
11
12
#include "cmGlobalGenerator.h"
13
#include "cmJSONHelpers.h"
14
#include "cmJSONState.h"
15
#include "cmMakefile.h"
16
#include "cmMessageType.h"
17
#include "cmPlistParser.h"
18
#include "cmStringAlgorithms.h"
19
#include "cmake.h"
20
21
namespace {
22
struct PlistMetadata
23
{
24
  std::string CFBundlePackageType;
25
  std::string XCFrameworkFormatVersion;
26
};
27
28
auto const PlistMetadataHelper =
29
  cmJSONHelperBuilder::Object<PlistMetadata>{}
30
    .Bind("CFBundlePackageType"_s, &PlistMetadata::CFBundlePackageType,
31
          cmJSONHelperBuilder::String())
32
    .Bind("XCFrameworkFormatVersion"_s,
33
          &PlistMetadata::XCFrameworkFormatVersion,
34
          cmJSONHelperBuilder::String());
35
36
bool PlistSupportedPlatformHelper(
37
  cmXcFrameworkPlistSupportedPlatform& platform, Json::Value const* value,
38
  cmJSONState* /*state*/)
39
0
{
40
0
  if (!value) {
41
0
    return false;
42
0
  }
43
44
0
  if (!value->isString()) {
45
0
    return false;
46
0
  }
47
48
0
  if (value->asString() == "macos"_s) {
49
0
    platform = cmXcFrameworkPlistSupportedPlatform::macOS;
50
0
    return true;
51
0
  }
52
0
  if (value->asString() == "ios"_s) {
53
0
    platform = cmXcFrameworkPlistSupportedPlatform::iOS;
54
0
    return true;
55
0
  }
56
0
  if (value->asString() == "tvos"_s) {
57
0
    platform = cmXcFrameworkPlistSupportedPlatform::tvOS;
58
0
    return true;
59
0
  }
60
0
  if (value->asString() == "watchos"_s) {
61
0
    platform = cmXcFrameworkPlistSupportedPlatform::watchOS;
62
0
    return true;
63
0
  }
64
0
  if (value->asString() == "xros"_s) {
65
0
    platform = cmXcFrameworkPlistSupportedPlatform::visionOS;
66
0
    return true;
67
0
  }
68
69
0
  return false;
70
0
}
71
72
bool PlistSupportedPlatformVariantHelper(
73
  cmXcFrameworkPlistSupportedPlatformVariant& variant,
74
  Json::Value const* value, cmJSONState* /*state*/)
75
0
{
76
0
  if (!value) {
77
0
    return false;
78
0
  }
79
80
0
  if (!value->isString()) {
81
0
    return false;
82
0
  }
83
84
0
  if (value->asString() == "maccatalyst"_s) {
85
0
    variant = cmXcFrameworkPlistSupportedPlatformVariant::maccatalyst;
86
0
    return true;
87
0
  }
88
0
  if (value->asString() == "simulator"_s) {
89
0
    variant = cmXcFrameworkPlistSupportedPlatformVariant::simulator;
90
0
    return true;
91
0
  }
92
93
0
  return false;
94
0
}
95
96
auto const PlistLibraryHelper =
97
  cmJSONHelperBuilder::Object<cmXcFrameworkPlistLibrary>{}
98
    .Bind("LibraryIdentifier"_s, &cmXcFrameworkPlistLibrary::LibraryIdentifier,
99
          cmJSONHelperBuilder::String())
100
    .Bind("LibraryPath"_s, &cmXcFrameworkPlistLibrary::LibraryPath,
101
          cmJSONHelperBuilder::String())
102
    .Bind("HeadersPath"_s, &cmXcFrameworkPlistLibrary::HeadersPath,
103
          cmJSONHelperBuilder::String(), false)
104
    .Bind("SupportedArchitectures"_s,
105
          &cmXcFrameworkPlistLibrary::SupportedArchitectures,
106
          cmJSONHelperBuilder::Vector<std::string>(
107
            JsonErrors::EXPECTED_TYPE("array"), cmJSONHelperBuilder::String()))
108
    .Bind("SupportedPlatform"_s, &cmXcFrameworkPlistLibrary::SupportedPlatform,
109
          PlistSupportedPlatformHelper)
110
    .Bind("SupportedPlatformVariant"_s,
111
          &cmXcFrameworkPlistLibrary::SupportedPlatformVariant,
112
          cmJSONHelperBuilder::Optional<
113
            cmXcFrameworkPlistSupportedPlatformVariant>(
114
            PlistSupportedPlatformVariantHelper),
115
          false);
116
117
auto const PlistHelper =
118
  cmJSONHelperBuilder::Object<cmXcFrameworkPlist>{}.Bind(
119
    "AvailableLibraries"_s, &cmXcFrameworkPlist::AvailableLibraries,
120
    cmJSONHelperBuilder::Vector<cmXcFrameworkPlistLibrary>(
121
      JsonErrors::EXPECTED_TYPE("array"), PlistLibraryHelper));
122
}
123
124
cm::optional<cmXcFrameworkPlist> cmParseXcFrameworkPlist(
125
  std::string const& xcframeworkPath, cmMakefile const& mf,
126
  cmListFileBacktrace const& bt)
127
0
{
128
0
  cmGlobalGenerator* gen = mf.GetGlobalGenerator();
129
0
  if (cm::optional<cmXcFrameworkPlist> cached =
130
0
        gen->GetXcFrameworkPListContent(xcframeworkPath)) {
131
0
    return cached;
132
0
  }
133
134
0
  std::string plistPath = cmStrCat(xcframeworkPath, "/Info.plist");
135
136
0
  auto value = cmParsePlist(plistPath);
137
0
  if (!value) {
138
0
    mf.GetCMakeInstance()->IssueMessage(
139
0
      MessageType::FATAL_ERROR,
140
0
      cmStrCat("Unable to parse plist file:\n  ", plistPath), bt);
141
0
    return cm::nullopt;
142
0
  }
143
144
0
  cmJSONState state;
145
146
0
  PlistMetadata metadata;
147
0
  if (!PlistMetadataHelper(metadata, &*value, &state)) {
148
0
    mf.GetCMakeInstance()->IssueMessage(
149
0
      MessageType::FATAL_ERROR,
150
0
      cmStrCat("Invalid xcframework .plist file:\n  ", plistPath), bt);
151
0
    return cm::nullopt;
152
0
  }
153
0
  if (metadata.CFBundlePackageType != "XFWK"_s ||
154
0
      metadata.XCFrameworkFormatVersion != "1.0"_s) {
155
0
    mf.GetCMakeInstance()->IssueMessage(
156
0
      MessageType::FATAL_ERROR,
157
0
      cmStrCat("Expected:\n  ", plistPath,
158
0
               "\nto have CFBundlePackageType \"XFWK\" and "
159
0
               "XCFrameworkFormatVersion \"1.0\""),
160
0
      bt);
161
0
    return cm::nullopt;
162
0
  }
163
164
0
  cmXcFrameworkPlist plist;
165
0
  if (!PlistHelper(plist, &*value, &state)) {
166
0
    mf.GetCMakeInstance()->IssueMessage(
167
0
      MessageType::FATAL_ERROR,
168
0
      cmStrCat("Invalid xcframework .plist file:\n  ", plistPath), bt);
169
0
    return cm::nullopt;
170
0
  }
171
0
  plist.Path = plistPath;
172
0
  gen->SetXcFrameworkPListContent(xcframeworkPath, plist);
173
0
  return cm::optional<cmXcFrameworkPlist>(plist);
174
0
}
175
176
cmXcFrameworkPlistLibrary const* cmXcFrameworkPlist::SelectSuitableLibrary(
177
  cmMakefile const& mf, cmListFileBacktrace const& bt) const
178
0
{
179
0
  auto systemName = mf.GetSafeDefinition("CMAKE_SYSTEM_NAME");
180
0
  cm::optional<cmXcFrameworkPlistSupportedPlatformVariant> systemVariant;
181
0
  if (mf.PlatformIsAppleSimulator()) {
182
0
    systemVariant = cmXcFrameworkPlistSupportedPlatformVariant::simulator;
183
0
  }
184
0
  if (mf.PlatformIsAppleCatalyst()) {
185
0
    systemVariant = cmXcFrameworkPlistSupportedPlatformVariant::maccatalyst;
186
0
  }
187
188
0
  for (auto const& lib : this->AvailableLibraries) {
189
0
    std::string supportedSystemName;
190
0
    switch (lib.SupportedPlatform) {
191
0
      case cmXcFrameworkPlistSupportedPlatform::macOS:
192
0
        supportedSystemName = "Darwin";
193
0
        break;
194
0
      case cmXcFrameworkPlistSupportedPlatform::iOS:
195
0
        supportedSystemName = "iOS";
196
0
        break;
197
0
      case cmXcFrameworkPlistSupportedPlatform::tvOS:
198
0
        supportedSystemName = "tvOS";
199
0
        break;
200
0
      case cmXcFrameworkPlistSupportedPlatform::watchOS:
201
0
        supportedSystemName = "watchOS";
202
0
        break;
203
0
      case cmXcFrameworkPlistSupportedPlatform::visionOS:
204
0
        supportedSystemName = "visionOS";
205
0
        break;
206
0
    }
207
208
0
    if (systemName == supportedSystemName &&
209
0
        systemVariant == lib.SupportedPlatformVariant) {
210
0
      return &lib;
211
0
    }
212
0
  }
213
214
0
  mf.GetCMakeInstance()->IssueMessage(
215
0
    MessageType::FATAL_ERROR,
216
0
    cmStrCat("Unable to find suitable library in:\n  ", this->Path,
217
0
             "\nfor system name \"", systemName, '"'),
218
0
    bt);
219
0
  return nullptr;
220
0
}