Skip to content

Commit 8d86326

Browse files
committed
[TextAPI] Add functionality to manipulate over InterfaceFiles
InterfaceFile is the in-memory representation for tbd files. Add APIs to merge, extract, remove, and inline reexported libraries. Reviewed By: zixuw Differential Revision: https://reviews.llvm.org/D153398 (cherry picked from commit 16c1f43)
1 parent 82c2a82 commit 8d86326

File tree

8 files changed

+1516
-1
lines changed

8 files changed

+1516
-1
lines changed

llvm/include/llvm/TextAPI/ArchitectureSet.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,18 @@ class ArchitectureSet {
4040
ArchitectureSet(Architecture Arch) : ArchitectureSet() { set(Arch); }
4141
ArchitectureSet(const std::vector<Architecture> &Archs);
4242

43+
static ArchitectureSet All() { return ArchitectureSet(EndIndexVal); }
44+
4345
void set(Architecture Arch) {
4446
if (Arch == AK_unknown)
4547
return;
4648
ArchSet |= 1U << static_cast<int>(Arch);
4749
}
4850

49-
void clear(Architecture Arch) { ArchSet &= ~(1U << static_cast<int>(Arch)); }
51+
ArchitectureSet clear(Architecture Arch) {
52+
ArchSet &= ~(1U << static_cast<int>(Arch));
53+
return ArchSet;
54+
}
5055

5156
bool has(Architecture Arch) const {
5257
return ArchSet & (1U << static_cast<int>(Arch));

llvm/include/llvm/TextAPI/InterfaceFile.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,37 @@ class InterfaceFile {
372372
return SymbolsSet->undefineds();
373373
};
374374

375+
/// Extract architecture slice from Interface.
376+
///
377+
/// \param Arch architecture to extract from.
378+
/// \return New InterfaceFile with extracted architecture slice.
379+
llvm::Expected<std::unique_ptr<InterfaceFile>>
380+
extract(Architecture Arch) const;
381+
382+
/// Remove architecture slice from Interface.
383+
///
384+
/// \param Arch architecture to remove.
385+
/// \return New Interface File with removed architecture slice.
386+
llvm::Expected<std::unique_ptr<InterfaceFile>>
387+
remove(Architecture Arch) const;
388+
389+
/// Merge Interfaces for the same library. The following library attributes
390+
/// must match.
391+
/// * Install name, Current & Compatibility version,
392+
/// * Two-level namespace enablement, and App extension enablement.
393+
///
394+
/// \param O The Interface to merge.
395+
/// \return New Interface File that was merged.
396+
llvm::Expected<std::unique_ptr<InterfaceFile>>
397+
merge(const InterfaceFile *O) const;
398+
399+
/// Inline reexported library into Interface.
400+
///
401+
/// \param Library Interface of reexported library.
402+
/// \param Overwrite Whether to overwrite preexisting inlined library.
403+
void inlineLibrary(std::shared_ptr<InterfaceFile> Library,
404+
bool Overwrite = false);
405+
375406
/// The equality is determined by attributes that impact linking
376407
/// compatibilities. Path, & FileKind are irrelevant since these by
377408
/// itself should not impact linking.

llvm/include/llvm/TextAPI/Symbol.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ class Symbol {
121121
return (Flags & SymbolFlags::Text) == SymbolFlags::Text;
122122
}
123123

124+
bool hasArchitecture(Architecture Arch) const {
125+
return mapToArchitectureSet(Targets).contains(Arch);
126+
}
127+
124128
using const_target_iterator = TargetList::const_iterator;
125129
using const_target_range = llvm::iterator_range<const_target_iterator>;
126130
const_target_range targets() const { return {Targets}; }
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===- llvm/TextAPI/TextAPIError.h - TAPI Error -----------------*- C++ -*-===//
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+
/// \file
10+
/// \brief Define TAPI specific error codes.
11+
///
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_TEXTAPI_TEXTAPIERROR_H
15+
#define LLVM_TEXTAPI_TEXTAPIERROR_H
16+
17+
#include "llvm/Support/Error.h"
18+
19+
namespace llvm::MachO {
20+
enum class TextAPIErrorCode {
21+
NoSuchArchitecture,
22+
EmptyResults,
23+
GenericFrontendError,
24+
};
25+
26+
class TextAPIError : public llvm::ErrorInfo<TextAPIError> {
27+
public:
28+
static char ID;
29+
TextAPIErrorCode EC;
30+
31+
TextAPIError(TextAPIErrorCode EC) : EC(EC) {}
32+
33+
void log(raw_ostream &OS) const override;
34+
std::error_code convertToErrorCode() const override;
35+
};
36+
37+
} // namespace llvm::MachO
38+
#endif // LLVM_TEXTAPI_TEXTAPIERROR_H

llvm/lib/TextAPI/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_llvm_component_library(LLVMTextAPI
88
Symbol.cpp
99
SymbolSet.cpp
1010
Target.cpp
11+
TextAPIError.cpp
1112
TextStub.cpp
1213
TextStubCommon.cpp
1314

llvm/lib/TextAPI/InterfaceFile.cpp

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "llvm/TextAPI/InterfaceFile.h"
14+
#include "llvm/TextAPI/TextAPIError.h"
1415
#include <iomanip>
1516
#include <sstream>
1617

@@ -81,6 +82,267 @@ void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
8182
Documents.insert(Pos, Document);
8283
}
8384

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+
84346
static bool isYAMLTextStub(const FileType &Kind) {
85347
return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
86348
}

0 commit comments

Comments
 (0)