mirror of
https://github.com/zeldaret/st
synced 2026-08-25 07:44:42 -04:00
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)
This commit is contained in:
Executable
+109
@@ -0,0 +1,109 @@
|
||||
#!/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()
|
||||
+12
-14
@@ -154,6 +154,7 @@ class ProjectConfig:
|
||||
self.cc_path = (self.mwcc_path / "mwccarm.exe").resolve()
|
||||
self.as_path = (self.mwcc_path / "mwasmarm.exe").resolve()
|
||||
self.ld_path = (self.mwcc_path / "mwldarm.exe").resolve()
|
||||
self.delinkpy_path = (self.root_path / "tools" / "delink.py").resolve()
|
||||
self.python_path = Path(sys.executable)
|
||||
|
||||
self.dsd_base_flags = [
|
||||
@@ -246,11 +247,11 @@ class ProjectConfig:
|
||||
return self.mwcc_tag
|
||||
|
||||
@property
|
||||
def current_path(self):
|
||||
def current_path(self) -> Path:
|
||||
return Path(__name__)
|
||||
|
||||
@property
|
||||
def root_path(self):
|
||||
def root_path(self) -> Path:
|
||||
return self.current_path.parent
|
||||
|
||||
@property
|
||||
@@ -290,7 +291,7 @@ class ProjectConfig:
|
||||
return self.root_path / "tools"
|
||||
|
||||
@property
|
||||
def mwcc_path(self):
|
||||
def mwcc_path(self) -> Path:
|
||||
return self.mwcc_root / self.mwcc_version
|
||||
|
||||
@property
|
||||
@@ -422,7 +423,7 @@ def add_download_tool_builds(cfg: ProjectConfig, n: ninja_syntax.Writer, args: A
|
||||
variables={
|
||||
"tool": "objdiff",
|
||||
"tag": cfg.objdiff_version,
|
||||
"path": cfg.objdiff_path,
|
||||
"path": str(cfg.objdiff_path),
|
||||
}
|
||||
)
|
||||
n.newline()
|
||||
@@ -609,11 +610,10 @@ def add_mwcc_build(cfg: ProjectConfig, version: str, n: ninja_syntax.Writer, sou
|
||||
src_obj_path = cfg.get_game_build(version) / source_file
|
||||
|
||||
cc_flags: list[str] = object.options["cflags"] or [] + object.options["extra_cflags"] or []
|
||||
if "-lang=c++" not in cc_flags or "-lang=c" not in cc_flags:
|
||||
if is_cpp(source_file):
|
||||
cc_flags.append("-lang=c++")
|
||||
elif is_c(source_file):
|
||||
cc_flags.append("-lang=c")
|
||||
if "-lang=c++" not in cc_flags and is_cpp(source_file):
|
||||
cc_flags.append("-lang=c++")
|
||||
elif "-lang=c" not in cc_flags and is_c(source_file):
|
||||
cc_flags.append("-lang=c")
|
||||
|
||||
n.build(
|
||||
inputs=str(source_file),
|
||||
@@ -1152,7 +1152,7 @@ def process_project(cfg: ProjectConfig, args: Any):
|
||||
])
|
||||
n.rule(
|
||||
name="objdiff",
|
||||
command=f"touch {cfg.dsd_path} && {cfg.dsd_path} {cfg.dsd_flags} objdiff --config-path $config_path --output-path $out_path {dsd_objdiff_args}"
|
||||
command=f"{cfg.dsd_path} {cfg.dsd_flags} objdiff --config-path $config_path --output-path $out_path {dsd_objdiff_args}"
|
||||
)
|
||||
n.newline()
|
||||
|
||||
@@ -1195,7 +1195,7 @@ def process_project(cfg: ProjectConfig, args: Any):
|
||||
configure_cmdline = subprocess.list2cmdline(sys.argv[1:])
|
||||
n.rule(
|
||||
name="configure",
|
||||
command=f"{cfg.python_path} tools/configure.py {configure_cmdline}",
|
||||
command=f"{cfg.python_path} tools/configure.py {configure_cmdline} && {cfg.python_path} {cfg.delinkpy_path} {cfg.dsd_path}",
|
||||
generator=True
|
||||
)
|
||||
n.newline()
|
||||
@@ -1275,7 +1275,6 @@ def process_project(cfg: ProjectConfig, args: Any):
|
||||
rule="post_objdiff",
|
||||
implicit=[f"objdiff_{version}.json" for version in cfg.game_versions],
|
||||
outputs="objdiff",
|
||||
order_only=cmds_map["delink"],
|
||||
)
|
||||
n.newline()
|
||||
|
||||
@@ -1298,7 +1297,6 @@ def process_project(cfg: ProjectConfig, args: Any):
|
||||
implicit=version_to_cmds[version],
|
||||
)
|
||||
|
||||
# n.default(["format", "objdiff", *defaults])
|
||||
n.default(["objdiff", *defaults])
|
||||
n.default(["format", "objdiff", *defaults])
|
||||
else:
|
||||
n.default(["download_tools"])
|
||||
|
||||
Reference in New Issue
Block a user