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