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