Working progress categories & type fixes

This commit is contained in:
Luke Street 2024-09-03 20:54:07 -06:00
parent 50fdffe5be
commit 0427fd1e1d
2 changed files with 108 additions and 75 deletions

View File

@ -16,14 +16,7 @@ import argparse
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List from typing import Any, Dict, List
from tools.project import *
from tools.project import (
Object,
ProjectConfig,
calculate_progress,
generate_build,
is_windows,
)
# Game versions # Game versions
DEFAULT_VERSION = 0 DEFAULT_VERSION = 0
@ -138,7 +131,7 @@ if not config.non_matching:
config.binutils_tag = "2.42-1" config.binutils_tag = "2.42-1"
config.compilers_tag = "20240706" config.compilers_tag = "20240706"
config.dtk_tag = "v0.9.4" config.dtk_tag = "v0.9.4"
config.objdiff_tag = "v2.0.0-beta.3" config.objdiff_tag = "v2.0.0-beta.5"
config.sjiswrap_tag = "v1.1.1" config.sjiswrap_tag = "v1.1.1"
config.wibo_tag = "0.6.11" config.wibo_tag = "0.6.11"
@ -218,7 +211,7 @@ def DolphinLib(lib_name: str, objects: List[Object]) -> Dict[str, Any]:
"lib": lib_name, "lib": lib_name,
"mw_version": "GC/1.2.5n", "mw_version": "GC/1.2.5n",
"cflags": cflags_base, "cflags": cflags_base,
"host": False, "progress_category": "sdk",
"objects": objects, "objects": objects,
} }
@ -229,7 +222,7 @@ def Rel(lib_name: str, objects: List[Object]) -> Dict[str, Any]:
"lib": lib_name, "lib": lib_name,
"mw_version": "GC/1.3.2", "mw_version": "GC/1.3.2",
"cflags": cflags_rel, "cflags": cflags_rel,
"host": True, "progress_category": "game",
"objects": objects, "objects": objects,
} }
@ -245,7 +238,7 @@ config.libs = [
"lib": "Runtime.PPCEABI.H", "lib": "Runtime.PPCEABI.H",
"mw_version": config.linker_version, "mw_version": config.linker_version,
"cflags": cflags_runtime, "cflags": cflags_runtime,
"host": False, "progress_category": "sdk", # str | List[str]
"objects": [ "objects": [
Object(NonMatching, "Runtime.PPCEABI.H/global_destructor_chain.c"), Object(NonMatching, "Runtime.PPCEABI.H/global_destructor_chain.c"),
Object(NonMatching, "Runtime.PPCEABI.H/__init_cpp_exceptions.cpp"), Object(NonMatching, "Runtime.PPCEABI.H/__init_cpp_exceptions.cpp"),
@ -253,12 +246,19 @@ config.libs = [
}, },
] ]
# Optional extra categories for progress tracking
# Adjust as desired for your project
config.progress_categories = [
ProgressCategory("game", "Game Code"),
ProgressCategory("sdk", "SDK Code"),
]
config.progress_each_module = args.verbose
if args.mode == "configure": if args.mode == "configure":
# Write build.ninja and objdiff.json # Write build.ninja and objdiff.json
generate_build(config) generate_build(config)
elif args.mode == "progress": elif args.mode == "progress":
# Print progress and write progress.json # Print progress and write progress.json
config.progress_each_module = args.verbose
calculate_progress(config) calculate_progress(config)
else: else:
sys.exit("Unknown mode: " + args.mode) sys.exit("Unknown mode: " + args.mode)

View File

