2017-10-24 05:29:53 -03:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# Script checking that all symbols exported by libpython start with Py or _Py
|
|
|
|
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import sysconfig
|
|
|
|
|
|
|
|
|
|
|
|
def get_exported_symbols():
|
|
|
|
LIBRARY = sysconfig.get_config_var('LIBRARY')
|
|
|
|
if not LIBRARY:
|
|
|
|
raise Exception("failed to get LIBRARY")
|
|
|
|
|
|
|
|
args = ('nm', '-p', LIBRARY)
|
|
|
|
print("+ %s" % ' '.join(args))
|
|
|
|
proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True)
|
|
|
|
if proc.returncode:
|
|
|
|
sys.stdout.write(proc.stdout)
|
|
|
|
sys.exit(proc.returncode)
|
|
|
|
|
|
|
|
stdout = proc.stdout.rstrip()
|
|
|
|
if not stdout:
|
|
|
|
raise Exception("command output is empty")
|
|
|
|
return stdout
|
|
|
|
|
|
|
|
|
|
|
|
def get_smelly_symbols(stdout):
|
|
|
|
symbols = []
|
|
|
|
ignored_symtypes = set()
|
2018-02-27 16:40:37 -04:00
|
|
|
|
|
|
|
allowed_prefixes = ('Py', '_Py')
|
|
|
|
if sys.platform == 'darwin':
|
|
|
|
allowed_prefixes += ('__Py',)
|
|
|
|
|
2017-10-24 05:29:53 -03:00
|
|
|
for line in stdout.splitlines():
|
|
|
|
# Split line '0000000000001b80 D PyTextIOWrapper_Type'
|
|
|
|
if not line:
|
|
|
|
continue
|
|
|
|
|
|
|
|
parts = line.split(maxsplit=2)
|
|
|
|
if len(parts) < 3:
|
|
|
|
continue
|
|
|
|
|
|
|
|
symtype = parts[1].strip()
|
|
|
|
# Ignore private symbols.
|
|
|
|
#
|
|
|
|
# If lowercase, the symbol is usually local; if uppercase, the symbol
|
|
|
|
# is global (external). There are however a few lowercase symbols that
|
|
|
|
# are shown for special global symbols ("u", "v" and "w").
|
|
|
|
if symtype.islower() and symtype not in "uvw":
|
|
|
|
ignored_symtypes.add(symtype)
|
|
|
|
continue
|
|
|
|
|
|
|
|
symbol = parts[-1]
|
2018-02-27 16:40:37 -04:00
|
|
|
if symbol.startswith(allowed_prefixes):
|
2017-10-24 05:29:53 -03:00
|
|
|
continue
|
|
|
|
symbol = '%s (type: %s)' % (symbol, symtype)
|
|
|
|
symbols.append(symbol)
|
|
|
|
|
|
|
|
if ignored_symtypes:
|
|
|
|
print("Ignored symbol types: %s" % ', '.join(sorted(ignored_symtypes)))
|
|
|
|
print()
|
|
|
|
return symbols
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
nm_output = get_exported_symbols()
|
|
|
|
symbols = get_smelly_symbols(nm_output)
|
|
|
|
|
|
|
|
if not symbols:
|
|
|
|
print("OK: no smelly symbol found")
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
symbols.sort()
|
|
|
|
for symbol in symbols:
|
|
|
|
print("Smelly symbol: %s" % symbol)
|
|
|
|
print()
|
|
|
|
print("ERROR: Found %s smelly symbols!" % len(symbols))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|