Update dtk-template, dtk, and objdiff

This commit is contained in:
LagoLunatic
2025-01-28 14:01:04 -05:00
parent 901bc51f40
commit d6c3081559
7 changed files with 206 additions and 95 deletions
+108 -37
View File
@@ -17,7 +17,20 @@ import os
import platform
import sys
from pathlib import Path
from typing import IO, Any, Dict, Iterable, List, Optional, Set, Tuple, Union, cast
from typing import (
Any,
Callable,
cast,
Dict,
IO,
Iterable,
List,
Optional,
Set,
Tuple,
TypedDict,
Union,
)
from . import ninja_syntax
from .ninja_syntax import serialize_path
@@ -176,7 +189,12 @@ class ProjectConfig:
True # Generate compile_commands.json for clangd
)
self.extra_clang_flags: List[str] = [] # Extra flags for clangd
self.scratch_preset_id: Optional[int] = None # Default decomp.me preset ID for scratches
self.scratch_preset_id: Optional[int] = (
None # Default decomp.me preset ID for scratches
)
self.link_order_callback: Optional[Callable[[int, List[str]], List[str]]] = (
None # Callback to add/remove/reorder units within a module
)
# Progress output, progress.json and report.json config
self.progress = True # Enable report.json generation and CLI progress output
@@ -292,10 +310,38 @@ def make_flags_str(flags: Optional[List[str]]) -> str:
return " ".join(flags)
# Unit configuration
class BuildConfigUnit(TypedDict):
object: Optional[str]
name: str
autogenerated: bool
# Module configuration
class BuildConfigModule(TypedDict):
name: str
module_id: int
ldscript: str
entry: str
units: List[BuildConfigUnit]
# Module link configuration
class BuildConfigLink(TypedDict):
modules: List[str]
# Build configuration generated by decomp-toolkit
class BuildConfig(BuildConfigModule):
version: str
modules: List[BuildConfigModule]
links: List[BuildConfigLink]
# Load decomp-toolkit generated config.json
def load_build_config(
config: ProjectConfig, build_config_path: Path
) -> Optional[Dict[str, Any]]:
) -> Optional[BuildConfig]:
if not build_config_path.is_file():
return None
@@ -303,7 +349,7 @@ def load_build_config(
return tuple(map(int, (v.split("."))))
f = open(build_config_path, "r", encoding="utf-8")
build_config: Dict[str, Any] = json.load(f)
build_config: BuildConfig = json.load(f)
config_version = build_config.get("version")
if config_version is None:
print("Invalid config.json, regenerating...")
@@ -319,6 +365,24 @@ def load_build_config(
return None
f.close()
# Apply link order callback
if config.link_order_callback:
modules: List[BuildConfigModule] = [build_config, *build_config["modules"]]
for module in modules:
unit_names = list(map(lambda u: u["name"], module["units"]))
unit_names = config.link_order_callback(module["module_id"], unit_names)
units: List[BuildConfigUnit] = []
for unit_name in unit_names:
units.append(
# Find existing unit or create a new one
next(
(u for u in module["units"] if u["name"] == unit_name),
{"object": None, "name": unit_name, "autogenerated": False},
)
)
module["units"] = units
return build_config
@@ -336,7 +400,7 @@ def generate_build(config: ProjectConfig) -> None:
def generate_build_ninja(
config: ProjectConfig,
objects: Dict[str, Object],
build_config: Optional[Dict[str, Any]],
build_config: Optional[BuildConfig],
) -> None:
out = io.StringIO()
n = ninja_syntax.Writer(out)
@@ -380,7 +444,7 @@ def generate_build_ninja(
decompctx = config.tools_dir / "decompctx.py"
n.rule(
name="decompctx",
command=f"$python {decompctx} $in -o $out -d $out.d",
command=f"$python {decompctx} $in -o $out -d $out.d $includes",
description="CTX $in",
depfile="$out.d",
deps="gcc",
@@ -697,9 +761,9 @@ def generate_build_ninja(
return path.parent / (path.name + ".MAP")
class LinkStep:
def __init__(self, config: Dict[str, Any]) -> None:
self.name: str = config["name"]
self.module_id: int = config["module_id"]
def __init__(self, config: BuildConfigModule) -> None:
self.name = config["name"]
self.module_id = config["module_id"]
self.ldscript: Optional[Path] = Path(config["ldscript"])
self.entry = config["entry"]
self.inputs: List[str] = []
@@ -809,10 +873,8 @@ def generate_build_ninja(
else:
extra_cflags.insert(0, "-lang=c")
cflags_str = make_flags_str(cflags)
if len(extra_cflags) > 0:
extra_cflags_str = make_flags_str(extra_cflags)
cflags_str += " " + extra_cflags_str
all_cflags = cflags + extra_cflags
cflags_str = make_flags_str(all_cflags)
used_compiler_versions.add(obj.options["mw_version"])
# Add MWCC build rule
@@ -836,11 +898,21 @@ def generate_build_ninja(
# Add ctx build rule
if obj.ctx_path is not None:
include_dirs = []
for flag in all_cflags:
if (
flag.startswith("-i ")
or flag.startswith("-I ")
or flag.startswith("-I+")
):
include_dirs.append(flag[3:])
includes = " ".join([f"-I {d}" for d in include_dirs])
n.build(
outputs=obj.ctx_path,
rule="decompctx",
inputs=src_path,
implicit=decompctx,
variables={"includes": includes},
)
# Add host build rule
@@ -897,13 +969,14 @@ def generate_build_ninja(
return obj_path
def add_unit(build_obj, link_step: LinkStep):
def add_unit(build_obj: BuildConfigUnit, link_step: LinkStep):
obj_path, obj_name = build_obj["object"], build_obj["name"]
obj = objects.get(obj_name)
if obj is None:
if config.warn_missing_config and not build_obj["autogenerated"]:
print(f"Missing configuration for {obj_name}")
link_step.add(obj_path)
if obj_path is not None:
link_step.add(Path(obj_path))
return
link_built_obj = obj.completed
@@ -932,12 +1005,7 @@ def generate_build_ninja(
link_step.add(built_obj_path)
elif obj_path is not None:
# Use the original (extracted) object
link_step.add(obj_path)
else:
lib_name = obj.options["lib"]
sys.exit(
f"Missing object for {obj_name}: {obj.src_path} {lib_name} {obj}"
)
link_step.add(Path(obj_path))
# Add DOL link step
link_step = LinkStep(build_config)
@@ -1258,7 +1326,7 @@ def generate_build_ninja(
def generate_objdiff_config(
config: ProjectConfig,
objects: Dict[str, Object],
build_config: Optional[Dict[str, Any]],
build_config: Optional[BuildConfig],
) -> None:
if build_config is None:
return
@@ -1323,7 +1391,7 @@ def generate_objdiff_config(
}
def add_unit(
build_obj: Dict[str, Any], module_name: str, progress_categories: List[str]
build_obj: BuildConfigUnit, module_name: str, progress_categories: List[str]
) -> None:
obj_path, obj_name = build_obj["object"], build_obj["name"]
base_object = Path(obj_name).with_suffix("")
@@ -1358,9 +1426,21 @@ def generate_objdiff_config(
unit_config["base_path"] = obj.src_obj_path
unit_config["metadata"]["source_path"] = obj.src_path
cflags = obj.options["cflags"]
# Filter out include directories
def keep_flag(flag):
return (
not flag.startswith("-i ")
and not flag.startswith("-i-")
and not flag.startswith("-I ")
and not flag.startswith("-I+")
and not flag.startswith("-I-")
)
all_cflags = list(
filter(keep_flag, obj.options["cflags"] + obj.options["extra_cflags"])
)
reverse_fn_order = False
for flag in cflags:
for flag in all_cflags:
if not flag.startswith("-inline "):
continue
for value in flag.split(" ")[1].split(","):
@@ -1369,20 +1449,11 @@ def generate_objdiff_config(
elif value == "nodeferred":
reverse_fn_order = False
# Filter out include directories
def keep_flag(flag):
return not flag.startswith("-i ") and not flag.startswith("-I ")
cflags = list(filter(keep_flag, cflags))
compiler_version = COMPILER_MAP.get(obj.options["mw_version"])
if compiler_version is None:
print(f"Missing scratch compiler mapping for {obj.options['mw_version']}")
else:
cflags_str = make_flags_str(cflags)
if len(obj.options["extra_cflags"]) > 0:
extra_cflags_str = make_flags_str(obj.options["extra_cflags"])
cflags_str += " " + extra_cflags_str
cflags_str = make_flags_str(all_cflags)
unit_config["scratch"] = {
"platform": "gc_wii",
"compiler": compiler_version,
@@ -1468,7 +1539,7 @@ def generate_objdiff_config(
def generate_compile_commands(
config: ProjectConfig,
objects: Dict[str, Object],
build_config: Optional[Dict[str, Any]],
build_config: Optional[BuildConfig],
) -> None:
if build_config is None or not config.generate_compile_commands:
return
@@ -1557,7 +1628,7 @@ def generate_compile_commands(
clangd_config = []
def add_unit(build_obj: Dict[str, Any]) -> None:
def add_unit(build_obj: BuildConfigUnit) -> None:
obj = objects.get(build_obj["name"])
if obj is None:
return