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