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