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