1 //===- InterfaceFile.cpp --------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Implements the Interface File.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/TextAPI/InterfaceFile.h"
14 #include "llvm/TextAPI/TextAPIError.h"
19 using namespace llvm::MachO
;
21 void InterfaceFileRef::addTarget(const Target
&Target
) {
22 addEntry(Targets
, Target
);
25 void InterfaceFile::addAllowableClient(StringRef InstallName
,
26 const Target
&Target
) {
27 if (InstallName
.empty())
29 auto Client
= addEntry(AllowableClients
, InstallName
);
30 Client
->addTarget(Target
);
33 void InterfaceFile::addReexportedLibrary(StringRef InstallName
,
34 const Target
&Target
) {
35 if (InstallName
.empty())
37 auto Lib
= addEntry(ReexportedLibraries
, InstallName
);
38 Lib
->addTarget(Target
);
41 void InterfaceFile::addParentUmbrella(const Target
&Target_
, StringRef Parent
) {
44 auto Iter
= lower_bound(ParentUmbrellas
, Target_
,
45 [](const std::pair
<Target
, std::string
> &LHS
,
46 Target RHS
) { return LHS
.first
< RHS
; });
48 if ((Iter
!= ParentUmbrellas
.end()) && !(Target_
< Iter
->first
)) {
49 Iter
->second
= std::string(Parent
);
53 ParentUmbrellas
.emplace(Iter
, Target_
, std::string(Parent
));
56 void InterfaceFile::addRPath(const Target
&InputTarget
, StringRef RPath
) {
59 using RPathEntryT
= const std::pair
<Target
, std::string
>;
60 RPathEntryT
Entry(InputTarget
, RPath
);
62 lower_bound(RPaths
, Entry
,
63 [](RPathEntryT
&LHS
, RPathEntryT
&RHS
) { return LHS
< RHS
; });
65 if ((Iter
!= RPaths
.end()) && (*Iter
== Entry
))
68 RPaths
.emplace(Iter
, Entry
);
71 void InterfaceFile::addTarget(const Target
&Target
) {
72 addEntry(Targets
, Target
);
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
);
80 return make_filter_range(Targets
, fn
);
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
;
89 Document
->Parent
= this;
90 Documents
.insert(Pos
, Document
);
93 void InterfaceFile::inlineLibrary(std::shared_ptr
<InterfaceFile
> Library
,
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
;
102 if (Overwrite
&& It
!= Documents
.end() &&
103 Reexport
->getInstallName() == (*It
)->getInstallName()) {
104 std::replace(Documents
.begin(), Documents
.end(), *It
,
105 std::move(Reexport
));
109 if ((It
!= Documents
.end()) &&
110 !(Reexport
->getInstallName() < (*It
)->getInstallName()))
113 Documents
.emplace(It
, std::move(Reexport
));
115 for (auto Doc
: Library
->documents())
116 AddFwk(std::move(Doc
));
118 Library
->Documents
.clear();
119 AddFwk(std::move(Library
));
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());
130 if (getCurrentVersion() != O
->getCurrentVersion()) {
131 return make_error
<StringError
>("current versions do not match",
132 inconvertibleErrorCode());
135 if (getCompatibilityVersion() != O
->getCompatibilityVersion()) {
136 return make_error
<StringError
>("compatibility versions do not match",
137 inconvertibleErrorCode());
140 if ((getSwiftABIVersion() != 0) && (O
->getSwiftABIVersion() != 0) &&
141 (getSwiftABIVersion() != O
->getSwiftABIVersion())) {
142 return make_error
<StringError
>("swift ABI versions do not match",
143 inconvertibleErrorCode());
146 if (isTwoLevelNamespace() != O
->isTwoLevelNamespace()) {
147 return make_error
<StringError
>("two level namespace flags do not match",
148 inconvertibleErrorCode());
151 if (isApplicationExtensionSafe() != O
->isApplicationExtensionSafe()) {
152 return make_error
<StringError
>(
153 "application extension safe flags do not match",
154 inconvertibleErrorCode());
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());
164 if (getSwiftABIVersion() == 0)
165 IF
->setSwiftABIVersion(O
->getSwiftABIVersion());
167 IF
->setSwiftABIVersion(getSwiftABIVersion());
169 IF
->setTwoLevelNamespace(isTwoLevelNamespace());
170 IF
->setApplicationExtensionSafe(isApplicationExtensionSafe());
172 for (const auto &It
: umbrellas()) {
173 if (!It
.second
.empty())
174 IF
->addParentUmbrella(It
.first
, It
.second
);
176 for (const auto &It
: O
->umbrellas()) {
177 if (!It
.second
.empty())
178 IF
->addParentUmbrella(It
.first
, It
.second
);
180 IF
->addTargets(targets());
181 IF
->addTargets(O
->targets());
183 for (const auto &Lib
: allowableClients())
184 for (const auto &Target
: Lib
.targets())
185 IF
->addAllowableClient(Lib
.getInstallName(), Target
);
187 for (const auto &Lib
: O
->allowableClients())
188 for (const auto &Target
: Lib
.targets())
189 IF
->addAllowableClient(Lib
.getInstallName(), Target
);
191 for (const auto &Lib
: reexportedLibraries())
192 for (const auto &Target
: Lib
.targets())
193 IF
->addReexportedLibrary(Lib
.getInstallName(), Target
);
195 for (const auto &Lib
: O
->reexportedLibraries())
196 for (const auto &Target
: Lib
.targets())
197 IF
->addReexportedLibrary(Lib
.getInstallName(), Target
);
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
);
204 for (const auto *Sym
: symbols()) {
205 IF
->addSymbol(Sym
->getKind(), Sym
->getName(), Sym
->targets(),
209 for (const auto *Sym
: O
->symbols()) {
210 IF
->addSymbol(Sym
->getKind(), Sym
->getName(), Sym
->targets(),
214 return std::move(IF
);
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());
224 if (!getArchitectures().has(Arch
)) {
226 for (auto &Doc
: Documents
) {
227 if (Doc
->getArchitectures().has(Arch
)) {
234 return make_error
<TextAPIError
>(TextAPIErrorCode::NoSuchArchitecture
);
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
);
251 for (const auto &Lib
: allowableClients()) {
252 for (const auto &Target
: Lib
.targets())
253 if (Target
.Arch
!= Arch
)
254 IF
->addAllowableClient(Lib
.getInstallName(), Target
);
257 for (const auto &Lib
: reexportedLibraries()) {
258 for (const auto &Target
: Lib
.targets())
259 if (Target
.Arch
!= Arch
)
260 IF
->addReexportedLibrary(Lib
.getInstallName(), Target
);
263 for (const auto *Sym
: symbols()) {
264 auto Archs
= Sym
->getArchitectures();
269 IF
->addSymbol(Sym
->getKind(), Sym
->getName(), Sym
->targets(Archs
),
273 for (auto &Doc
: Documents
) {
274 // Skip the inlined document if the to be removed architecture is the
276 if (Doc
->getArchitectures() == Arch
)
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
)) {
283 IF
->addDocument(std::move(NewDoc
));
287 auto Result
= Doc
->remove(Arch
);
291 IF
->addDocument(std::move(Result
.get()));
294 return std::move(IF
);
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());
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
);
319 for (const auto &It
: rpaths())
320 if (It
.first
.Arch
== Arch
)
321 IF
->addRPath(It
.first
, It
.second
);
323 for (const auto &Lib
: allowableClients())
324 for (const auto &Target
: Lib
.targets())
325 if (Target
.Arch
== Arch
)
326 IF
->addAllowableClient(Lib
.getInstallName(), Target
);
328 for (const auto &Lib
: reexportedLibraries())
329 for (const auto &Target
: Lib
.targets())
330 if (Target
.Arch
== Arch
)
331 IF
->addReexportedLibrary(Lib
.getInstallName(), Target
);
333 for (const auto *Sym
: symbols()) {
334 if (Sym
->hasArchitecture(Arch
))
335 IF
->addSymbol(Sym
->getKind(), Sym
->getName(), Sym
->targets(Arch
),
339 for (auto &Doc
: Documents
) {
340 // Skip documents that don't have the requested architecture.
341 if (!Doc
->getArchitectures().has(Arch
))
344 auto Result
= Doc
->extract(Arch
);
348 IF
->addDocument(std::move(Result
.get()));
351 return std::move(IF
);
354 static bool isYAMLTextStub(const FileType
&Kind
) {
355 return (Kind
>= FileType::TBD_V1
) && (Kind
< FileType::TBD_V5
);
358 bool InterfaceFile::operator==(const InterfaceFile
&O
) const {
359 if (Targets
!= O
.Targets
)
361 if (InstallName
!= O
.InstallName
)
363 if ((CurrentVersion
!= O
.CurrentVersion
) ||
364 (CompatibilityVersion
!= O
.CompatibilityVersion
))
366 if (SwiftABIVersion
!= O
.SwiftABIVersion
)
368 if (IsTwoLevelNamespace
!= O
.IsTwoLevelNamespace
)
370 if (IsAppExtensionSafe
!= O
.IsAppExtensionSafe
)
372 if (IsOSLibNotForSharedCache
!= O
.IsOSLibNotForSharedCache
)
374 if (HasSimSupport
!= O
.HasSimSupport
)
376 if (ParentUmbrellas
!= O
.ParentUmbrellas
)
378 if (AllowableClients
!= O
.AllowableClients
)
380 if (ReexportedLibraries
!= O
.ReexportedLibraries
)
382 if (*SymbolsSet
!= *O
.SymbolsSet
)
384 // Don't compare run search paths for older filetypes that cannot express
386 if (!(isYAMLTextStub(FileKind
)) && !(isYAMLTextStub(O
.FileKind
))) {
387 if (RPaths
!= O
.RPaths
)
389 if (mapToPlatformVersionSet(Targets
) != mapToPlatformVersionSet(O
.Targets
))
393 if (!std::equal(Documents
.begin(), Documents
.end(), O
.Documents
.begin(),
395 [](const std::shared_ptr
<InterfaceFile
> LHS
,
396 const std::shared_ptr
<InterfaceFile
> RHS
) {