summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorAetias <aetias@outlook.com>2024-04-06 09:26:50 +0200
committerAetias <aetias@outlook.com>2024-04-06 09:26:50 +0200
commitc0cc540ee855184e49cbc6ead23f65bf22f3185c (patch)
tree3119eacd3612ecfc9f709290c758f445319fc08f /tools
parentcab451da34cee81d0cd8cd4c9827753890fa858f (diff)
Add `make gen_externs`
Diffstat (limited to 'tools')
-rw-r--r--tools/gen_externs.py143
1 files changed, 74 insertions, 69 deletions
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()
-
- # 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 []
+ 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)
+ 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
-
- # 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:
+ 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
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)")