1#!/usr/bin/env python3 2 3import sys, os, shutil, subprocess, re, difflib 4 5os.environ['LC_ALL'] = 'C' # otherwise 'nm' prints in wrong order 6 7builddir = os.getenv ('builddir', os.path.dirname (__file__)) 8libs = os.getenv ('libs', '.libs') 9 10IGNORED_SYMBOLS = '|'.join(['_fini', '_init', '_fdata', '_ftext', '_fbss', 11 '__bss_start', '__bss_start__', '__bss_end__', '_edata', '_end', '_bss_end__', 12 '__end__', '__gcov_.*', 'llvm_.*', 'flush_fn_list', 'writeout_fn_list', 'mangle_path']) 13 14nm = shutil.which ('nm') 15if not nm: 16 print ('check-symbols.py: \'nm\' not found; skipping test') 17 sys.exit (77) 18 19cxxflit = shutil.which ('c++filt') 20 21tested = False 22stat = 0 23 24for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-icu', 'harfbuzz-gobject']: 25 for suffix in ['so', 'dylib']: 26 so = os.path.join (builddir, libs, 'lib%s.%s' % (soname, suffix)) 27 if not os.path.exists (so): continue 28 29 # On macOS, C symbols are prefixed with _ 30 symprefix = '_' if suffix == 'dylib' else '' 31 32 EXPORTED_SYMBOLS = [s.split ()[2] 33 for s in re.findall (r'^.+ [BCDGIRST] .+$', subprocess.check_output ([nm, so]).decode ('utf-8'), re.MULTILINE) 34 if not re.match (r'.* %s(%s)\b' % (symprefix, IGNORED_SYMBOLS), s)] 35 36 # run again c++flit also if is available 37 if cxxflit: 38 EXPORTED_SYMBOLS = subprocess.check_output ( 39 [cxxflit], input='\n'.join (EXPORTED_SYMBOLS).encode () 40 ).decode ('utf-8').splitlines () 41 42 prefix = (symprefix + os.path.basename (so)).replace ('libharfbuzz', 'hb').replace ('-', '_').split ('.')[0] 43 44 print ('Checking that %s does not expose internal symbols' % so) 45 suspicious_symbols = [x for x in EXPORTED_SYMBOLS if not re.match (r'^%s(_|$)' % prefix, x)] 46 if suspicious_symbols: 47 print ('Ouch, internal symbols exposed:', suspicious_symbols) 48 stat = 1 49 50 def_path = os.path.join (builddir, soname + '.def') 51 if not os.path.exists (def_path): 52 print ('\'%s\' not found; skipping' % def_path) 53 else: 54 print ('Checking that %s has the same symbol list as %s' % (so, def_path)) 55 with open (def_path, 'r', encoding='utf-8') as f: def_file = f.read () 56 diff_result = list (difflib.context_diff ( 57 def_file.splitlines (), 58 ['EXPORTS'] + [re.sub ('^%shb' % symprefix, 'hb', x) for x in EXPORTED_SYMBOLS] + 59 # cheat: copy the last line from the def file! 60 [def_file.splitlines ()[-1]] 61 )) 62 63 if diff_result: 64 print ('\n'.join (diff_result)) 65 stat = 1 66 67 tested = True 68 69if not tested: 70 print ('check-symbols.sh: no shared libraries found; skipping test') 71 sys.exit (77) 72 73sys.exit (stat) 74