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 parser
.add_argument('--ignore-symbol',
93 help='do not process this symbol')
94 args
= parser
.parse_args()
97 if platform
.system() == 'Windows':
99 parser
.error('--dumpbin is mandatory')
100 lib_symbols
= get_symbols_dumpbin(args
.dumpbin
, args
.lib
)
103 parser
.error('--nm is mandatory')
104 lib_symbols
= get_symbols_nm(args
.nm
, args
.lib
)
106 # We can't run this test, but we haven't technically failed it either
107 # Return the GNU "skip" error code
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():
116 line
= line
.split('#')[0]
126 fields
= line
.split()
129 elif len(fields
) == 2:
130 qualifier
= fields
[0]
133 print(args
.symbols_file
+ ': invalid format: ' + line
)
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
)
142 if qualifier
== qualifier_optional
:
143 optional_symbols
.append(symbol
)
145 mandatory_symbols
.append(symbol
)
148 for symbol
in lib_symbols
:
149 if symbol
in mandatory_symbols
:
151 if symbol
in optional_symbols
:
153 if args
.ignore_symbol
and symbol
in args
.ignore_symbol
:
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
159 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36022#c4
161 unknown_symbols
.append(symbol
)
164 sym
for sym
in mandatory_symbols
if sym
not in lib_symbols
167 for symbol
in unknown_symbols
:
168 print(args
.lib
+ ': unknown symbol exported: ' + symbol
)
170 for symbol
in missing_symbols
:
171 print(args
.lib
+ ': missing symbol: ' + symbol
)
173 if unknown_symbols
or missing_symbols
:
178 if __name__
== '__main__':