Files
st/tools/delink.py
T
Yanis 05612891ad Run dsd delink automatically on symbols/delinks/relocs update (#145)
* automatically run delink when config is changed

* fix issue with -lang flag

* add back the intended defaults

* raise error on failed run

* remove ninja objdiff running ninja delink

* delink all modules I guess (fixes an issue)
2026-08-24 17:56:17 +02:00

110 lines
3.4 KiB
Python
Executable File

#!/usr/bin/env python3
# this script attempts to run `dsd delink` for modules that were updated
# `python delink.py dsd_path [optional version]`
import glob
import json
import subprocess
import sys
from pathlib import Path
CONFIG_DIR = Path("config").resolve()
DSD_PATH = Path("./dsd").resolve() # only used when debugging
TIMESTAMP_FILE = Path("build").resolve() / "delink_timestamps.json"
TIMESTAMP_FILE.parent.mkdir(exist_ok=True)
DEBUG = False
def version_exists(version: str) -> bool:
return glob.glob(str(Path("extract") / f"baserom_st_{version}.nds")) != []
def main():
# get supported versions
GAME_VERSIONS = [
"eur",
"eur1",
"usa",
"jp",
]
versions = [version for version in GAME_VERSIONS if version_exists(version)]
# or if we want a specific one
if len(sys.argv) > 2:
versions = [sys.argv[2]]
# should we write TIMESTAMP_FILE
do_write = False
# initialize dicts
file_paths_map: dict[str, list[Path]] = {}
current_timestamp_map: dict[str, dict[str, float]] = {}
for version in versions:
file_paths_map[version] = []
current_timestamp_map[version] = {}
# fetch config paths
file_kinds = ["symbols", "relocs", "delinks"]
for version in versions:
for file_path in (CONFIG_DIR / version).rglob("**/*.txt"):
for file_kind in file_kinds:
if file_path.stem != file_kind:
continue
file_paths_map[version].append(file_path)
# fetch current timestamps
for version, file_paths in file_paths_map.items():
for path in file_paths:
current_timestamp_map[version][str(path.relative_to(Path.cwd()))] = path.stat().st_mtime
if TIMESTAMP_FILE.exists():
# if the cache exists
# load the cache
saved_timestamp_map: dict[str, dict[str, float]] = json.loads(TIMESTAMP_FILE.read_text())
for version, saved_map in saved_timestamp_map.items():
do_delink = False
for saved_path_str, saved_timestamp in saved_map.items():
timestamp = current_timestamp_map[version].get(saved_path_str)
# if current timestamp not found or current timestamp is different than the saved timestamp
# we make it delink when not found because it might mean we added a new version
# and haven't cached it yet
if timestamp is None or timestamp != saved_timestamp:
do_delink = True
break
if do_delink:
dsd_p = DSD_PATH if DEBUG else sys.argv[1]
# ideally we'd only delink the necessary modules
# however because of how dsd works we can't do that otherwise modules with
# external calls won't be delinked again
command = [str(dsd_p), "delink", "--config-path", str(CONFIG_DIR / version / "arm9" / "config.yaml")]
subprocess.run(command, check=True)
if DEBUG:
print(f"[DEBUG]: execution completed for '{' '.join(command)}'")
do_write = True
else:
# if the cache doesn't exist simply create it
do_write = True
if do_write:
with TIMESTAMP_FILE.open("w") as file:
json.dump(current_timestamp_map, file, indent=4)
if __name__ == "__main__":
main()