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(Container&: Targets, Targ: Target); |
22 | } |
23 | |
24 | void InterfaceFile::addAllowableClient(StringRef InstallName, |
25 | const Target &Target) { |
26 | if (InstallName.empty()) |
27 | return; |
28 | auto Client = addEntry(Container&: 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(Container&: 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(Range&: ParentUmbrellas, Value: Target_, |
44 | C: [](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(position: Iter, args: Target_, args: 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(Range&: RPaths, Element: Entry)) |
62 | return; |
63 | |
64 | RPaths.emplace_back(args: Entry); |
65 | } |
66 | |
67 | void InterfaceFile::addTarget(const Target &Target) { |
68 | addEntry(Container&: Targets, Targ: 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(Arch: Target_.Arch); |
75 | }; |
76 | return make_filter_range(Range: Targets, Pred: fn); |
77 | } |
78 | |
79 | void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) { |
80 | auto Pos = llvm::lower_bound(Range&: Documents, Value&: Document, |
81 | C: [](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(position: Pos, x: 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 | Range&: Documents, Value: Reexport->getInstallName(), |
97 | C: [](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(Range&: Documents, OldValue: *It, NewValue: std::move(Reexport)); |
104 | return; |
105 | } |
106 | |
107 | if ((It != Documents.end()) && |
108 | !(Reexport->getInstallName() < (*It)->getInstallName())) |
109 | return; |
110 | |
111 | Documents.emplace(position: It, args: 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>(Args: "install names do not match" , |
125 | Args: inconvertibleErrorCode()); |
126 | } |
127 | |
128 | if (getCurrentVersion() != O->getCurrentVersion()) { |
129 | return make_error<StringError>(Args: "current versions do not match" , |
130 | Args: inconvertibleErrorCode()); |
131 | } |
132 | |
133 | if (getCompatibilityVersion() != O->getCompatibilityVersion()) { |
134 | return make_error<StringError>(Args: "compatibility versions do not match" , |
135 | Args: inconvertibleErrorCode()); |
136 | } |
137 | |
138 | if ((getSwiftABIVersion() != 0) && (O->getSwiftABIVersion() != 0) && |
139 | (getSwiftABIVersion() != O->getSwiftABIVersion())) { |
140 | return make_error<StringError>(Args: "swift ABI versions do not match" , |
141 | Args: inconvertibleErrorCode()); |
142 | } |
143 | |
144 | if (isTwoLevelNamespace() != O->isTwoLevelNamespace()) { |
145 | return make_error<StringError>(Args: "two level namespace flags do not match" , |
146 | Args: inconvertibleErrorCode()); |
147 | } |
148 | |
149 | if (isApplicationExtensionSafe() != O->isApplicationExtensionSafe()) { |
150 | return make_error<StringError>( |
151 | Args: "application extension safe flags do not match" , |
152 | Args: inconvertibleErrorCode()); |
153 | } |
154 | |
155 | std::unique_ptr<InterfaceFile> IF(new InterfaceFile()); |
156 | IF->setFileType(std::max(a: getFileType(), b: 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(Target_: It.first, Parent: It.second); |
174 | } |
175 | for (const auto &It : O->umbrellas()) { |
176 | if (!It.second.empty()) |
177 | IF->addParentUmbrella(Target_: It.first, Parent: It.second); |
178 | } |
179 | IF->addTargets(Targets: targets()); |
180 | IF->addTargets(Targets: O->targets()); |
181 | |
182 | for (const auto &Lib : allowableClients()) |
183 | for (const auto &Target : Lib.targets()) |
184 | IF->addAllowableClient(InstallName: Lib.getInstallName(), Target); |
185 | |
186 | for (const auto &Lib : O->allowableClients()) |
187 | for (const auto &Target : Lib.targets()) |
188 | IF->addAllowableClient(InstallName: Lib.getInstallName(), Target); |
189 | |
190 | for (const auto &Lib : reexportedLibraries()) |
191 | for (const auto &Target : Lib.targets()) |
192 | IF->addReexportedLibrary(InstallName: Lib.getInstallName(), Target); |
193 | |
194 | for (const auto &Lib : O->reexportedLibraries()) |
195 | for (const auto &Target : Lib.targets()) |
196 | IF->addReexportedLibrary(InstallName: Lib.getInstallName(), Target); |
197 | |
198 | for (const auto &[Target, Path] : rpaths()) |
199 | IF->addRPath(RPath: Path, InputTarget: Target); |
200 | for (const auto &[Target, Path] : O->rpaths()) |
201 | IF->addRPath(RPath: Path, InputTarget: Target); |
202 | |
203 | for (const auto *Sym : symbols()) { |
204 | IF->addSymbol(Kind: Sym->getKind(), Name: Sym->getName(), Targets: Sym->targets(), |
205 | Flags: Sym->getFlags()); |
206 | } |
207 | |
208 | for (const auto *Sym : O->symbols()) { |
209 | IF->addSymbol(Kind: Sym->getKind(), Name: Sym->getName(), Targets: Sym->targets(), |
210 | Flags: 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>(Args: "cannot remove last architecture slice '" + |
220 | getArchitectureName(Arch) + "'" , |
221 | Args: 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>(Args: 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: 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 | IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache()); |
249 | for (const auto &It : umbrellas()) |
250 | if (It.first.Arch != Arch) |
251 | IF->addParentUmbrella(Target_: It.first, Parent: It.second); |
252 | |
253 | for (const auto &Lib : allowableClients()) { |
254 | for (const auto &Target : Lib.targets()) |
255 | if (Target.Arch != Arch) |
256 | IF->addAllowableClient(InstallName: 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(InstallName: 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(Kind: Sym->getKind(), Name: Sym->getName(), Targets: Sym->targets(architectures: Archs), |
272 | Flags: 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(Document: std::move(NewDoc)); |
286 | continue; |
287 | } |
288 | |
289 | auto Result = Doc->remove(Arch); |
290 | if (!Result) |
291 | return Result; |
292 | |
293 | IF->addDocument(Document: std::move(Result.get())); |
294 | } |
295 | |
296 | return std::move(IF); |
297 | } |
298 | |
299 | Expected<std::unique_ptr<InterfaceFile>> |
300 | InterfaceFile::(Architecture Arch) const { |
301 | if (!getArchitectures().has(Arch)) { |
302 | return make_error<StringError>(Args: "file doesn't have architecture '" + |
303 | getArchitectureName(Arch) + "'" , |
304 | Args: inconvertibleErrorCode()); |
305 | } |
306 | |
307 | std::unique_ptr<InterfaceFile> IF(new InterfaceFile()); |
308 | IF->setFileType(getFileType()); |
309 | IF->setPath(getPath()); |
310 | IF->addTargets(Targets: targets(Archs: 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(Target_: It.first, Parent: It.second); |
321 | |
322 | for (const auto &It : rpaths()) |
323 | if (It.first.Arch == Arch) |
324 | IF->addRPath(RPath: It.second, InputTarget: It.first); |
325 | |
326 | for (const auto &Lib : allowableClients()) |
327 | for (const auto &Target : Lib.targets()) |
328 | if (Target.Arch == Arch) |
329 | IF->addAllowableClient(InstallName: 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(InstallName: Lib.getInstallName(), Target); |
335 | |
336 | for (const auto *Sym : symbols()) { |
337 | if (Sym->hasArchitecture(Arch)) |
338 | IF->addSymbol(Kind: Sym->getKind(), Name: Sym->getName(), Targets: Sym->targets(architectures: Arch), |
339 | Flags: 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(Document: 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(Target_: Targ, Parent: BA.ParentUmbrella); |
379 | for (const auto &Client : BA.AllowableClients) |
380 | addAllowableClient(InstallName: Client, Target: Targ); |
381 | for (const auto &Lib : BA.RexportedLibraries) |
382 | addReexportedLibrary(InstallName: Lib, Target: 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(Kind: FileKind)) && !(isYAMLTextStub(Kind: O.FileKind))) { |
418 | if (RPaths != O.RPaths) |
419 | return false; |
420 | if (mapToPlatformVersionSet(Targets) != mapToPlatformVersionSet(Targets: O.Targets)) |
421 | return false; |
422 | } |
423 | |
424 | if (!std::equal(first1: Documents.begin(), last1: Documents.end(), first2: O.Documents.begin(), |
425 | last2: O.Documents.end(), |
426 | binary_pred: [](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 | |