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