1# -*- Python -*- vim: set syntax=python tabstop=4 expandtab cc=80: 2#===----------------------------------------------------------------------===## 3# 4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5# See https://llvm.org/LICENSE.txt for license information. 6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7# 8#===----------------------------------------------------------------------===## 9""" 10diff - A set of functions for diff-ing two symbol lists. 11""" 12 13from libcxx.sym_check import util 14 15 16def _symbol_difference(lhs, rhs): 17 lhs_names = set(((n['name'], n['type']) for n in lhs)) 18 rhs_names = set(((n['name'], n['type']) for n in rhs)) 19 diff_names = lhs_names - rhs_names 20 return [n for n in lhs if (n['name'], n['type']) in diff_names] 21 22 23def _find_by_key(sym_list, k): 24 for sym in sym_list: 25 if sym['name'] == k: 26 return sym 27 return None 28 29 30def added_symbols(old, new): 31 return _symbol_difference(new, old) 32 33 34def removed_symbols(old, new): 35 return _symbol_difference(old, new) 36 37 38def changed_symbols(old, new): 39 changed = [] 40 for old_sym in old: 41 if old_sym in new: 42 continue 43 new_sym = _find_by_key(new, old_sym['name']) 44 if (new_sym is not None and not new_sym in old 45 and old_sym != new_sym): 46 changed += [(old_sym, new_sym)] 47 return changed 48 49 50def diff(old, new): 51 added = added_symbols(old, new) 52 removed = removed_symbols(old, new) 53 changed = changed_symbols(old, new) 54 return added, removed, changed 55 56 57def report_diff(added_syms, removed_syms, changed_syms, names_only=False, 58 demangle=True): 59 def maybe_demangle(name): 60 return util.demangle_symbol(name) if demangle else name 61 62 report = '' 63 for sym in added_syms: 64 report += 'Symbol added: %s\n' % maybe_demangle(sym['name']) 65 if not names_only: 66 report += ' %s\n\n' % sym 67 if added_syms and names_only: 68 report += '\n' 69 for sym in removed_syms: 70 report += 'SYMBOL REMOVED: %s\n' % maybe_demangle(sym['name']) 71 if not names_only: 72 report += ' %s\n\n' % sym 73 if removed_syms and names_only: 74 report += '\n' 75 if not names_only: 76 for sym_pair in changed_syms: 77 old_sym, new_sym = sym_pair 78 old_str = '\n OLD SYMBOL: %s' % old_sym 79 new_str = '\n NEW SYMBOL: %s' % new_sym 80 report += ('SYMBOL CHANGED: %s%s%s\n\n' % 81 (maybe_demangle(old_sym['name']), 82 old_str, new_str)) 83 84 added = bool(len(added_syms) != 0) 85 abi_break = bool(len(removed_syms)) 86 if not names_only: 87 abi_break = abi_break or len(changed_syms) 88 if added or abi_break: 89 report += 'Summary\n' 90 report += ' Added: %d\n' % len(added_syms) 91 report += ' Removed: %d\n' % len(removed_syms) 92 if not names_only: 93 report += ' Changed: %d\n' % len(changed_syms) 94 if not abi_break: 95 report += 'Symbols added.' 96 else: 97 report += 'ABI BREAKAGE: SYMBOLS ADDED OR REMOVED!' 98 else: 99 report += 'Symbols match.' 100 is_different = abi_break or bool(len(added_syms)) \ 101 or bool(len(changed_syms)) 102 return report, abi_break, is_different 103