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