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