diff --git a/.gitignore b/.gitignore index bcb556b3..b63315e9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,3 @@ ph_*/ *bios.bin /m2ctx *.sav -_gen_externs_tmp* diff --git a/Makefile b/Makefile index 8dedf701..85bd8cb1 100644 --- a/Makefile +++ b/Makefile @@ -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 $< diff --git a/tools/gen_externs.py b/tools/gen_externs.py index 192e7eee..2845502e 100644 --- a/tools/gen_externs.py +++ b/tools/gen_externs.py @@ -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)")