@ -17,7 +17,7 @@ import os
import platform import platform
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Set, Tuple, Union from typing import Any, Dict, List, Optional, Set, Tuple, Union, cast
from . import ninja_syntax from . import ninja_syntax
from .ninja_syntax import serialize_path from .ninja_syntax import serialize_path
@ -72,6 +72,7 @@ class Object:
def set_default(key: str, value: Any) -> None: def set_default(key: str, value: Any) -> None:
if obj.options[key] is None: if obj.options[key] is None:
obj.options[key] = value obj.options[key] = value
set_default("add_to_all", True) set_default("add_to_all", True)
set_default("asflags", config.asflags) set_default("asflags", config.asflags)
set_default("asm_dir", config.asm_dir) set_default("asm_dir", config.asm_dir)
@ -156,11 +157,11 @@ class ProjectConfig:
None # Custom build steps, types are ["pre-compile", "post-compile", "post-link", "post-build"] None # Custom build steps, types are ["pre-compile", "post-compile", "post-link", "post-build"]
) )
# Progress output and progress.json config # Progress output, progress.json and report.json config
self.progress_all: bool = True # Include combined "all" category self.progress_all: bool = True # Include combined "all" category
self.progress_modules: bool = True # Include combined "modules" category self.progress_modules: bool = True # Include combined "modules" category
self.progress_each_module: bool = ( self.progress_each_module: bool = (
True # Include individual modules, disable for large numbers of modules False # Include individual modules, disable for large numbers of modules
) )
self.progress_categories: List[ProgressCategory] = [] # Additional categories self.progress_categories: List[ProgressCategory] = [] # Additional categories
@ -567,8 +568,8 @@ def generate_build_ninja(
n.comment("Custom project build rules (pre/post-processing)") n.comment("Custom project build rules (pre/post-processing)")
for rule in config.custom_build_rules or {}: for rule in config.custom_build_rules or {}:
n.rule( n.rule(
name=rule.get("name"), name=cast(str, rule.get("name")),
command=rule.get("command"), command=cast(str, rule.get("command")),
description=rule.get("description", None), description=rule.get("description", None),
depfile=rule.get("depfile", None), depfile=rule.get("depfile", None),
generator=rule.get("generator", False), generator=rule.get("generator", False),
@ -580,12 +581,12 @@ def generate_build_ninja(
) )
n.newline() n.newline()
def write_custom_step(step: str) -> List[str]: def write_custom_step(step: str) -> List[str | Path]:
implicit = [] implicit: List[str | Path] = []
if config.custom_build_steps and step in config.custom_build_steps: if config.custom_build_steps and step in config.custom_build_steps:
n.comment(f"Custom build steps ({step})") n.comment(f"Custom build steps ({step})")
for custom_step in config.custom_build_steps[step]: for custom_step in config.custom_build_steps[step]:
outputs = custom_step.get("outputs") outputs = cast(List[str | Path], custom_step.get("outputs"))
if isinstance(outputs, list): if isinstance(outputs, list):
implicit.extend(outputs) implicit.extend(outputs)
@ -594,7 +595,7 @@ def generate_build_ninja(
n.build( n.build(
outputs=outputs, outputs=outputs,
rule=custom_step.get("rule"), rule=cast(str, custom_step.get("rule")),
inputs=custom_step.get("inputs", None), inputs=custom_step.get("inputs", None),
implicit=custom_step.get("implicit", None), implicit=custom_step.get("implicit", None),
order_only=custom_step.get("order_only", None), order_only=custom_step.get("order_only", None),
@ -734,7 +735,7 @@ def generate_build_ninja(
used_compiler_versions.add(obj.options["mw_version"]) used_compiler_versions.add(obj.options["mw_version"])
# Avoid creating duplicate build rules # Avoid creating duplicate build rules
if obj.src_obj_path in source_added: if obj.src_obj_path is None or obj.src_obj_path in source_added:
return obj.src_obj_path return obj.src_obj_path
source_added.add(obj.src_obj_path) source_added.add(obj.src_obj_path)
@ -757,15 +758,16 @@ def generate_build_ninja(
) )
# Add ctx build rule # Add ctx build rule
n.build( if obj.ctx_path is not None:
outputs=obj.ctx_path, n.build(
rule="decompctx", outputs=obj.ctx_path,
inputs=src_path, rule="decompctx",
implicit=decompctx, inputs=src_path,
) implicit=decompctx,
)
# Add host build rule # Add host build rule
if obj.options["host"]: if obj.options["host"] and obj.host_obj_path is not None:
n.build( n.build(
outputs=obj.host_obj_path, outputs=obj.host_obj_path,
rule="host_cc" if src_path.suffix == ".c" else "host_cpp", rule="host_cc" if src_path.suffix == ".c" else "host_cpp",
@ -784,7 +786,9 @@ def generate_build_ninja(
return obj.src_obj_path return obj.src_obj_path
def asm_build(obj: Object, src_path: Path, obj_path: Path) -> Optional[Path]: def asm_build(
obj: Object, src_path: Path, obj_path: Optional[Path]
) -> Optional[Path]:
if obj.options["asflags"] is None: if obj.options["asflags"] is None:
sys.exit("ProjectConfig.asflags missing") sys.exit("ProjectConfig.asflags missing")
asflags_str = make_flags_str(obj.options["asflags"]) asflags_str = make_flags_str(obj.options["asflags"])
@ -793,7 +797,7 @@ def generate_build_ninja(
asflags_str += " " + extra_asflags_str asflags_str += " " + extra_asflags_str
# Avoid creating duplicate build rules # Avoid creating duplicate build rules
if obj_path in source_added: if obj_path is None or obj_path in source_added:
return obj_path return obj_path
source_added.add(obj_path) source_added.add(obj_path)
@ -825,7 +829,7 @@ def generate_build_ninja(
link_built_obj = obj.completed link_built_obj = obj.completed
built_obj_path: Optional[Path] = None built_obj_path: Optional[Path] = None
if obj.src_path.exists(): if obj.src_path is not None and obj.src_path.exists():
if obj.src_path.suffix in (".c", ".cp", ".cpp"): if obj.src_path.suffix in (".c", ".cp", ".cpp"):
# Add MWCC & host build rules # Add MWCC & host build rules
built_obj_path = c_build(obj, obj.src_path) built_obj_path = c_build(obj, obj.src_path)
@ -932,7 +936,7 @@ def generate_build_ninja(
rspfile="$rspfile", rspfile="$rspfile",
rspfile_content="$in_newline", rspfile_content="$in_newline",
) )
generated_rels = [] generated_rels: List[str] = []
for idx, link in enumerate(build_config["links"]): for idx, link in enumerate(build_config["links"]):
# Map module names to link steps # Map module names to link steps
link_steps_local = list( link_steps_local = list(
@ -1048,10 +1052,11 @@ def generate_build_ninja(
command=f"{objdiff} report generate -o $out", command=f"{objdiff} report generate -o $out",
description="REPORT", description="REPORT",
) )
report_implicit: List[str | Path] = [objdiff, "all_source"]
n.build( n.build(
outputs=report_path, outputs=report_path,
rule="report", rule="report",
implicit=[objdiff, "all_source"], implicit=report_implicit,
) )
### ###
@ -1169,7 +1174,7 @@ def generate_objdiff_config(
return return
objdiff_config: Dict[str, Any] = { objdiff_config: Dict[str, Any] = {
"min_version": "1.0.0", "min_version": "2.0.0-beta.5",
"custom_make": "ninja", "custom_make": "ninja",
"build_target": False, "build_target": False,
"watch_patterns": [ "watch_patterns": [
@ -1185,6 +1190,7 @@ def generate_objdiff_config(
"*.json", "*.json",
], ],
"units": [], "units": [],
"progress_categories": [],
} }
# decomp.me compiler name mapping # decomp.me compiler name mapping
@ -1220,22 +1226,21 @@ def generate_objdiff_config(
"Wii/1.7": "mwcc_43_213", "Wii/1.7": "mwcc_43_213",
} }
build_path = config.out_path() def add_unit(
build_obj: Dict[str, Any], module_name: str, progress_categories: List[str]
def add_unit(build_obj: Dict[str, Any], module_name: str) -> None: ) -> None:
if build_obj["autogenerated"]:
# Skip autogenerated objects
return
obj_path, obj_name = build_obj["object"], build_obj["name"] obj_path, obj_name = build_obj["object"], build_obj["name"]
base_object = Path(obj_name).with_suffix("") base_object = Path(obj_name).with_suffix("")
unit_config: Dict[str, Any] = { unit_config: Dict[str, Any] = {
"name": Path(module_name) / base_object, "name": Path(module_name) / base_object,
"target_path": obj_path, "target_path": obj_path,
"metadata": {
"auto_generated": build_obj["autogenerated"],
},
} }
obj = objects.get(obj_name) obj = objects.get(obj_name)
if obj is None or not obj.src_path.exists(): if obj is None or not obj.src_path or not obj.src_path.exists():
objdiff_config["units"].append(unit_config) objdiff_config["units"].append(unit_config)
return return
@ -1264,8 +1269,6 @@ def generate_objdiff_config(
cflags.insert(0, "-lang=c") cflags.insert(0, "-lang=c")
unit_config["base_path"] = obj.src_obj_path unit_config["base_path"] = obj.src_obj_path
unit_config["reverse_fn_order"] = reverse_fn_order
unit_config["complete"] = obj.completed
compiler_version = COMPILER_MAP.get(obj.options["mw_version"]) compiler_version = COMPILER_MAP.get(obj.options["mw_version"])
if compiler_version is None: if compiler_version is None:
print(f"Missing scratch compiler mapping for {obj.options['mw_version']}") print(f"Missing scratch compiler mapping for {obj.options['mw_version']}")
@ -1281,25 +1284,56 @@ def generate_objdiff_config(
"ctx_path": obj.ctx_path, "ctx_path": obj.ctx_path,
"build_ctx": True, "build_ctx": True,
} }
metadata = { category_opt: List[str] | str = obj.options["progress_category"]
if isinstance(category_opt, list):
progress_categories.extend(category_opt)
elif category_opt is not None:
progress_categories.append(category_opt)
unit_config["metadata"].update({
"complete": obj.completed,
"reverse_fn_order": reverse_fn_order,
"source_path": obj.src_path, "source_path": obj.src_path,
} "progress_categories": progress_categories,
if obj.options["progress_category"] is not None: })
if isinstance(obj.options["progress_category"], list):
metadata["progress_categories"] = obj.options["progress_category"]
else:
metadata["progress_categories"] = [obj.options["progress_category"]]
unit_config["metadata"] = metadata
objdiff_config["units"].append(unit_config) objdiff_config["units"].append(unit_config)
# Add DOL units # Add DOL units
for unit in build_config["units"]: for unit in build_config["units"]:
add_unit(unit, build_config["name"]) progress_categories = []
# Only include a "dol" category if there are any modules
# Otherwise it's redundant with the global report measures
if len(build_config["modules"]) > 0:
progress_categories.append("dol")
add_unit(unit, build_config["name"], progress_categories)
# Add REL units # Add REL units
for module in build_config["modules"]: for module in build_config["modules"]:
for unit in module["units"]: for unit in module["units"]:
add_unit(unit, module["name"]) progress_categories = []
if config.progress_modules:
progress_categories.append("modules")
if config.progress_each_module:
progress_categories.append(module["name"])
add_unit(unit, module["name"], progress_categories)
# Add progress categories
def add_category(id: str, name: str):
objdiff_config["progress_categories"].append(
{
"id": id,
"name": name,
}
)
if len(build_config["modules"]) > 0:
add_category("dol", "DOL")
if config.progress_modules:
add_category("modules", "Modules")
if config.progress_each_module:
for module in build_config["modules"]:
add_category(module["name"], module["name"])
for category in config.progress_categories:
add_category(category.id, category.name)
# Write objdiff.json # Write objdiff.json
with open("objdiff.json", "w", encoding="utf-8") as w: with open("objdiff.json", "w", encoding="utf-8") as w:
@ -1364,16 +1398,15 @@ def calculate_progress(config: ProjectConfig) -> None:
return self.data_progress / self.data_total return self.data_progress / self.data_total
progress_units: Dict[str, ProgressUnit] = {} progress_units: Dict[str, ProgressUnit] = {}
if config.progress_all:
progress_units["all"] = ProgressUnit("All")
progress_units["dol"] = ProgressUnit("DOL")
if len(build_config["modules"]) > 0:
if config.progress_modules:
progress_units["modules"] = ProgressUnit("Modules")
if len(config.progress_categories) > 0: if len(config.progress_categories) > 0:
for category in config.progress_categories: for category in config.progress_categories:
progress_units[category.id] = ProgressUnit(category.name) progress_units[category.id] = ProgressUnit(category.name)
else:
if config.progress_all:
progress_units["all"] = ProgressUnit("All")
progress_units["dol"] = ProgressUnit("DOL")
if len(build_config["modules"]) > 0:
if config.progress_modules:
progress_units["modules"] = ProgressUnit("Modules")
if config.progress_each_module: if config.progress_each_module:
for module in build_config["modules"]: for module in build_config["modules"]:
progress_units[module["name"]] = ProgressUnit(module["name"]) progress_units[module["name"]] = ProgressUnit(module["name"])
@ -1389,12 +1422,12 @@ def calculate_progress(config: ProjectConfig) -> None:
add_unit("dol", unit) add_unit("dol", unit)
obj = objects.get(unit["name"]) obj = objects.get(unit["name"])
if obj is not None: if obj is not None:
category = obj.options["progress_category"] category_opt = obj.options["progress_category"]
if isinstance(category, list): if isinstance(category_opt, list):
for category in category: for id in category_opt:
add_unit(category, unit) add_unit(id, unit)
elif category is not None: elif category_opt is not None:
add_unit(category, unit) add_unit(category_opt, unit)
# Add REL units # Add REL units
for module in build_config["modules"]: for module in build_config["modules"]:
@ -1404,12 +1437,12 @@ def calculate_progress(config: ProjectConfig) -> None:
add_unit(module["name"], unit) add_unit(module["name"], unit)
obj = objects.get(unit["name"]) obj = objects.get(unit["name"])
if obj is not None: if obj is not None:
category = obj.options["progress_category"] category_opt = obj.options["progress_category"]
if isinstance(category, list): if isinstance(category_opt, list):
for category in category: for id in category_opt:
add_unit(category, unit) add_unit(id, unit)
elif category is not None: elif category_opt is not None:
add_unit(category, unit) add_unit(category_opt, unit)
# Print human-readable progress # Print human-readable progress
print("Progress:") print("Progress:")