8 # This list contains symbols that _might_ be exported for some platforms
22 def get_symbols_nm(nm
, lib
):
24 List all the (non platform-specific) symbols exported by the library
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():
33 if len(fields
) == 2 or fields
[1] == 'U':
35 symbol_name
= fields
[0]
36 if platform_name
== 'Linux':
37 if symbol_name
in PLATFORM_SYMBOLS
:
39 elif platform_name
== 'Darwin':
40 assert symbol_name
[0] == '_'
41 symbol_name
= symbol_name
[1:]
42 symbols
.append(symbol_name
)
46 def get_symbols_dumpbin(dumpbin
, lib
):
48 List all the (non platform-specific) symbols exported by the library
52 output
= subprocess
.check_output([dumpbin
, '/exports', lib
],
53 stderr
=open(os
.devnull
, 'w')).decode("ascii")
54 for line
in output
.splitlines():
56 # The lines with the symbols are made of at least 4 columns; see details below
60 # Making sure the first 3 columns are a dec counter, a hex counter
62 _
= int(fields
[0], 10)
63 _
= int(fields
[1], 16)
64 _
= int(fields
[2], 16)
67 symbol_name
= fields
[3]
69 if symbol_name
[0] == '_':
70 symbol_name
= symbol_name
[1:].split('@')[0]
71 symbols
.append(symbol_name
)
76 parser
= argparse
.ArgumentParser()
77 parser
.add_argument('--symbols-file',
80 help='path to file containing symbols')
81 parser
.add_argument('--lib',
84 help='path to library')
85 parser
.add_argument('--nm',
87 help='path to binary (or name in $PATH)')
88 parser
.add_argument('--dumpbin',
90 help='path to binary (or name in $PATH)')
91 args
= parser
.parse_args()
94 if platform
.system() == 'Windows':
96 parser
.error('--dumpbin is mandatory')
97 lib_symbols
= get_symbols_dumpbin(args
.dumpbin
, args
.lib
)
100 parser
.error('--nm is mandatory')
101 lib_symbols
= get_symbols_nm(args
.nm
, args
.lib
)
103 # We can't run this test, but we haven't technically failed it either
104 # Return the GNU "skip" error code
106 mandatory_symbols
= []
107 optional_symbols
= []
108 with
open(args
.symbols_file
) as symbols_file
:
109 qualifier_optional
= '(optional)'
110 for line
in symbols_file
.readlines():
113 line
= line
.split('#')[0]
123 fields
= line
.split()
126 elif len(fields
) == 2:
127 qualifier
= fields
[0]
130 print(args
.symbols_file
+ ': invalid format: ' + line
)
133 # The only supported qualifier is 'optional', which means the
134 # symbol doesn't have to be exported by the library
135 if qualifier
and not qualifier
== qualifier_optional
:
136 print(args
.symbols_file
+ ': invalid qualifier: ' + qualifier
)
139 if qualifier
== qualifier_optional
:
140 optional_symbols
.append(symbol
)
142 mandatory_symbols
.append(symbol
)
145 for symbol
in lib_symbols
:
146 if symbol
in mandatory_symbols
:
148 if symbol
in optional_symbols
:
150 if symbol
[:2] == '_Z':
151 # Ignore random C++ symbols
152 #TODO: figure out if there's any way to avoid exporting them in the first place
154 unknown_symbols
.append(symbol
)
157 sym
for sym
in mandatory_symbols
if sym
not in lib_symbols
160 for symbol
in unknown_symbols
:
161 print(args
.lib
+ ': unknown symbol exported: ' + symbol
)
163 for symbol
in missing_symbols
:
164 print(args
.lib
+ ': missing symbol: ' + symbol
)
166 if unknown_symbols
or missing_symbols
:
171 if __name__
== '__main__':