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
17 using namespace llvm;
18 using namespace llvm::MachO;
19
addTarget(const Target & Target)20 void InterfaceFileRef::addTarget(const Target &Target) {
21 addEntry(Targets, Target);
22 }
23
addAllowableClient(StringRef InstallName,const Target & Target)24 void InterfaceFile::addAllowableClient(StringRef InstallName,
25 const Target &Target) {
26 if (InstallName.empty())
27 return;
28 auto Client = addEntry(AllowableClients, InstallName);
29 Client->addTarget(Target);
30 }
31
addReexportedLibrary(StringRef InstallName,const Target & Target)32 void InterfaceFile::addReexportedLibrary(StringRef InstallName,
33 const Target &Target) {
34 if (InstallName.empty())
35 return;
36 auto Lib = addEntry(ReexportedLibraries, InstallName);
37 Lib->addTarget(Target);
38 }
39
addParentUmbrella(const Target & Target_,StringRef Parent)40 void InterfaceFile::addParentUmbrella(const Target &Target_, StringRef Parent) {
41 if (Parent.empty())
42 return;
43 auto Iter = lower_bound(ParentUmbrellas, Target_,
44 [](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(Iter, Target_, std::string(Parent));
53 }
54
addRPath(StringRef RPath,const Target & InputTarget)55 void 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(RPaths, Entry))
62 return;
63
64 RPaths.emplace_back(Entry);
65 }
66
addTarget(const Target & Target)67 void InterfaceFile::addTarget(const Target &Target) {
68 addEntry(Targets, Target);
69 }
70
71 InterfaceFile::const_filtered_target_range
targets(ArchitectureSet Archs) const72 InterfaceFile::targets(ArchitectureSet Archs) const {
73 std::function<bool(const Target &)> fn = [Archs](const Target &Target_) {
74 return Archs.has(Target_.Arch);
75 };
76 return make_filter_range(Targets, fn);
77 }
78
addDocument(std::shared_ptr<InterfaceFile> && Document)79 void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
80 auto Pos = llvm::lower_bound(Documents, Document,
81 [](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(Pos, Document);
90 }
91
inlineLibrary(std::shared_ptr<InterfaceFile> Library,bool Overwrite)92 void InterfaceFile::inlineLibrary(std::shared_ptr<InterfaceFile> Library,
93 bool Overwrite) {
94 auto AddFwk = [&](std::shared_ptr<InterfaceFile> &&Reexport) {
95 auto It = lower_bound(
96 Documents, Reexport->getInstallName(),
97 [](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(Documents, *It, std::move(Reexport));
104 return;
105 }
106
107 if ((It != Documents.end()) &&
108 !(Reexport->getInstallName() < (*It)->getInstallName()))
109 return;
110
111 Documents.emplace(It, 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
120 Expected<std::unique_ptr<InterfaceFile>>
merge(const InterfaceFile * O) const121 InterfaceFile::merge(const InterfaceFile *O) const {
122 // Verify files can be merged.
123 if (getInstallName() != O->getInstallName()) {
124 return make_error<StringError>("install names do not match",
125 inconvertibleErrorCode());
126 }
127
128 if (getCurrentVersion() != O->getCurrentVersion()) {
129 return make_error<StringError>("current versions do not match",
130 inconvertibleErrorCode());
131 }
132
133 if (getCompatibilityVersion() != O->getCompatibilityVersion()) {
134 return make_error<StringError>("compatibility versions do not match",
135 inconvertibleErrorCode());
136 }
137
138 if ((getSwiftABIVersion() != 0) && (O->getSwiftABIVersion() != 0) &&
139 (getSwiftABIVersion() != O->getSwiftABIVersion())) {
140 return make_error<StringError>("swift ABI versions do not match",
141 inconvertibleErrorCode());
142 }
143
144 if (isTwoLevelNamespace() != O->isTwoLevelNamespace()) {
145 return make_error<StringError>("two level namespace flags do not match",
146 inconvertibleErrorCode());
147 }
148
149 if (isApplicationExtensionSafe() != O->isApplicationExtensionSafe()) {
150 return make_error<StringError>(
151 "application extension safe flags do not match",
152 inconvertibleErrorCode());
153 }
154
155 std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
156 IF->setFileType(std::max(getFileType(), 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(It.first, It.second);
174 }
175 for (const auto &It : O->umbrellas()) {
176 if (!It.second.empty())
177 IF->addParentUmbrella(It.first, It.second);
178 }
179 IF->addTargets(targets());
180 IF->addTargets(O->targets());
181
182 for (const auto &Lib : allowableClients())
183 for (const auto &Target : Lib.targets())
184 IF->addAllowableClient(Lib.getInstallName(), Target);
185
186 for (const auto &Lib : O->allowableClients())
187 for (const auto &Target : Lib.targets())
188 IF->addAllowableClient(Lib.getInstallName(), Target);
189
190 for (const auto &Lib : reexportedLibraries())
191 for (const auto &Target : Lib.targets())
192 IF->addReexportedLibrary(Lib.getInstallName(), Target);
193
194 for (const auto &Lib : O->reexportedLibraries())
195 for (const auto &Target : Lib.targets())
196 IF->addReexportedLibrary(Lib.getInstallName(), Target);
197
198 for (const auto &[Target, Path] : rpaths())
199 IF->addRPath(Path, Target);
200 for (const auto &[Target, Path] : O->rpaths())
201 IF->addRPath(Path, Target);
202
203 for (const auto *Sym : symbols()) {
204 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
205 Sym->getFlags());
206 }
207
208 for (const auto *Sym : O->symbols()) {
209 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
210 Sym->getFlags());
211 }
212
213 return std::move(IF);
214 }
215
216 Expected<std::unique_ptr<InterfaceFile>>
remove(Architecture Arch) const217 InterfaceFile::remove(Architecture Arch) const {
218 if (getArchitectures() == Arch)
219 return make_error<StringError>("cannot remove last architecture slice '" +
220 getArchitectureName(Arch) + "'",
221 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>(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(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(It.first, It.second);
252
253 for (const auto &Lib : allowableClients()) {
254 for (const auto &Target : Lib.targets())
255 if (Target.Arch != Arch)
256 IF->addAllowableClient(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(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(Sym->getKind(), Sym->getName(), Sym->targets(Archs),
272 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(std::move(NewDoc));
286 continue;
287 }
288
289 auto Result = Doc->remove(Arch);
290 if (!Result)
291 return Result;
292
293 IF->addDocument(std::move(Result.get()));
294 }
295
296 return std::move(IF);
297 }
298
299 Expected<std::unique_ptr<InterfaceFile>>
extract(Architecture Arch) const300 InterfaceFile::extract(Architecture Arch) const {
301 if (!getArchitectures().has(Arch)) {
302 return make_error<StringError>("file doesn't have architecture '" +
303 getArchitectureName(Arch) + "'",
304 inconvertibleErrorCode());
305 }
306
307 std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
308 IF->setFileType(getFileType());
309 IF->setPath(getPath());
310 IF->addTargets(targets(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(It.first, It.second);
321
322 for (const auto &It : rpaths())
323 if (It.first.Arch == Arch)
324 IF->addRPath(It.second, It.first);
325
326 for (const auto &Lib : allowableClients())
327 for (const auto &Target : Lib.targets())
328 if (Target.Arch == Arch)
329 IF->addAllowableClient(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(Lib.getInstallName(), Target);
335
336 for (const auto *Sym : symbols()) {
337 if (Sym->hasArchitecture(Arch))
338 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Arch),
339 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(std::move(Result.get()));
352 }
353
354 return std::move(IF);
355 }
356
setFromBinaryAttrs(const RecordsSlice::BinaryAttrs & BA,const Target & Targ)357 void 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(Targ, BA.ParentUmbrella);
379 for (const auto &Client : BA.AllowableClients)
380 addAllowableClient(Client, Targ);
381 for (const auto &Lib : BA.RexportedLibraries)
382 addReexportedLibrary(Lib, Targ);
383 }
384
isYAMLTextStub(const FileType & Kind)385 static bool isYAMLTextStub(const FileType &Kind) {
386 return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
387 }
388
operator ==(const InterfaceFile & O) const389 bool 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(FileKind)) && !(isYAMLTextStub(O.FileKind))) {
418 if (RPaths != O.RPaths)
419 return false;
420 if (mapToPlatformVersionSet(Targets) != mapToPlatformVersionSet(O.Targets))
421 return false;
422 }
423
424 if (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(),
425 O.Documents.end(),
426 [](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