| #!/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()) |