powermac-rom/AsmSymScanner.py

138 lines
4.2 KiB
Python
Executable File

#!/usr/bin/env python3
import sys
import re
import os.path
HEADER = 'AUTO-GENERATED SYMBOL LIST'
ENC = 'macroman'
RECORD = re.compile(r'^[^;\s]*\s+record', re.I)
ENDR = re.compile(r'^\s+endr', re.I)
SETEQU = re.compile(r'^\S+\s+(dc\.|ds\.|set|equ|record)', flags=re.I)
def neaten_name(x):
bn = os.path.basename(x)
bn = os.path.splitext(bn)[0]
return bn
all_args = list(sys.argv[1:])
opts = []
while all_args and all_args[0].startswith('-'):
opts.append(all_args.pop(0))
fnames = all_args
fnames = [fn for fn in fnames if HEADER in open(fn, encoding=ENC).read(2048)]
fexports = []
for name in fnames:
exports = set()
can_keep = False
with open(name, encoding=ENC) as f:
forbid = False
for l in f:
if not forbid and RECORD.match(l):
forbid=True
elif forbid and ENDR.match(l):
forbid=False
if not forbid:
m = re.match(r'^(\w+)', l)
if m and not SETEQU.match(l):
exports.add(m.group(1))
fexports.append(exports)
regex_cache = {}
for export_set in fexports:
for export in export_set:
if export not in regex_cache:
regex_cache[export] = re.compile(r'^[^;]+\b' + re.escape(export) + r'\b', flags=re.I)
export_matrix = set()
for expname, exports in zip(fnames, fexports):
for impname in fnames:
if impname == expname: continue
with open(impname, encoding=ENC) as f:
for l in f:
for e in exports:
er = regex_cache[e]
if er.match(l):
export_matrix.add((expname, impname, e))
for exp, imp, sym in sorted(export_matrix):
print(exp.rpartition('/')[2], imp.rpartition('/')[2], sym)
dict_exporter = {}
for exp, imp, sym in export_matrix:
dict_exporter[sym] = exp
dict_importers = {}
for exp, imp, sym in export_matrix:
if sym not in dict_importers:
dict_importers[sym] = set()
dict_importers[sym].add(imp)
dict_fileimports = {}
for exp, imp, sym in export_matrix:
if imp not in dict_fileimports:
dict_fileimports[imp] = set()
dict_fileimports[imp].add(sym)
dict_fileexports = {}
for exp, imp, sym in export_matrix:
if exp not in dict_fileexports:
dict_fileexports[exp] = set()
dict_fileexports[exp].add(sym)
for f in fnames:
if f not in dict_fileimports:
dict_fileimports[f] = set()
if f not in dict_fileexports:
dict_fileexports[f] = set()
for path in fnames:
with open(path, encoding=ENC) as i:
with open(path + '~', 'w', encoding=ENC) as o:
for l in i:
o.write(l)
if HEADER in l:
prefix, _, suffix = l.partition(HEADER)
imports = sorted(dict_fileimports[path])
exports = sorted(dict_fileexports[path])
if imports:
dict_exp_to_imp = {}
for imp in imports:
exporter = dict_exporter[imp]
if exporter not in dict_exp_to_imp:
dict_exp_to_imp[exporter] = set()
dict_exp_to_imp[exporter].add(imp)
o.write(prefix + 'IMPORTS:' + suffix)
for exp, imps in sorted(dict_exp_to_imp.items()):
o.write(prefix + ' ' + neaten_name(exp) + suffix)
for imp in sorted(imps):
o.write(prefix + ' ' + imp + suffix)
if exports:
o.write(prefix + 'EXPORTS:' + suffix)
for exp in exports:
importers = sorted(dict_importers[exp])
importers = [neaten_name(x) for x in importers]
impstring = ' (=> %s)' % (', '.join(importers))
o.write(prefix + ' ' + exp + impstring + suffix)
for l in i:
if not l.startswith(prefix):
o.write(l)
break
open(path, 'wb').write(open(path + '~', 'rb').read().replace(b'\n', b'\r'))
os.unlink(path + '~')