1//===- InterfaceFile.cpp --------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Implements the Interface File.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/TextAPI/InterfaceFile.h"
14#include "llvm/TextAPI/RecordsSlice.h"
15#include "llvm/TextAPI/TextAPIError.h"
16#include <iomanip>
17#include <sstream>
18
19using namespace llvm;
20using namespace llvm::MachO;
21
22void InterfaceFileRef::addTarget(const Target &Target) {
23 addEntry(Container&: Targets, Targ: Target);
24}
25
26void InterfaceFile::addAllowableClient(StringRef InstallName,
27 const Target &Target) {
28 if (InstallName.empty())
29 return;
30 auto Client = addEntry(Container&: AllowableClients, InstallName);
31 Client->addTarget(Target);
32}
33
34void InterfaceFile::addReexportedLibrary(StringRef InstallName,
35 const Target &Target) {
36 if (InstallName.empty())
37 return;
38 auto Lib = addEntry(Container&: ReexportedLibraries, InstallName);
39 Lib->addTarget(Target);
40}
41
42void InterfaceFile::addParentUmbrella(const Target &Target_, StringRef Parent) {
43 if (Parent.empty())
44 return;
45 auto Iter = lower_bound(Range&: ParentUmbrellas, Value: Target_,
46 C: [](const std::pair<Target, std::string> &LHS,
47 Target RHS) { return LHS.first < RHS; });
48
49 if ((Iter != ParentUmbrellas.end()) && !(Target_ < Iter->first)) {
50 Iter->second = std::string(Parent);
51 return;
52 }
53
54 ParentUmbrellas.emplace(position: Iter, args: Target_, args: std::string(Parent));
55}
56
57void InterfaceFile::addRPath(StringRef RPath, const Target &InputTarget) {
58 if (RPath.empty())
59 return;
60 using RPathEntryT = const std::pair<Target, std::string>;
61 RPathEntryT Entry(InputTarget, RPath);
62 auto Iter =
63 lower_bound(Range&: RPaths, Value: Entry,
64 C: [](RPathEntryT &LHS, RPathEntryT &RHS) { return LHS < RHS; });
65
66 if ((Iter != RPaths.end()) && (*Iter == Entry))
67 return;
68
69 RPaths.emplace(position: Iter, args: Entry);
70}
71
72void InterfaceFile::addTarget(const Target &Target) {
73 addEntry(Container&: Targets, Targ: Target);
74}
75
76InterfaceFile::const_filtered_target_range
77InterfaceFile::targets(ArchitectureSet Archs) const {
78 std::function<bool(const Target &)> fn = [Archs](const Target &Target_) {
79 return Archs.has(Arch: Target_.Arch);
80 };
81 return make_filter_range(Range: Targets, Pred: fn);
82}
83
84void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
85 auto Pos = llvm::lower_bound(Range&: Documents, Value&: Document,
86 C: [](const std::shared_ptr<InterfaceFile> &LHS,
87 const std::shared_ptr<InterfaceFile> &RHS) {
88 return LHS->InstallName < RHS->InstallName;
89 });
90 Document->Parent = this;
91 Documents.insert(position: Pos, x: Document);
92}
93
94void InterfaceFile::inlineLibrary(std::shared_ptr<InterfaceFile> Library,
95 bool Overwrite) {
96 auto AddFwk = [&](std::shared_ptr<InterfaceFile> &&Reexport) {
97 auto It = lower_bound(
98 Range&: Documents, Value: Reexport->getInstallName(),
99 C: [](std::shared_ptr<InterfaceFile> &Lhs, const StringRef Rhs) {
100 return Lhs->getInstallName() < Rhs;
101 });
102
103 if (Overwrite && It != Documents.end() &&
104 Reexport->getInstallName() == (*It)->getInstallName()) {
105 std::replace(first: Documents.begin(), last: Documents.end(), old_value: *It,
106 new_value: std::move(Reexport));
107 return;
108 }
109
110 if ((It != Documents.end()) &&
111 !(Reexport->getInstallName() < (*It)->getInstallName()))
112 return;
113
114 Documents.emplace(position: It, args: std::move(Reexport));
115 };
116 for (auto Doc : Library->documents())
117 AddFwk(std::move(Doc));
118
119 Library->Documents.clear();
120 AddFwk(std::move(Library));
121}
122
123Expected<std::unique_ptr<InterfaceFile>>
124InterfaceFile::merge(const InterfaceFile *O) const {
125 // Verify files can be merged.
126 if (getInstallName() != O->getInstallName()) {
127 return make_error<StringError>(Args: "install names do not match",
128 Args: inconvertibleErrorCode());
129 }
130
131 if (getCurrentVersion() != O->getCurrentVersion()) {
132 return make_error<StringError>(Args: "current versions do not match",
133 Args: inconvertibleErrorCode());
134 }
135
136 if (getCompatibilityVersion() != O->getCompatibilityVersion()) {
137 return make_error<StringError>(Args: "compatibility versions do not match",
138 Args: inconvertibleErrorCode());
139 }
140
141 if ((getSwiftABIVersion() != 0) && (O->getSwiftABIVersion() != 0) &&
142 (getSwiftABIVersion() != O->getSwiftABIVersion())) {
143 return make_error<StringError>(Args: "swift ABI versions do not match",
144 Args: inconvertibleErrorCode());
145 }
146
147 if (isTwoLevelNamespace() != O->isTwoLevelNamespace()) {
148 return make_error<StringError>(Args: "two level namespace flags do not match",
149 Args: inconvertibleErrorCode());
150 }
151
152 if (isApplicationExtensionSafe() != O->isApplicationExtensionSafe()) {
153 return make_error<StringError>(
154 Args: "application extension safe flags do not match",
155 Args: inconvertibleErrorCode());
156 }
157
158 std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
159 IF->setFileType(std::max(a: getFileType(), b: O->getFileType()));
160 IF->setPath(getPath());
161 IF->setInstallName(getInstallName());
162 IF->setCurrentVersion(getCurrentVersion());
163 IF->setCompatibilityVersion(getCompatibilityVersion());
164
165 if (getSwiftABIVersion() == 0)
166 IF->setSwiftABIVersion(O->getSwiftABIVersion());
167 else
168 IF->setSwiftABIVersion(getSwiftABIVersion());
169
170 IF->setTwoLevelNamespace(isTwoLevelNamespace());
171 IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
172
173 for (const auto &It : umbrellas()) {
174 if (!It.second.empty())
175 IF->addParentUmbrella(Target_: It.first, Parent: It.second);
176 }
177 for (const auto &It : O->umbrellas()) {
178 if (!It.second.empty())
179 IF->addParentUmbrella(Target_: It.first, Parent: It.second);
180 }
181 IF->addTargets(Targets: targets());
182 IF->addTargets(Targets: O->targets());
183
184 for (const auto &Lib : allowableClients())
185 for (const auto &Target : Lib.targets())
186 IF->addAllowableClient(InstallName: Lib.getInstallName(), Target);
187
188 for (const auto &Lib : O->allowableClients())
189 for (const auto &Target : Lib.targets())
190 IF->addAllowableClient(InstallName: Lib.getInstallName(), Target);
191
192 for (const auto &Lib : reexportedLibraries())
193 for (const auto &Target : Lib.targets())
194 IF->addReexportedLibrary(InstallName: Lib.getInstallName(), Target);
195
196 for (const auto &Lib : O->reexportedLibraries())
197 for (const auto &Target : Lib.targets())
198 IF->addReexportedLibrary(InstallName: Lib.getInstallName(), Target);
199
200 for (const auto &[Target, Path] : rpaths())
201 IF->addRPath(RPath: Path, InputTarget: Target);
202 for (const auto &[Target, Path] : O->rpaths())
203 IF->addRPath(RPath: Path, InputTarget: Target);
204
205 for (const auto *Sym : symbols()) {
206 IF->addSymbol(Kind: Sym->getKind(), Name: Sym->getName(), Targets: Sym->targets(),
207 Flags: Sym->getFlags());
208 }
209
210 for (const auto *Sym : O->symbols()) {
211 IF->addSymbol(Kind: Sym->getKind(), Name: Sym->getName(), Targets: Sym->targets(),
212 Flags: Sym->getFlags());
213 }
214
215 return std::move(IF);
216}
217
218Expected<std::unique_ptr<InterfaceFile>>
219InterfaceFile::remove(Architecture Arch) const {
220 if (getArchitectures() == Arch)
221 return make_error<StringError>(Args: "cannot remove last architecture slice '" +
222 getArchitectureName(Arch) + "'",
223 Args: inconvertibleErrorCode());
224
225 if (!getArchitectures().has(Arch)) {
226 bool Found = false;
227 for (auto &Doc : Documents) {
228 if (Doc->getArchitectures().has(Arch)) {
229 Found = true;
230 break;
231 }
232 }
233
234 if (!Found)
235 return make_error<TextAPIError>(Args: TextAPIErrorCode::NoSuchArchitecture);
236 }
237
238 std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
239 IF->setFileType(getFileType());
240 IF->setPath(getPath());
241 IF->addTargets(Targets: targets(Archs: ArchitectureSet::All().clear(Arch)));
242 IF->setInstallName(getInstallName());
243 IF->setCurrentVersion(getCurrentVersion());
244 IF->setCompatibilityVersion(getCompatibilityVersion());
245 IF->setSwiftABIVersion(getSwiftABIVersion());
246 IF->setTwoLevelNamespace(isTwoLevelNamespace());
247 IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
248 for (const auto &It : umbrellas())
249 if (It.first.Arch != Arch)
250 IF->addParentUmbrella(Target_: It.first, Parent: It.second);
251
252 for (const auto &Lib : allowableClients()) {
253 for (const auto &Target : Lib.targets())
254 if (Target.Arch != Arch)
255 IF->addAllowableClient(InstallName: Lib.getInstallName(), Target);
256 }
257
258 for (const auto &Lib : reexportedLibraries()) {
259 for (const auto &Target : Lib.targets())
260 if (Target.Arch != Arch)
261 IF->addReexportedLibrary(InstallName: Lib.getInstallName(), Target);
262 }
263
264 for (const auto *Sym : symbols()) {
265 auto Archs = Sym->getArchitectures();
266 Archs.clear(Arch);
267 if (Archs.empty())
268 continue;
269
270 IF->addSymbol(Kind: Sym->getKind(), Name: Sym->getName(), Targets: Sym->targets(architectures: Archs),
271 Flags: Sym->getFlags());
272 }
273
274 for (auto &Doc : Documents) {
275 // Skip the inlined document if the to be removed architecture is the
276 // only one left.
277 if (Doc->getArchitectures() == Arch)
278 continue;
279
280 // If the document doesn't contain the arch, then no work is to be done
281 // and it can be copied over.
282 if (!Doc->getArchitectures().has(Arch)) {
283 auto NewDoc = Doc;
284 IF->addDocument(Document: std::move(NewDoc));
285 continue;
286 }
287
288 auto Result = Doc->remove(Arch);
289 if (!Result)
290 return Result;
291
292 IF->addDocument(Document: std::move(Result.get()));
293 }
294
295 return std::move(IF);
296}
297
298Expected<std::unique_ptr<InterfaceFile>>
299InterfaceFile::extract(Architecture Arch) const {
300 if (!getArchitectures().has(Arch)) {
301 return make_error<StringError>(Args: "file doesn't have architecture '" +
302 getArchitectureName(Arch) + "'",
303 Args: inconvertibleErrorCode());
304 }
305
306 std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
307 IF->setFileType(getFileType());
308 IF->setPath(getPath());
309 IF->addTargets(Targets: targets(Archs: Arch));
310 IF->setInstallName(getInstallName());
311 IF->setCurrentVersion(getCurrentVersion());
312 IF->setCompatibilityVersion(getCompatibilityVersion());
313 IF->setSwiftABIVersion(getSwiftABIVersion());
314 IF->setTwoLevelNamespace(isTwoLevelNamespace());
315 IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
316 for (const auto &It : umbrellas())
317 if (It.first.Arch == Arch)
318 IF->addParentUmbrella(Target_: It.first, Parent: It.second);
319
320 for (const auto &It : rpaths())
321 if (It.first.Arch == Arch)
322 IF->addRPath(RPath: It.second, InputTarget: It.first);
323
324 for (const auto &Lib : allowableClients())
325 for (const auto &Target : Lib.targets())
326 if (Target.Arch == Arch)
327 IF->addAllowableClient(InstallName: Lib.getInstallName(), Target);
328
329 for (const auto &Lib : reexportedLibraries())
330 for (const auto &Target : Lib.targets())
331 if (Target.Arch == Arch)
332 IF->addReexportedLibrary(InstallName: Lib.getInstallName(), Target);
333
334 for (const auto *Sym : symbols()) {
335 if (Sym->hasArchitecture(Arch))
336 IF->addSymbol(Kind: Sym->getKind(), Name: Sym->getName(), Targets: Sym->targets(architectures: Arch),
337 Flags: Sym->getFlags());
338 }
339
340 for (auto &Doc : Documents) {
341 // Skip documents that don't have the requested architecture.
342 if (!Doc->getArchitectures().has(Arch))
343 continue;
344
345 auto Result = Doc->extract(Arch);
346 if (!Result)
347 return Result;
348
349 IF->addDocument(Document: std::move(Result.get()));
350 }
351
352 return std::move(IF);
353}
354
355void InterfaceFile::setFromBinaryAttrs(const RecordsSlice::BinaryAttrs &BA,
356 const Target &Targ) {
357 if (getFileType() != BA.File)
358 setFileType(BA.File);
359 if (getInstallName().empty())
360 setInstallName(BA.InstallName);
361 if (BA.AppExtensionSafe && !isApplicationExtensionSafe())
362 setApplicationExtensionSafe();
363 if (BA.TwoLevelNamespace && !isTwoLevelNamespace())
364 setTwoLevelNamespace();
365 if (BA.OSLibNotForSharedCache && !isOSLibNotForSharedCache())
366 setOSLibNotForSharedCache();
367 if (getCurrentVersion().empty())
368 setCurrentVersion(BA.CurrentVersion);
369 if (getCompatibilityVersion().empty())
370 setCompatibilityVersion(BA.CompatVersion);
371 if (getSwiftABIVersion() == 0)
372 setSwiftABIVersion(BA.SwiftABI);
373 if (getPath().empty())
374 setPath(BA.Path);
375 if (!BA.ParentUmbrella.empty())
376 addParentUmbrella(Target_: Targ, Parent: BA.ParentUmbrella);
377 for (const auto &Client : BA.AllowableClients)
378 addAllowableClient(InstallName: Client, Target: Targ);
379 for (const auto &Lib : BA.RexportedLibraries)
380 addReexportedLibrary(InstallName: Lib, Target: Targ);
381}
382
383static bool isYAMLTextStub(const FileType &Kind) {
384 return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
385}
386
387bool InterfaceFile::operator==(const InterfaceFile &O) const {
388 if (Targets != O.Targets)
389 return false;
390 if (InstallName != O.InstallName)
391 return false;
392 if ((CurrentVersion != O.CurrentVersion) ||
393 (CompatibilityVersion != O.CompatibilityVersion))
394 return false;
395 if (SwiftABIVersion != O.SwiftABIVersion)
396 return false;
397 if (IsTwoLevelNamespace != O.IsTwoLevelNamespace)
398 return false;
399 if (IsAppExtensionSafe != O.IsAppExtensionSafe)
400 return false;
401 if (IsOSLibNotForSharedCache != O.IsOSLibNotForSharedCache)
402 return false;
403 if (HasSimSupport != O.HasSimSupport)
404 return false;
405 if (ParentUmbrellas != O.ParentUmbrellas)
406 return false;
407 if (AllowableClients != O.AllowableClients)
408 return false;
409 if (ReexportedLibraries != O.ReexportedLibraries)
410 return false;
411 if (*SymbolsSet != *O.SymbolsSet)
412 return false;
413 // Don't compare run search paths for older filetypes that cannot express
414 // them.
415 if (!(isYAMLTextStub(Kind: FileKind)) && !(isYAMLTextStub(Kind: O.FileKind))) {
416 if (RPaths != O.RPaths)
417 return false;
418 if (mapToPlatformVersionSet(Targets) != mapToPlatformVersionSet(Targets: O.Targets))
419 return false;
420 }
421
422 if (!std::equal(first1: Documents.begin(), last1: Documents.end(), first2: O.Documents.begin(),
423 last2: O.Documents.end(),
424 binary_pred: [](const std::shared_ptr<InterfaceFile> LHS,
425 const std::shared_ptr<InterfaceFile> RHS) {
426 return *LHS == *RHS;
427 }))
428 return false;
429 return true;
430}
431