1 | //===-- DiffEngine.cpp - Structural file comparison -----------------------===// |
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 | // This file defines the implementation of the llvm-tapi difference |
10 | // engine, which structurally compares two tbd files. |
11 | // |
12 | //===----------------------------------------------------------------------===/ |
13 | #include "DiffEngine.h" |
14 | #include "llvm/ADT/SmallString.h" |
15 | #include "llvm/Support/Casting.h" |
16 | #include "llvm/Support/raw_ostream.h" |
17 | #include "llvm/TextAPI/InterfaceFile.h" |
18 | #include "llvm/TextAPI/Symbol.h" |
19 | #include "llvm/TextAPI/Target.h" |
20 | #include <iterator> |
21 | |
22 | using namespace llvm; |
23 | using namespace MachO; |
24 | using namespace object; |
25 | |
26 | StringRef setOrderIndicator(InterfaceInputOrder Order) { |
27 | return ((Order == lhs) ? "< " : "> " ); |
28 | } |
29 | |
30 | // The following template specialization implementations |
31 | // need to be explicitly placed into the llvm namespace |
32 | // to work around a GCC 4.8 bug. |
33 | namespace llvm { |
34 | |
35 | template <typename T, DiffAttrKind U> |
36 | inline void DiffScalarVal<T, U>::print(raw_ostream &OS, std::string Indent) { |
37 | OS << Indent << "\t" << setOrderIndicator(Order) << Val << "\n" ; |
38 | } |
39 | |
40 | template <> |
41 | inline void |
42 | DiffScalarVal<StringRef, AD_Diff_Scalar_Str>::print(raw_ostream &OS, |
43 | std::string Indent) { |
44 | OS << Indent << "\t\t" << setOrderIndicator(Order) << Val << "\n" ; |
45 | } |
46 | |
47 | template <> |
48 | inline void |
49 | DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>::print(raw_ostream &OS, |
50 | std::string Indent) { |
51 | OS << Indent << "\t" << setOrderIndicator(Order) << std::to_string(val: Val) |
52 | << "\n" ; |
53 | } |
54 | |
55 | template <> |
56 | inline void |
57 | DiffScalarVal<bool, AD_Diff_Scalar_Bool>::print(raw_ostream &OS, |
58 | std::string Indent) { |
59 | OS << Indent << "\t" << setOrderIndicator(Order) |
60 | << ((Val == true) ? "true" : "false" ) << "\n" ; |
61 | } |
62 | |
63 | } // end namespace llvm |
64 | |
65 | StringLiteral SymScalar::getSymbolNamePrefix(MachO::EncodeKind Kind) { |
66 | switch (Kind) { |
67 | case MachO::EncodeKind::GlobalSymbol: |
68 | return StringLiteral("" ); |
69 | case MachO::EncodeKind::ObjectiveCClass: |
70 | return ObjC2MetaClassNamePrefix; |
71 | case MachO::EncodeKind ::ObjectiveCClassEHType: |
72 | return ObjC2EHTypePrefix; |
73 | case MachO::EncodeKind ::ObjectiveCInstanceVariable: |
74 | return ObjC2IVarPrefix; |
75 | } |
76 | llvm_unreachable("Unknown llvm::MachO::EncodeKind enum" ); |
77 | } |
78 | |
79 | std::string SymScalar::getFlagString(const MachO::Symbol *Sym) { |
80 | if (Sym->getFlags() == SymbolFlags::None) |
81 | return {}; |
82 | SmallString<64> Flags(" - " ); |
83 | if (Sym->isThreadLocalValue()) |
84 | Flags.append(RHS: "Thread-Local " ); |
85 | if (Sym->isWeakDefined()) |
86 | Flags.append(RHS: "Weak-Defined " ); |
87 | if (Sym->isWeakReferenced()) |
88 | Flags.append(RHS: "Weak-Referenced " ); |
89 | if (Sym->isUndefined()) |
90 | Flags.append(RHS: "Undefined " ); |
91 | if (Sym->isReexported()) |
92 | Flags.append(RHS: "Reexported " ); |
93 | if (Sym->isData()) |
94 | Flags.append(RHS: "Data " ); |
95 | if (Sym->isText()) |
96 | Flags.append(RHS: "Text " ); |
97 | |
98 | return std::string(Flags); |
99 | } |
100 | |
101 | void SymScalar::print(raw_ostream &OS, std::string Indent, MachO::Target Targ) { |
102 | if (Val->getKind() == MachO::EncodeKind::ObjectiveCClass) { |
103 | if (Targ.Arch == MachO::AK_i386 && Targ.Platform == MachO::PLATFORM_MACOS) { |
104 | OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> " ) |
105 | << ObjC1ClassNamePrefix << Val->getName() << getFlagString(Sym: Val) |
106 | << "\n" ; |
107 | return; |
108 | } |
109 | OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> " ) |
110 | << ObjC2ClassNamePrefix << Val->getName() << getFlagString(Sym: Val) << "\n" ; |
111 | } |
112 | OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> " ) |
113 | << getSymbolNamePrefix(Kind: Val->getKind()) << Val->getName() |
114 | << getFlagString(Sym: Val) << "\n" ; |
115 | } |
116 | |
117 | bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS, |
118 | llvm::MachO::InterfaceFile::const_symbol_range RHS) { |
119 | if (std::distance(first: LHS.begin(), last: LHS.end()) != |
120 | std::distance(first: RHS.begin(), last: RHS.end())) |
121 | return false; |
122 | return std::equal(first1: LHS.begin(), last1: LHS.end(), first2: RHS.begin(), |
123 | binary_pred: [&](auto LHS, auto RHS) { return *LHS == *RHS; }); |
124 | } |
125 | |
126 | template <typename TargetVecT, typename ValTypeT, typename V> |
127 | void addDiffForTargSlice(V Val, Target Targ, DiffOutput &Diff, |
128 | InterfaceInputOrder Order) { |
129 | auto TargetVector = llvm::find_if( |
130 | Diff.Values, [&](const std::unique_ptr<AttributeDiff> &RawTVec) { |
131 | if (TargetVecT *TVec = dyn_cast<TargetVecT>(RawTVec.get())) |
132 | return TVec->Targ == Targ; |
133 | return false; |
134 | }); |
135 | if (TargetVector != Diff.Values.end()) { |
136 | ValTypeT NewVal(Order, Val); |
137 | cast<TargetVecT>(TargetVector->get())->TargValues.push_back(NewVal); |
138 | } else { |
139 | auto NewTargetVec = std::make_unique<TargetVecT>(Targ); |
140 | ValTypeT NewVal(Order, Val); |
141 | NewTargetVec->TargValues.push_back(NewVal); |
142 | Diff.Values.push_back(std::move(NewTargetVec)); |
143 | } |
144 | } |
145 | |
146 | DiffOutput getSingleAttrDiff(const std::vector<InterfaceFileRef> &IRefVec, |
147 | std::string Name, InterfaceInputOrder Order) { |
148 | DiffOutput Diff(Name); |
149 | Diff.Kind = AD_Str_Vec; |
150 | for (const auto &IRef : IRefVec) |
151 | for (auto Targ : IRef.targets()) |
152 | addDiffForTargSlice<DiffStrVec, |
153 | DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( |
154 | Val: IRef.getInstallName(), Targ, Diff, Order); |
155 | return Diff; |
156 | } |
157 | |
158 | DiffOutput |
159 | getSingleAttrDiff(const std::vector<std::pair<Target, std::string>> &PairVec, |
160 | std::string Name, InterfaceInputOrder Order) { |
161 | DiffOutput Diff(Name); |
162 | Diff.Kind = AD_Str_Vec; |
163 | for (const auto &Pair : PairVec) |
164 | addDiffForTargSlice<DiffStrVec, |
165 | DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( |
166 | Val: StringRef(Pair.second), Targ: Pair.first, Diff, Order); |
167 | return Diff; |
168 | } |
169 | |
170 | DiffOutput getSingleAttrDiff(InterfaceFile::const_symbol_range SymRange, |
171 | std::string Name, InterfaceInputOrder Order) { |
172 | DiffOutput Diff(Name); |
173 | Diff.Kind = AD_Sym_Vec; |
174 | for (const auto *Sym : SymRange) |
175 | for (auto Targ : Sym->targets()) |
176 | addDiffForTargSlice<DiffSymVec, SymScalar>(Val: Sym, Targ, Diff, Order); |
177 | return Diff; |
178 | } |
179 | |
180 | template <typename T> |
181 | DiffOutput getSingleAttrDiff(T SingleAttr, std::string Attribute) { |
182 | DiffOutput Diff(Attribute); |
183 | Diff.Kind = SingleAttr.getKind(); |
184 | Diff.Values.push_back(std::make_unique<T>(SingleAttr)); |
185 | return Diff; |
186 | } |
187 | |
188 | template <typename T, DiffAttrKind U> |
189 | void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, |
190 | DiffScalarVal<T, U> Attr) { |
191 | Output.push_back(getSingleAttrDiff(Attr, Name)); |
192 | } |
193 | |
194 | template <typename T> |
195 | void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, |
196 | const T &Val, InterfaceInputOrder Order) { |
197 | Output.push_back(getSingleAttrDiff(Val, Name, Order)); |
198 | } |
199 | |
200 | std::vector<DiffOutput> getSingleIF(InterfaceFile *Interface, |
201 | InterfaceInputOrder Order) { |
202 | std::vector<DiffOutput> Output; |
203 | diffAttribute(Name: "Install Name" , Output, |
204 | Attr: DiffScalarVal<StringRef, AD_Diff_Scalar_Str>( |
205 | Order, Interface->getInstallName())); |
206 | for (const auto &Doc : Interface->documents()) { |
207 | DiffOutput Documents("Inlined Reexported Frameworks/Libraries" ); |
208 | Documents.Kind = AD_Inline_Doc; |
209 | Documents.Values.push_back(x: std::make_unique<InlineDoc>( |
210 | args: InlineDoc(Doc->getInstallName(), getSingleIF(Interface: Doc.get(), Order)))); |
211 | Output.push_back(x: std::move(Documents)); |
212 | } |
213 | return Output; |
214 | } |
215 | |
216 | void findAndAddDiff(const std::vector<InterfaceFileRef> &CollectedIRefVec, |
217 | const std::vector<InterfaceFileRef> &LookupIRefVec, |
218 | DiffOutput &Result, InterfaceInputOrder Order) { |
219 | Result.Kind = AD_Str_Vec; |
220 | for (const auto &IRef : CollectedIRefVec) |
221 | for (auto Targ : IRef.targets()) { |
222 | auto FoundIRef = llvm::any_of(Range: LookupIRefVec, P: [&](const auto LIRef) { |
223 | return llvm::is_contained(LIRef.targets(), Targ) && |
224 | IRef.getInstallName() == LIRef.getInstallName(); |
225 | }); |
226 | if (!FoundIRef) |
227 | addDiffForTargSlice<DiffStrVec, |
228 | DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( |
229 | Val: IRef.getInstallName(), Targ, Diff&: Result, Order); |
230 | } |
231 | } |
232 | |
233 | void findAndAddDiff( |
234 | const std::vector<std::pair<Target, std::string>> &CollectedPairs, |
235 | const std::vector<std::pair<Target, std::string>> &LookupPairs, |
236 | DiffOutput &Result, InterfaceInputOrder Order) { |
237 | Result.Kind = AD_Str_Vec; |
238 | for (const auto &Pair : CollectedPairs) { |
239 | auto FoundPair = llvm::find(Range: LookupPairs, Val: Pair); |
240 | if (FoundPair == LookupPairs.end()) |
241 | addDiffForTargSlice<DiffStrVec, |
242 | DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( |
243 | Val: StringRef(Pair.second), Targ: Pair.first, Diff&: Result, Order); |
244 | } |
245 | } |
246 | |
247 | void findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms, |
248 | InterfaceFile::const_symbol_range LookupSyms, |
249 | DiffOutput &Result, InterfaceInputOrder Order) { |
250 | Result.Kind = AD_Sym_Vec; |
251 | for (const auto *Sym : CollectedSyms) |
252 | for (const auto Targ : Sym->targets()) { |
253 | auto FoundSym = llvm::any_of(Range&: LookupSyms, P: [&](const auto LSym) { |
254 | return (Sym->getName() == LSym->getName() && |
255 | Sym->getKind() == LSym->getKind() && |
256 | Sym->getFlags() == LSym->getFlags() && |
257 | llvm::is_contained(LSym->targets(), Targ)); |
258 | }); |
259 | if (!FoundSym) |
260 | addDiffForTargSlice<DiffSymVec, SymScalar>(Val: Sym, Targ, Diff&: Result, Order); |
261 | } |
262 | } |
263 | |
264 | template <typename T> |
265 | DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) { |
266 | DiffOutput Diff(Attr); |
267 | if (LHS.getKind() == RHS.getKind()) { |
268 | Diff.Kind = LHS.getKind(); |
269 | Diff.Values.push_back(std::make_unique<T>(LHS)); |
270 | Diff.Values.push_back(std::make_unique<T>(RHS)); |
271 | } |
272 | return Diff; |
273 | } |
274 | |
275 | template <typename T> |
276 | DiffOutput recordDifferences(const std::vector<T> &LHS, |
277 | const std::vector<T> &RHS, std::string Attr) { |
278 | DiffOutput Diff(Attr); |
279 | Diff.Kind = AD_Str_Vec; |
280 | findAndAddDiff(LHS, RHS, Diff, lhs); |
281 | findAndAddDiff(RHS, LHS, Diff, rhs); |
282 | return Diff; |
283 | } |
284 | |
285 | DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS, |
286 | llvm::MachO::InterfaceFile::const_symbol_range RHS, |
287 | std::string Attr) { |
288 | DiffOutput Diff(Attr); |
289 | Diff.Kind = AD_Sym_Vec; |
290 | findAndAddDiff(CollectedSyms: LHS, LookupSyms: RHS, Result&: Diff, Order: lhs); |
291 | findAndAddDiff(CollectedSyms: RHS, LookupSyms: LHS, Result&: Diff, Order: rhs); |
292 | return Diff; |
293 | } |
294 | |
295 | std::vector<DiffOutput> |
296 | DiffEngine::findDifferences(const InterfaceFile *IFLHS, |
297 | const InterfaceFile *IFRHS) { |
298 | std::vector<DiffOutput> Output; |
299 | if (IFLHS->getInstallName() != IFRHS->getInstallName()) |
300 | Output.push_back(x: recordDifferences( |
301 | LHS: DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs, |
302 | IFLHS->getInstallName()), |
303 | RHS: DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs, |
304 | IFRHS->getInstallName()), |
305 | Attr: "Install Name" )); |
306 | |
307 | if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion()) |
308 | Output.push_back(x: recordDifferences( |
309 | LHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
310 | lhs, IFLHS->getCurrentVersion()), |
311 | RHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
312 | rhs, IFRHS->getCurrentVersion()), |
313 | Attr: "Current Version" )); |
314 | if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion()) |
315 | Output.push_back(x: recordDifferences( |
316 | LHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
317 | lhs, IFLHS->getCompatibilityVersion()), |
318 | RHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
319 | rhs, IFRHS->getCompatibilityVersion()), |
320 | Attr: "Compatibility Version" )); |
321 | if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion()) |
322 | Output.push_back( |
323 | x: recordDifferences(LHS: DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( |
324 | lhs, IFLHS->getSwiftABIVersion()), |
325 | RHS: DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( |
326 | rhs, IFRHS->getSwiftABIVersion()), |
327 | Attr: "Swift ABI Version" )); |
328 | |
329 | if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace()) |
330 | Output.push_back(x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
331 | lhs, IFLHS->isTwoLevelNamespace()), |
332 | RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
333 | rhs, IFRHS->isTwoLevelNamespace()), |
334 | Attr: "Two Level Namespace" )); |
335 | |
336 | if (IFLHS->isApplicationExtensionSafe() != |
337 | IFRHS->isApplicationExtensionSafe()) |
338 | Output.push_back( |
339 | x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
340 | lhs, IFLHS->isApplicationExtensionSafe()), |
341 | RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
342 | rhs, IFRHS->isApplicationExtensionSafe()), |
343 | Attr: "Application Extension Safe" )); |
344 | |
345 | if (IFLHS->hasSimulatorSupport() != IFRHS->hasSimulatorSupport()) |
346 | Output.push_back(x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
347 | lhs, IFLHS->hasSimulatorSupport()), |
348 | RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
349 | rhs, IFRHS->hasSimulatorSupport()), |
350 | Attr: "Simulator Support" )); |
351 | |
352 | if (IFLHS->isOSLibNotForSharedCache() != IFRHS->isOSLibNotForSharedCache()) |
353 | Output.push_back( |
354 | x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
355 | lhs, IFLHS->isOSLibNotForSharedCache()), |
356 | RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
357 | rhs, IFRHS->isOSLibNotForSharedCache()), |
358 | Attr: "Shared Cache Ineligible" )); |
359 | |
360 | if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries()) |
361 | Output.push_back(x: recordDifferences(LHS: IFLHS->reexportedLibraries(), |
362 | RHS: IFRHS->reexportedLibraries(), |
363 | Attr: "Reexported Libraries" )); |
364 | |
365 | if (IFLHS->rpaths() != IFRHS->rpaths()) |
366 | Output.push_back(x: recordDifferences(LHS: IFLHS->rpaths(), RHS: IFRHS->rpaths(), |
367 | Attr: "Run Path Search Paths" )); |
368 | |
369 | if (IFLHS->allowableClients() != IFRHS->allowableClients()) |
370 | Output.push_back(x: recordDifferences(LHS: IFLHS->allowableClients(), |
371 | RHS: IFRHS->allowableClients(), |
372 | Attr: "Allowable Clients" )); |
373 | |
374 | if (IFLHS->umbrellas() != IFRHS->umbrellas()) |
375 | Output.push_back(x: recordDifferences(LHS: IFLHS->umbrellas(), RHS: IFRHS->umbrellas(), |
376 | Attr: "Parent Umbrellas" )); |
377 | |
378 | if (!checkSymbolEquality(LHS: IFLHS->symbols(), RHS: IFRHS->symbols())) |
379 | Output.push_back( |
380 | x: recordDifferences(LHS: IFLHS->symbols(), RHS: IFRHS->symbols(), Attr: "Symbols" )); |
381 | |
382 | if (IFLHS->documents() != IFRHS->documents()) { |
383 | DiffOutput Docs("Inlined Reexported Frameworks/Libraries" ); |
384 | Docs.Kind = AD_Inline_Doc; |
385 | std::vector<StringRef> DocsInserted; |
386 | // Iterate through inline frameworks/libraries from interface file and find |
387 | // match based on install name. |
388 | for (auto DocLHS : IFLHS->documents()) { |
389 | auto Pair = llvm::find_if(Range: IFRHS->documents(), P: [&](const auto &DocRHS) { |
390 | return (DocLHS->getInstallName() == DocRHS->getInstallName()); |
391 | }); |
392 | // If a match found, recursively get differences between the pair. |
393 | if (Pair != IFRHS->documents().end()) { |
394 | InlineDoc PairDiff = |
395 | InlineDoc(DocLHS->getInstallName(), |
396 | findDifferences(IFLHS: DocLHS.get(), IFRHS: Pair->get())); |
397 | if (!PairDiff.DocValues.empty()) |
398 | Docs.Values.push_back( |
399 | x: std::make_unique<InlineDoc>(args: std::move(PairDiff))); |
400 | } |
401 | // No matching inlined library was found. |
402 | else |
403 | Docs.Values.push_back(x: std::make_unique<InlineDoc>( |
404 | args: InlineDoc(DocLHS->getInstallName(), getSingleIF(Interface: DocLHS.get(), Order: lhs), |
405 | /*IsMissingDoc=*/true))); |
406 | DocsInserted.push_back(x: DocLHS->getInstallName()); |
407 | } |
408 | for (auto DocRHS : IFRHS->documents()) { |
409 | auto WasGathered = |
410 | llvm::any_of(Range&: DocsInserted, P: [&](const auto &GatheredDoc) { |
411 | return (GatheredDoc == DocRHS->getInstallName()); |
412 | }); |
413 | if (!WasGathered) |
414 | Docs.Values.push_back(x: std::make_unique<InlineDoc>( |
415 | args: InlineDoc(DocRHS->getInstallName(), getSingleIF(Interface: DocRHS.get(), Order: rhs), |
416 | /*IsMissingDoc=*/true))); |
417 | } |
418 | if (!Docs.Values.empty()) |
419 | Output.push_back(x: std::move(Docs)); |
420 | } |
421 | return Output; |
422 | } |
423 | |
424 | template <typename T> |
425 | void printSingleVal(std::string Indent, const DiffOutput &Attr, |
426 | raw_ostream &OS) { |
427 | if (Attr.Values.empty()) |
428 | return; |
429 | OS << Indent << Attr.Name << "\n" ; |
430 | for (auto &RawItem : Attr.Values) |
431 | if (T *Item = dyn_cast<T>(RawItem.get())) |
432 | Item->print(OS, Indent); |
433 | } |
434 | |
435 | template <typename T> |
436 | T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) { |
437 | T *CastAttr = cast<T>(RawAttr.get()); |
438 | return CastAttr; |
439 | } |
440 | |
441 | template <typename T> void sortTargetValues(std::vector<T> &TargValues) { |
442 | llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) { |
443 | if (ValA.getOrder() == ValB.getOrder()) { |
444 | return ValA.getVal() < ValB.getVal(); |
445 | } |
446 | return ValA.getOrder() < ValB.getOrder(); |
447 | }); |
448 | } |
449 | |
450 | template <typename T> |
451 | void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) { |
452 | if (Attr.Values.empty()) { |
453 | OS << Indent << "'" << Attr.Name << "' differ by order\n" ; |
454 | return; |
455 | } |
456 | |
457 | OS << Indent << Attr.Name << "\n" ; |
458 | |
459 | std::vector<T *> SortedAttrs; |
460 | |
461 | llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), castValues<T>); |
462 | |
463 | llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) { |
464 | return ValA->Targ < ValB->Targ; |
465 | }); |
466 | |
467 | for (auto *Vec : SortedAttrs) { |
468 | sortTargetValues<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( |
469 | Vec->TargValues); |
470 | OS << Indent << "\t" << getTargetTripleName(Vec->Targ) << "\n" ; |
471 | for (auto &Item : Vec->TargValues) |
472 | Item.print(OS, Indent); |
473 | } |
474 | } |
475 | |
476 | template <> |
477 | void printVecVal<DiffSymVec>(std::string Indent, const DiffOutput &Attr, |
478 | raw_ostream &OS) { |
479 | if (Attr.Values.empty()) |
480 | return; |
481 | |
482 | OS << Indent << Attr.Name << "\n" ; |
483 | |
484 | std::vector<DiffSymVec *> SortedAttrs; |
485 | |
486 | llvm::transform(Range: Attr.Values, d_first: std::back_inserter(x&: SortedAttrs), |
487 | F: castValues<DiffSymVec>); |
488 | |
489 | llvm::sort(C&: SortedAttrs, Comp: [&](const auto &ValA, const auto &ValB) { |
490 | return ValA->Targ < ValB->Targ; |
491 | }); |
492 | for (auto *SymVec : SortedAttrs) { |
493 | sortTargetValues<SymScalar>(TargValues&: SymVec->TargValues); |
494 | OS << Indent << "\t" << getTargetTripleName(Targ: SymVec->Targ) << "\n" ; |
495 | for (auto &Item : SymVec->TargValues) |
496 | Item.print(OS, Indent, Targ: SymVec->Targ); |
497 | } |
498 | } |
499 | |
500 | void DiffEngine::printDifferences(raw_ostream &OS, |
501 | const std::vector<DiffOutput> &Diffs, |
502 | int IndentCounter) { |
503 | std::string Indent = std::string(IndentCounter, '\t'); |
504 | for (auto &Attr : Diffs) { |
505 | switch (Attr.Kind) { |
506 | case AD_Diff_Scalar_Str: |
507 | if (IndentCounter == 0) |
508 | printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(Indent, |
509 | Attr, OS); |
510 | break; |
511 | case AD_Diff_Scalar_PackedVersion: |
512 | printSingleVal< |
513 | DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>>(Indent, |
514 | Attr, OS); |
515 | break; |
516 | case AD_Diff_Scalar_Unsigned: |
517 | printSingleVal<DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>>(Indent, |
518 | Attr, OS); |
519 | break; |
520 | case AD_Diff_Scalar_Bool: |
521 | printSingleVal<DiffScalarVal<bool, AD_Diff_Scalar_Bool>>(Indent, Attr, |
522 | OS); |
523 | break; |
524 | case AD_Str_Vec: |
525 | printVecVal<DiffStrVec>(Indent, Attr, OS); |
526 | break; |
527 | case AD_Sym_Vec: |
528 | printVecVal<DiffSymVec>(Indent, Attr, OS); |
529 | break; |
530 | case AD_Inline_Doc: |
531 | if (!Attr.Values.empty()) { |
532 | OS << Indent << Attr.Name << "\n" ; |
533 | for (auto &Item : Attr.Values) { |
534 | if (InlineDoc *Doc = dyn_cast<InlineDoc>(Val: Item.get())) { |
535 | if (Doc->DocValues.empty()) |
536 | continue; |
537 | IndentCounter = 2; |
538 | // When only one input file contains an inlined library, print out |
539 | // the install name for it. Otherwise print out the different values |
540 | // by the install name. |
541 | if (Doc->IsMissingDoc) { |
542 | printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( |
543 | Indent: std::string(IndentCounter, '\t'), Attr: Doc->DocValues.front(), OS); |
544 | } else { |
545 | OS << Indent << "\t" << Doc->InstallName << "\n" ; |
546 | printDifferences(OS, Diffs: std::move(Doc->DocValues), IndentCounter); |
547 | } |
548 | } |
549 | } |
550 | } |
551 | break; |
552 | } |
553 | } |
554 | } |
555 | |
556 | bool DiffEngine::compareFiles(raw_ostream &OS) { |
557 | if (*FileLHS == *FileRHS) |
558 | return false; |
559 | OS << "< " << std::string(FileLHS->getPath().data()) << "\n> " |
560 | << std::string(FileRHS->getPath().data()) << "\n\n" ; |
561 | std::vector<DiffOutput> Diffs = findDifferences(IFLHS: FileLHS, IFRHS: FileRHS); |
562 | printDifferences(OS, Diffs, IndentCounter: 0); |
563 | return true; |
564 | } |
565 | |