mirror of
https://github.com/zeldaret/ph
synced 2026-05-25 07:22:58 -04:00
133 lines
4.1 KiB
Python
133 lines
4.1 KiB
Python
import argparse
|
|
import os
|
|
from pathlib import Path
|
|
import platform
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
def eprint(*args, **kwargs):
|
|
print(*args, file=sys.stderr, **kwargs)
|
|
|
|
parser = argparse.ArgumentParser(description='Generates .inc files with external symbols used by Assembly code')
|
|
parser.add_argument('files', nargs='+', help='.s file to generate .inc file for')
|
|
|
|
args = parser.parse_args()
|
|
|
|
tools_dir = Path(os.path.dirname(os.path.realpath(__file__)))
|
|
as_path = tools_dir / 'mwccarm' / '2.0' / 'sp1p5' / 'mwasmarm.exe'
|
|
root_dir = tools_dir.parent
|
|
asm_dir = root_dir / 'asm'
|
|
|
|
MSG_UNKNOWN_IDENTIFIER = 'Unknown identifier,'
|
|
MSG_DUPLICATE_DECLARATION = 'Incompatible duplicate declaration of'
|
|
|
|
if platform.system() == 'Windows': as_cmd = [str(as_path)]
|
|
else: as_cmd = ['wine', str(as_path)]
|
|
|
|
as_cmd.extend([
|
|
'-nolink',
|
|
'-proc', 'arm5te',
|
|
'-msgstyle', 'gcc',
|
|
'-DEUR', '-DUSA',
|
|
f'-I{asm_dir}',
|
|
])
|
|
|
|
def get_unknown_symbols(file: Path):
|
|
with tempfile.NamedTemporaryFile(delete=True) as tmp_obj_file:
|
|
# Run assembler
|
|
cmd = as_cmd.copy()
|
|
cmd.extend(['-o', tmp_obj_file.name])
|
|
cmd.append(str(file))
|
|
try:
|
|
output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
|
|
output = output.decode()
|
|
except subprocess.CalledProcessError as e:
|
|
output = e.stdout.decode()
|
|
|
|
# Get unknown identifiers
|
|
lines = output.splitlines()
|
|
symbol = ''
|
|
symbols: set[str] = set()
|
|
skip = False
|
|
for line in lines:
|
|
# print(line)
|
|
if ':' not in line: continue
|
|
line = line.rsplit(':', 1)[-1].strip()
|
|
if line.startswith(MSG_DUPLICATE_DECLARATION):
|
|
symbols.add(symbol)
|
|
skip = True
|
|
elif line.startswith(MSG_UNKNOWN_IDENTIFIER):
|
|
symbols.add(symbol)
|
|
skip = False
|
|
symbol = line[len(MSG_UNKNOWN_IDENTIFIER):].strip()
|
|
elif not skip:
|
|
symbol += line.strip()
|
|
symbols.add(symbol)
|
|
if '' in symbols: symbols.remove('')
|
|
if len(symbols) == 0:
|
|
return []
|
|
|
|
return sorted(list(symbols))
|
|
|
|
files_updated = 0
|
|
inc_files_created = 0
|
|
|
|
def generate_externs(file: Path):
|
|
global inc_files_created
|
|
|
|
with tempfile.NamedTemporaryFile('w', suffix='.s', delete=True) as tmp_asm_file:
|
|
file_name = file.name.rsplit('.', 1)[0]
|
|
inc_file_name = f'{file_name}.inc'
|
|
inc_path = file.parent / inc_file_name
|
|
|
|
# Comment out '.include ".../my_file.inc"
|
|
with open(file, 'r') as f:
|
|
contents = f.read()
|
|
lines = contents.splitlines()
|
|
has_inc_file = False
|
|
for i in range(len(lines)):
|
|
if not lines[i].strip().startswith('.include'): continue
|
|
if inc_file_name not in lines[i]: continue
|
|
lines[i] = f';{lines[i]}'
|
|
has_inc_file = True
|
|
break
|
|
|
|
# Get unknown symbols
|
|
for line in lines:
|
|
tmp_asm_file.write(line)
|
|
tmp_asm_file.write('\n')
|
|
tmp_asm_file.flush()
|
|
symbols = get_unknown_symbols(tmp_asm_file.name)
|
|
|
|
# Create inc file
|
|
with open(inc_path, 'w') as f:
|
|
f.write('#pragma once\n')
|
|
for i, symbol in enumerate(symbols):
|
|
# Skip duplicates
|
|
if symbol in symbols[:i]: continue
|
|
f.write(f'.extern {symbol}\n')
|
|
|
|
# Add .include
|
|
if not has_inc_file:
|
|
relative_inc_path = inc_path.relative_to('asm/')
|
|
lines.insert(0, f' .include "{relative_inc_path}"')
|
|
with open(file, 'w') as f:
|
|
for line in lines:
|
|
f.write(line)
|
|
f.write('\n')
|
|
inc_files_created += 1
|
|
|
|
|
|
for file in args.files:
|
|
if not file.endswith('.s'): continue
|
|
path = Path(file)
|
|
if len(args.files) > 1:
|
|
print(path)
|
|
generate_externs(path)
|
|
files_updated += 1
|
|
|
|
if len(args.files) > 1:
|
|
print(f"Updated {files_updated} file(s)")
|
|
print(f"Created {inc_files_created} .inc file(s)")
|