xref: /llvm-project/llvm/lib/TextAPI/InterfaceFile.cpp (revision 03506bc0a99fd53d0f4e3d0bd77eb2f7bad96102)
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   IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache());
176 
177   for (const auto &It : umbrellas()) {
178     if (!It.second.empty())
179       IF->addParentUmbrella(It.first, It.second);
180   }
181   for (const auto &It : O->umbrellas()) {
182     if (!It.second.empty())
183       IF->addParentUmbrella(It.first, It.second);
184   }
185   IF->addTargets(targets());
186   IF->addTargets(O->targets());
187 
188   for (const auto &Lib : allowableClients())
189     for (const auto &Target : Lib.targets())
190       IF->addAllowableClient(Lib.getInstallName(), Target);
191 
192   for (const auto &Lib : O->allowableClients())
193     for (const auto &Target : Lib.targets())
194       IF->addAllowableClient(Lib.getInstallName(), Target);
195 
196   for (const auto &Lib : reexportedLibraries())
197     for (const auto &Target : Lib.targets())
198       IF->addReexportedLibrary(Lib.getInstallName(), Target);
199 
200   for (const auto &Lib : O->reexportedLibraries())
201     for (const auto &Target : Lib.targets())
202       IF->addReexportedLibrary(Lib.getInstallName(), Target);
203 
204   for (const auto &[Target, Path] : rpaths())
205     IF->addRPath(Path, Target);
206   for (const auto &[Target, Path] : O->rpaths())
207     IF->addRPath(Path, Target);
208 
209   for (const auto *Sym : symbols()) {
210     IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
211                   Sym->getFlags());
212   }
213 
214   for (const auto *Sym : O->symbols()) {
215     IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
216                   Sym->getFlags());
217   }
218 
219   return std::move(IF);
220 }
221 
222 Expected<std::unique_ptr<InterfaceFile>>
223 InterfaceFile::remove(Architecture Arch) const {
224   if (getArchitectures() == Arch)
225     return make_error<StringError>("cannot remove last architecture slice '" +
226                                        getArchitectureName(Arch) + "'",
227                                    inconvertibleErrorCode());
228 
229   if (!getArchitectures().has(Arch)) {
230     bool Found = false;
231     for (auto &Doc : Documents) {
232       if (Doc->getArchitectures().has(Arch)) {
233         Found = true;
234         break;
235       }
236     }
237 
238     if (!Found)
239       return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture);
240   }
241 
242   // FIXME: Figure out how to keep these attributes in sync when new ones are
243   // added.
244   std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
245   IF->setFileType(getFileType());
246   IF->setPath(getPath());
247   IF->addTargets(targets(ArchitectureSet::All().clear(Arch)));
248   IF->setInstallName(getInstallName());
249   IF->setCurrentVersion(getCurrentVersion());
250   IF->setCompatibilityVersion(getCompatibilityVersion());
251   IF->setSwiftABIVersion(getSwiftABIVersion());
252   IF->setTwoLevelNamespace(isTwoLevelNamespace());
253   IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
254   IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache());
255   for (const auto &It : umbrellas())
256     if (It.first.Arch != Arch)
257       IF->addParentUmbrella(It.first, It.second);
258 
259   for (const auto &Lib : allowableClients()) {
260     for (const auto &Target : Lib.targets())
261       if (Target.Arch != Arch)
262         IF->addAllowableClient(Lib.getInstallName(), Target);
263   }
264 
265   for (const auto &Lib : reexportedLibraries()) {
266     for (const auto &Target : Lib.targets())
267       if (Target.Arch != Arch)
268         IF->addReexportedLibrary(Lib.getInstallName(), Target);
269   }
270 
271   for (const auto *Sym : symbols()) {
272     auto Archs = Sym->getArchitectures();
273     Archs.clear(Arch);
274     if (Archs.empty())
275       continue;
276 
277     IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Archs),
278                   Sym->getFlags());
279   }
280 
281   for (auto &Doc : Documents) {
282     // Skip the inlined document if the to be removed architecture is the
283     // only one left.
284     if (Doc->getArchitectures() == Arch)
285       continue;
286 
287     // If the document doesn't contain the arch, then no work is to be done
288     // and it can be copied over.
289     if (!Doc->getArchitectures().has(Arch)) {
290       auto NewDoc = Doc;
291       IF->addDocument(std::move(NewDoc));
292       continue;
293     }
294 
295     auto Result = Doc->remove(Arch);
296     if (!Result)
297       return Result;
298 
299     IF->addDocument(std::move(Result.get()));
300   }
301 
302   return std::move(IF);
303 }
304 
305 Expected<std::unique_ptr<InterfaceFile>>
306 InterfaceFile::extract(Architecture Arch) const {
307   if (!getArchitectures().has(Arch)) {
308     return make_error<StringError>("file doesn't have architecture '" +
309                                        getArchitectureName(Arch) + "'",
310                                    inconvertibleErrorCode());
311   }
312 
313   std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
314   IF->setFileType(getFileType());
315   IF->setPath(getPath());
316   IF->addTargets(targets(Arch));
317   IF->setInstallName(getInstallName());
318   IF->setCurrentVersion(getCurrentVersion());
319   IF->setCompatibilityVersion(getCompatibilityVersion());
320   IF->setSwiftABIVersion(getSwiftABIVersion());
321   IF->setTwoLevelNamespace(isTwoLevelNamespace());
322   IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
323   IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache());
324   for (const auto &It : umbrellas())
325     if (It.first.Arch == Arch)
326       IF->addParentUmbrella(It.first, It.second);
327 
328   for (const auto &It : rpaths())
329     if (It.first.Arch == Arch)
330       IF->addRPath(It.second, It.first);
331 
332   for (const auto &Lib : allowableClients())
333     for (const auto &Target : Lib.targets())
334       if (Target.Arch == Arch)
335         IF->addAllowableClient(Lib.getInstallName(), Target);
336 
337   for (const auto &Lib : reexportedLibraries())
338     for (const auto &Target : Lib.targets())
339       if (Target.Arch == Arch)
340         IF->addReexportedLibrary(Lib.getInstallName(), Target);
341 
342   for (const auto *Sym : symbols()) {
343     if (Sym->hasArchitecture(Arch))
344       IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Arch),
345                     Sym->getFlags());
346   }
347 
348   for (auto &Doc : Documents) {
349     // Skip documents that don't have the requested architecture.
350     if (!Doc->getArchitectures().has(Arch))
351       continue;
352 
353     auto Result = Doc->extract(Arch);
354     if (!Result)
355       return Result;
356 
357     IF->addDocument(std::move(Result.get()));
358   }
359 
360   return std::move(IF);
361 }
362 
363 void InterfaceFile::setFromBinaryAttrs(const RecordsSlice::BinaryAttrs &BA,
364                                        const Target &Targ) {
365   if (getFileType() != BA.File)
366     setFileType(BA.File);
367   if (getInstallName().empty())
368     setInstallName(BA.InstallName);
369   if (BA.AppExtensionSafe && !isApplicationExtensionSafe())
370     setApplicationExtensionSafe();
371   if (BA.TwoLevelNamespace && !isTwoLevelNamespace())
372     setTwoLevelNamespace();
373   if (BA.OSLibNotForSharedCache && !isOSLibNotForSharedCache())
374     setOSLibNotForSharedCache();
375   if (getCurrentVersion().empty())
376     setCurrentVersion(BA.CurrentVersion);
377   if (getCompatibilityVersion().empty())
378     setCompatibilityVersion(BA.CompatVersion);
379   if (getSwiftABIVersion() == 0)
380     setSwiftABIVersion(BA.SwiftABI);
381   if (getPath().empty())
382     setPath(BA.Path);
383   if (!BA.ParentUmbrella.empty())
384     addParentUmbrella(Targ, BA.ParentUmbrella);
385   for (const auto &Client : BA.AllowableClients)
386     addAllowableClient(Client, Targ);
387   for (const auto &Lib : BA.RexportedLibraries)
388     addReexportedLibrary(Lib, Targ);
389 }
390 
391 static bool isYAMLTextStub(const FileType &Kind) {
392   return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
393 }
394 
395 bool InterfaceFile::operator==(const InterfaceFile &O) const {
396   if (Targets != O.Targets)
397     return false;
398   if (InstallName != O.InstallName)
399     return false;
400   if ((CurrentVersion != O.CurrentVersion) ||
401       (CompatibilityVersion != O.CompatibilityVersion))
402     return false;
403   if (SwiftABIVersion != O.SwiftABIVersion)
404     return false;
405   if (IsTwoLevelNamespace != O.IsTwoLevelNamespace)
406     return false;
407   if (IsAppExtensionSafe != O.IsAppExtensionSafe)
408     return false;
409   if (IsOSLibNotForSharedCache != O.IsOSLibNotForSharedCache)
410     return false;
411   if (HasSimSupport != O.HasSimSupport)
412     return false;
413   if (ParentUmbrellas != O.ParentUmbrellas)
414     return false;
415   if (AllowableClients != O.AllowableClients)
416     return false;
417   if (ReexportedLibraries != O.ReexportedLibraries)
418     return false;
419   if (*SymbolsSet != *O.SymbolsSet)
420     return false;
421   // Don't compare run search paths for older filetypes that cannot express
422   // them.
423   if (!(isYAMLTextStub(FileKind)) && !(isYAMLTextStub(O.FileKind))) {
424     if (RPaths != O.RPaths)
425       return false;
426     if (mapToPlatformVersionSet(Targets) != mapToPlatformVersionSet(O.Targets))
427       return false;
428   }
429 
430   if (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(),
431                   O.Documents.end(),
432                   [](const std::shared_ptr<InterfaceFile> LHS,
433                      const std::shared_ptr<InterfaceFile> RHS) {
434                     return *LHS == *RHS;
435                   }))
436     return false;
437   return true;
438 }
439