blob: bc220ce6211b0bf835c934614303c2ec8846753a [file] [edit]
#!/usr/bin/env python2
# Copyright 2019 The Emscripten Authors. All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.
"""Update 'symbols' files based on the contents of libraries in the cache.
The symbols files looks like the output of `nm` but only contain external
symbols and the symbols from all object in that archive are sorted and
de-duplicated.
"""
import argparse
import glob
import os
import sys
root_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
sys.path.insert(0, root_dir)
from tools import shared, cache
def filter_and_sort(symbols):
lines = symbols.splitlines()
lines = [l.rstrip() for l in lines]
lines = [l for l in lines if l and l[-1] != ':']
# Extract symbol type and name
symbols = [l.split()[-2:] for l in lines]
# Remove local symbols (lowercase type name)
symbols = [(typ, name) for typ, name in symbols if typ.isupper()]
symbol_map = {}
for sym_type, sym_name in symbols:
assert sym_type in ('W', 'T', 'D', 'C', 'U')
existing_type = symbol_map.get(sym_name)
if not existing_type:
symbol_map[sym_name] = sym_type
continue
if existing_type == 'U' and sym_type != 'U':
symbol_map[sym_name] = sym_type
elif existing_type == 'W' and sym_type not in ('W', 'U'):
symbol_map[sym_name] = sym_type
elif sym_type not in ('W', 'U'):
# We don't expect to see two defined version of a given symbol
if existing_type != sym_type:
print('Unexpected symbol types found: %s: %s vs %s' % (sym_name, existing_type, sym_type))
symbols = [(typ, name) for name, typ in symbol_map.items()]
# sort by name
symbols.sort(key=lambda s: s[1])
lines = ['# Auto-generated by tools/update_symbols.py. DO NOT EDIT.']
for typ, name in symbols:
if typ == 'U':
lines.append(" %s %s" % (typ, name))
else:
lines.append("-------- %s %s" % (typ, name))
return '\n'.join(lines) + '\n'
def handle_symbol_file(args, symbol_file):
"""Regenerate the contents of a given symbol file."""
if args.filter_in_place:
output = open(symbol_file).read()
else:
basename = os.path.splitext(os.path.basename(symbol_file))[0]
if not basename.startswith('lib'):
basename = 'lib' + basename
if basename.endswith('_asmjs'):
basename = basename.rsplit('_asmjs', 1)[0]
basename = basename.replace('cxx', 'c++')
cache_dir = cache.Cache().dirname
pattern = os.path.join(cache_dir, basename + '.*')
libs = glob.glob(pattern)
if basename == 'libgl':
# For libgl we generate the symbol list based on a superset of all
# library variants.
pattern = os.path.join(cache_dir, basename + '-*.*')
libs += glob.glob(pattern)
if not libs:
print(cache_dir)
print("%s: Unable to find library to generate symbols from" % symbol_file)
return
print('Generating %s based on syms from: %s' % (basename, libs))
output = ''
for lib in libs:
output += shared.run_process([shared.LLVM_NM, '-g', lib], stdout=shared.PIPE).stdout
new_symbols = filter_and_sort(output)
with open(symbol_file, 'w') as f:
f.write(new_symbols)
def main():
parser = argparse.ArgumentParser(description=__doc__, usage="usage: %prog [options] [<file>...]")
parser.add_argument('--asmjs', dest='asmjs', action='store_true',
help='Use asmjs library files')
parser.add_argument('-i', dest='filter_in_place', action='store_true',
help='filter symbols in place rather than re-generating from library file')
parser.add_argument('files', metavar='N', type=int, nargs='*',
help='symbol files to regenerate (default: all)')
args = parser.parse_args()
if args.asmjs:
shared.Settings.WASM_BACKEND = False
shared.Settings.ASM_JS = True
if args.files:
for f in args.files:
handle_symbol_file(args, f)
else:
symbols_dir = os.path.join(root_dir, 'system', 'lib')
for symbol_file in glob.glob(os.path.join(symbols_dir, '*.symbols')):
if shared.Settings.WASM_BACKEND:
if symbol_file.endswith('_asmjs.symbols'):
print('skipping asmjs-only file: %s' % symbol_file)
continue
else:
asmjs_only_file = os.path.splitext(symbol_file)[0] + '_asmjs.symbols'
if os.path.exists(asmjs_only_file):
print('skipping wasm-only file: %s' % symbol_file)
continue
handle_symbol_file(args, symbol_file)
return 0
if __name__ == '__main__':
sys.exit(main())