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