mirror of
https://github.com/zeldaret/ph
synced 2026-05-23 15:01:37 -04:00
Add make gen_externs
This commit is contained in:
@@ -5,4 +5,3 @@ ph_*/
|
||||
*bios.bin
|
||||
/m2ctx
|
||||
*.sav
|
||||
_gen_externs_tmp*
|
||||
|
||||
@@ -25,6 +25,7 @@ ASSETS_TXT := assets.txt
|
||||
ASM_FILES := $(shell find asm -name *.s)
|
||||
CXX_FILES := $(shell find src -name *.cpp)
|
||||
ASM_OBJS = $(ASM_FILES:%.s=$(TARGET_DIR)/%.s.o)
|
||||
ASM_INCS = $(ASM_FILES:%.s=%.inc)
|
||||
CXX_OBJS = $(CXX_FILES:%.cpp=$(TARGET_DIR)/%.cpp.o)
|
||||
|
||||
OV_BINS := $(wildcard $(TARGET_DIR)/overlays/*.bin)
|
||||
@@ -58,6 +59,7 @@ help:
|
||||
@echo "make eur ........................ Builds European ROM"
|
||||
@echo "make usa ........................ Builds American ROM"
|
||||
@echo "make clean ...................... Clean up build files"
|
||||
@echo "make gen_externs ................ Generates .inc files for Assembly"
|
||||
|
||||
.PHONY: eur
|
||||
eur:
|
||||
@@ -128,3 +130,9 @@ compress: $(OV_LZS)
|
||||
|
||||
$(OV_LZS): %.lz: %.bin
|
||||
$(TOOLS_DIR)/compress/compress -p -i $< -o $@
|
||||
|
||||
.PHONY: gen_externs
|
||||
gen_externs: $(ASM_INCS)
|
||||
|
||||
$(ASM_INCS): %.inc: %.s
|
||||
python $(TOOLS_DIR)/gen_externs.py $<
|
||||
|
||||
+72
-67
@@ -2,9 +2,9 @@ import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
@@ -18,11 +18,9 @@ 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'
|
||||
tmp_file = '_gen_externs_tmp'
|
||||
tmp_obj_file = f'{tmp_file}.o'
|
||||
tmp_asm_file = f'{tmp_file}.s'
|
||||
|
||||
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)]
|
||||
@@ -32,39 +30,45 @@ as_cmd.extend([
|
||||
'-proc', 'arm5te',
|
||||
'-msgstyle', 'gcc',
|
||||
'-DEUR', '-DUSA',
|
||||
'-o', tmp_obj_file,
|
||||
f'-I{asm_dir}',
|
||||
])
|
||||
|
||||
def get_unknown_symbols(file: Path):
|
||||
# Run assembler
|
||||
cmd = as_cmd.copy()
|
||||
cmd.append(str(file))
|
||||
try:
|
||||
output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
|
||||
output = output.decode()
|
||||
os.remove(tmp_obj_file)
|
||||
except subprocess.CalledProcessError as e:
|
||||
output = e.stdout.decode()
|
||||
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: list[str] = []
|
||||
for line in lines:
|
||||
if not line.startswith(tmp_asm_file): continue
|
||||
if ':' not in line: continue
|
||||
line = line[line.index(':', len(tmp_asm_file) + 1) + 1:].strip()
|
||||
if line.startswith(MSG_UNKNOWN_IDENTIFIER):
|
||||
if symbol != '': symbols.append(symbol)
|
||||
symbol = line[len(MSG_UNKNOWN_IDENTIFIER):].strip()
|
||||
else:
|
||||
symbol += line.strip()
|
||||
if symbol != '': symbols.append(symbol)
|
||||
if len(symbols) == 0:
|
||||
return []
|
||||
# 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)
|
||||
symbols.remove('')
|
||||
if len(symbols) == 0:
|
||||
return []
|
||||
|
||||
return sorted(symbols)
|
||||
return sorted(list(symbols))
|
||||
|
||||
files_updated = 0
|
||||
inc_files_created = 0
|
||||
@@ -72,56 +76,57 @@ inc_files_created = 0
|
||||
def generate_externs(file: Path):
|
||||
global inc_files_created
|
||||
|
||||
file_name = file.name.rsplit('.', 1)[0]
|
||||
inc_file_name = f'{file_name}.inc'
|
||||
inc_path = file.parent / 'include' / inc_file_name
|
||||
with tempfile.NamedTemporaryFile('w', suffix='.s', delete=True, encoding='Shift-JIS') 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', encoding='Shift-JIS') 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
|
||||
with open(f'{tmp_file}.s', 'w', encoding='Shift-JIS') as f:
|
||||
# Comment out '.include ".../my_file.inc"
|
||||
with open(file, 'r', encoding='Shift-JIS') 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:
|
||||
f.write(line)
|
||||
f.write('\n')
|
||||
symbols = get_unknown_symbols(tmp_asm_file)
|
||||
os.remove(tmp_asm_file)
|
||||
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
|
||||
if len(symbols) > 0:
|
||||
# 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 and len(symbols) > 0:
|
||||
relative_inc_path = inc_path.relative_to('asm/')
|
||||
lines.insert(0, f' .include "{relative_inc_path}"')
|
||||
with open(file, 'w', encoding='Shift-JIS') as f:
|
||||
for line in lines:
|
||||
f.write(line)
|
||||
f.write('\n')
|
||||
inc_files_created += 1
|
||||
|
||||
# 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', encoding='Shift-JIS') 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)
|
||||
print(path)
|
||||
if len(args.files) > 1:
|
||||
print(path)
|
||||
generate_externs(path)
|
||||
files_updated += 1
|
||||
|
||||
print(f"Updated {files_updated} file(s)")
|
||||
print(f"Created {inc_files_created} .inc file(s)")
|
||||
if len(args.files) > 1:
|
||||
print(f"Updated {files_updated} file(s)")
|
||||
print(f"Created {inc_files_created} .inc file(s)")
|
||||
|
||||
Reference in New Issue
Block a user