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