egl: drop unused _EGLDriver from WaitClient()
[mesa.git] / bin / symbols-check.py
1 #!/usr/bin/env python
2
3 import argparse
4 import os
5 import platform
6 import subprocess
7
8 # This list contains symbols that _might_ be exported for some platforms
9 PLATFORM_SYMBOLS = [
10 '__bss_end__',
11 '__bss_start__',
12 '__bss_start',
13 '__end__',
14 '_bss_end__',
15 '_edata',
16 '_end',
17 '_fini',
18 '_init',
19 ]
20
21
22 def get_symbols_nm(nm, lib):
23 '''
24 List all the (non platform-specific) symbols exported by the library
25 using `nm`
26 '''
27 symbols = []
28 platform_name = platform.system()
29 output = subprocess.check_output([nm, '-gP', lib],
30 stderr=open(os.devnull, 'w')).decode("ascii")
31 for line in output.splitlines():
32 fields = line.split()
33 if len(fields) == 2 or fields[1] == 'U':
34 continue
35 symbol_name = fields[0]
36 if platform_name == 'Linux':
37 if symbol_name in PLATFORM_SYMBOLS:
38 continue
39 elif platform_name == 'Darwin':
40 assert symbol_name[0] == '_'
41 symbol_name = symbol_name[1:]
42 symbols.append(symbol_name)
43 return symbols
44
45
46 def get_symbols_dumpbin(dumpbin, lib):
47 '''
48 List all the (non platform-specific) symbols exported by the library
49 using `dumpbin`
50 '''
51 symbols = []
52 output = subprocess.check_output([dumpbin, '/exports', lib],
53 stderr=open(os.devnull, 'w')).decode("ascii")
54 for line in output.splitlines():
55 fields = line.split()
56 # The lines with the symbols are made of at least 4 columns; see details below
57 if len(fields) < 4:
58 continue
59 try:
60 # Making sure the first 3 columns are a dec counter, a hex counter
61 # and a hex address
62 _ = int(fields[0], 10)
63 _ = int(fields[1], 16)
64 _ = int(fields[2], 16)
65 except ValueError:
66 continue
67 symbol_name = fields[3]
68 # De-mangle symbols
69 if symbol_name[0] == '_':
70 symbol_name = symbol_name[1:].split('@')[0]
71 symbols.append(symbol_name)
72 return symbols
73
74
75 def main():
76 parser = argparse.ArgumentParser()
77 parser.add_argument('--symbols-file',
78 action='store',
79 required=True,
80 help='path to file containing symbols')
81 parser.add_argument('--lib',
82 action='store',
83 required=True,
84 help='path to library')
85 parser.add_argument('--nm',
86 action='store',
87 help='path to binary (or name in $PATH)')
88 parser.add_argument('--dumpbin',
89 action='store',
90 help='path to binary (or name in $PATH)')
91 parser.add_argument('--ignore-symbol',
92 action='append',
93 help='do not process this symbol')
94 args = parser.parse_args()
95
96 try:
97 if platform.system() == 'Windows':
98 if not args.dumpbin:
99 parser.error('--dumpbin is mandatory')
100 lib_symbols = get_symbols_dumpbin(args.dumpbin, args.lib)
101 else:
102 if not args.nm:
103 parser.error('--nm is mandatory')
104 lib_symbols = get_symbols_nm(args.nm, args.lib)
105 except:
106 # We can't run this test, but we haven't technically failed it either
107 # Return the GNU "skip" error code
108 exit(77)
109 mandatory_symbols = []
110 optional_symbols = []
111 with open(args.symbols_file) as symbols_file:
112 qualifier_optional = '(optional)'
113 for line in symbols_file.readlines():
114
115 # Strip comments
116 line = line.split('#')[0]
117 line = line.strip()
118 if not line:
119 continue
120
121 # Line format:
122 # [qualifier] symbol
123 qualifier = None
124 symbol = None
125
126 fields = line.split()
127 if len(fields) == 1:
128 symbol = fields[0]
129 elif len(fields) == 2:
130 qualifier = fields[0]
131 symbol = fields[1]
132 else:
133 print(args.symbols_file + ': invalid format: ' + line)
134 exit(1)
135
136 # The only supported qualifier is 'optional', which means the
137 # symbol doesn't have to be exported by the library
138 if qualifier and not qualifier == qualifier_optional:
139 print(args.symbols_file + ': invalid qualifier: ' + qualifier)
140 exit(1)
141
142 if qualifier == qualifier_optional:
143 optional_symbols.append(symbol)
144 else:
145 mandatory_symbols.append(symbol)
146
147 unknown_symbols = []
148 for symbol in lib_symbols:
149 if symbol in mandatory_symbols:
150 continue
151 if symbol in optional_symbols:
152 continue
153 if args.ignore_symbol and symbol in args.ignore_symbol:
154 continue
155 if symbol[:2] == '_Z':
156 # As ajax found out, the compiler intentionally exports symbols
157 # that we explicitely asked it not to export, and we can't do
158 # anything about it:
159 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36022#c4
160 continue
161 unknown_symbols.append(symbol)
162
163 missing_symbols = [
164 sym for sym in mandatory_symbols if sym not in lib_symbols
165 ]
166
167 for symbol in unknown_symbols:
168 print(args.lib + ': unknown symbol exported: ' + symbol)
169
170 for symbol in missing_symbols:
171 print(args.lib + ': missing symbol: ' + symbol)
172
173 if unknown_symbols or missing_symbols:
174 exit(1)
175 exit(0)
176
177
178 if __name__ == '__main__':
179 main()