WIP custom progress categories

This commit is contained in:
Luke Street 2024-08-27 23:04:38 -07:00
parent 4b1995b381
commit 50fdffe5be
1 changed files with 208 additions and 180 deletions

View File

@ -29,24 +29,77 @@ if sys.platform == "cygwin":
f"\n(Current path: {sys.executable})" f"\n(Current path: {sys.executable})"
) )
Library = Dict[str, Any]
class Object: class Object:
def __init__(self, completed: bool, name: str, **options: Any) -> None: def __init__(self, completed: bool, name: str, **options: Any) -> None:
self.name = name self.name = name
self.base_name = Path(name).with_suffix("")
self.completed = completed self.completed = completed
self.options: Dict[str, Any] = { self.options: Dict[str, Any] = {
"add_to_all": True, "add_to_all": None,
"asflags": None, "asflags": None,
"extra_asflags": None, "asm_dir": None,
"cflags": None, "cflags": None,
"extra_asflags": None,
"extra_cflags": None, "extra_cflags": None,
"host": None,
"lib": None,
"mw_version": None, "mw_version": None,
"progress_category": None,
"shift_jis": None, "shift_jis": None,
"source": name, "source": name,
"src_dir": None,
} }
self.options.update(options) self.options.update(options)
# Internal
self.src_path: Optional[Path] = None
self.asm_path: Optional[Path] = None
self.src_obj_path: Optional[Path] = None
self.asm_obj_path: Optional[Path] = None
self.host_obj_path: Optional[Path] = None
self.ctx_path: Optional[Path] = None
def resolve(self, config: "ProjectConfig", lib: Library) -> "Object":
# Use object options, then library options
obj = Object(self.completed, self.name, **lib)
for key, value in self.options.items():
if value is not None or key not in obj.options:
obj.options[key] = value
# Use default options from config
def set_default(key: str, value: Any) -> None:
if obj.options[key] is None:
obj.options[key] = value
set_default("add_to_all", True)
set_default("asflags", config.asflags)
set_default("asm_dir", config.asm_dir)
set_default("host", False)
set_default("mw_version", config.linker_version)
set_default("shift_jis", config.shift_jis)
set_default("src_dir", config.src_dir)
# Resolve paths
build_dir = config.out_path()
obj.src_path = Path(obj.options["src_dir"]) / obj.options["source"]
if obj.options["asm_dir"] is not None:
obj.asm_path = (
Path(obj.options["asm_dir"]) / obj.options["source"]
).with_suffix(".s")
base_name = Path(self.name).with_suffix("")
obj.src_obj_path = build_dir / "src" / f"{base_name}.o"
obj.asm_obj_path = build_dir / "mod" / f"{base_name}.o"
obj.host_obj_path = build_dir / "host" / f"{base_name}.o"
obj.ctx_path = build_dir / "src" / f"{base_name}.ctx"
return obj
class ProgressCategory:
def __init__(self, id: str, name: str) -> None:
self.id = id
self.name = name
class ProjectConfig: class ProjectConfig:
def __init__(self) -> None: def __init__(self) -> None:
@ -81,7 +134,7 @@ class ProjectConfig:
self.generate_map: bool = False # Generate map file(s) self.generate_map: bool = False # Generate map file(s)
self.asflags: Optional[List[str]] = None # Assembler flags self.asflags: Optional[List[str]] = None # Assembler flags
self.ldflags: Optional[List[str]] = None # Linker flags self.ldflags: Optional[List[str]] = None # Linker flags
self.libs: Optional[List[Dict[str, Any]]] = None # List of libraries self.libs: Optional[List[Library]] = None # List of libraries
self.linker_version: Optional[str] = None # mwld version self.linker_version: Optional[str] = None # mwld version
self.version: Optional[str] = None # Version name self.version: Optional[str] = None # Version name
self.warn_missing_config: bool = False # Warn on missing unit configuration self.warn_missing_config: bool = False # Warn on missing unit configuration
@ -109,6 +162,7 @@ class ProjectConfig:
self.progress_each_module: bool = ( self.progress_each_module: bool = (
True # Include individual modules, disable for large numbers of modules True # Include individual modules, disable for large numbers of modules
) )
self.progress_categories: List[ProgressCategory] = [] # Additional categories
# Progress fancy printing # Progress fancy printing
self.progress_use_fancy: bool = False self.progress_use_fancy: bool = False
@ -133,12 +187,17 @@ class ProjectConfig:
if getattr(self, attr) is None: if getattr(self, attr) is None:
sys.exit(f"ProjectConfig.{attr} missing") sys.exit(f"ProjectConfig.{attr} missing")
def find_object(self, name: str) -> Optional[Tuple[Dict[str, Any], Object]]: # Creates a map of object names to Object instances
# Options are fully resolved from the library and object
def objects(self) -> Dict[str, Object]:
out = {}
for lib in self.libs or {}: for lib in self.libs or {}:
for obj in lib["objects"]: objects: List[Object] = lib["objects"]
if obj.name == name: for obj in objects:
return lib, obj if obj.name in out:
return None sys.exit(f"Duplicate object name {obj.name}")
out[obj.name] = obj.resolve(self, lib)
return out
def out_path(self) -> Path: def out_path(self) -> Path:
return self.build_dir / str(self.version) return self.build_dir / str(self.version)
@ -174,7 +233,7 @@ def load_build_config(
f = open(build_config_path, "r", encoding="utf-8") f = open(build_config_path, "r", encoding="utf-8")
build_config: Dict[str, Any] = json.load(f) build_config: Dict[str, Any] = json.load(f)
config_version = build_config.get("version") config_version = build_config.get("version")
if not config_version: if config_version is None:
# Invalid config.json # Invalid config.json
f.close() f.close()
os.remove(build_config_path) os.remove(build_config_path)
@ -193,17 +252,19 @@ def load_build_config(
# Generate build.ninja and objdiff.json # Generate build.ninja and objdiff.json
def generate_build(config: ProjectConfig) -> None: def generate_build(config: ProjectConfig) -> None:
config.validate()
objects = config.objects()
build_config = load_build_config(config, config.out_path() / "config.json") build_config = load_build_config(config, config.out_path() / "config.json")
generate_build_ninja(config, build_config) generate_build_ninja(config, objects, build_config)
generate_objdiff_config(config, build_config) generate_objdiff_config(config, objects, build_config)
# Generate build.ninja # Generate build.ninja
def generate_build_ninja( def generate_build_ninja(
config: ProjectConfig, build_config: Optional[Dict[str, Any]] config: ProjectConfig,
objects: Dict[str, Object],
build_config: Optional[Dict[str, Any]],
) -> None: ) -> None:
config.validate()
out = io.StringIO() out = io.StringIO()
n = ninja_syntax.Writer(out) n = ninja_syntax.Writer(out)
n.variable("ninja_required_version", "1.3") n.variable("ninja_required_version", "1.3")
@ -227,7 +288,7 @@ def generate_build_ninja(
if config.debug: if config.debug:
ldflags += " -g" ldflags += " -g"
n.variable("ldflags", ldflags) n.variable("ldflags", ldflags)
if not config.linker_version: if config.linker_version is None:
sys.exit("ProjectConfig.linker_version missing") sys.exit("ProjectConfig.linker_version missing")
n.variable("mw_version", Path(config.linker_version)) n.variable("mw_version", Path(config.linker_version))
n.newline() n.newline()
@ -570,10 +631,6 @@ def generate_build_ninja(
# Source files # Source files
### ###
n.comment("Source files") n.comment("Source files")
build_asm_path = build_path / "mod"
build_src_path = build_path / "src"
build_host_path = build_path / "host"
build_config_path = build_path / "config.json"
def map_path(path: Path) -> Path: def map_path(path: Path) -> Path:
return path.parent / (path.name + ".MAP") return path.parent / (path.name + ".MAP")
@ -669,99 +726,82 @@ def generate_build_ninja(
host_source_inputs: List[Path] = [] host_source_inputs: List[Path] = []
source_added: Set[Path] = set() source_added: Set[Path] = set()
def c_build( def c_build(obj: Object, src_path: Path) -> Optional[Path]:
obj: Object, options: Dict[str, Any], lib_name: str, src_path: Path cflags_str = make_flags_str(obj.options["cflags"])
) -> Optional[Path]: if obj.options["extra_cflags"] is not None:
cflags_str = make_flags_str(options["cflags"]) extra_cflags_str = make_flags_str(obj.options["extra_cflags"])
if options["extra_cflags"] is not None:
extra_cflags_str = make_flags_str(options["extra_cflags"])
cflags_str += " " + extra_cflags_str cflags_str += " " + extra_cflags_str
used_compiler_versions.add(options["mw_version"]) used_compiler_versions.add(obj.options["mw_version"])
src_obj_path = build_src_path / f"{obj.base_name}.o"
src_base_path = build_src_path / obj.base_name
# Avoid creating duplicate build rules # Avoid creating duplicate build rules
if src_obj_path in source_added: if obj.src_obj_path in source_added:
return src_obj_path return obj.src_obj_path
source_added.add(src_obj_path) source_added.add(obj.src_obj_path)
shift_jis = options["shift_jis"]
if shift_jis is None:
shift_jis = config.shift_jis
# Add MWCC build rule # Add MWCC build rule
lib_name = obj.options["lib"]
n.comment(f"{obj.name}: {lib_name} (linked {obj.completed})") n.comment(f"{obj.name}: {lib_name} (linked {obj.completed})")
n.build( n.build(
outputs=src_obj_path, outputs=obj.src_obj_path,
rule="mwcc_sjis" if shift_jis else "mwcc", rule="mwcc_sjis" if obj.options["shift_jis"] else "mwcc",
inputs=src_path, inputs=src_path,
variables={ variables={
"mw_version": Path(options["mw_version"]), "mw_version": Path(obj.options["mw_version"]),
"cflags": cflags_str, "cflags": cflags_str,
"basedir": os.path.dirname(src_base_path), "basedir": os.path.dirname(obj.src_obj_path),
"basefile": src_base_path, "basefile": obj.src_obj_path.with_suffix(""),
}, },
implicit=mwcc_sjis_implicit if shift_jis else mwcc_implicit, implicit=(
mwcc_sjis_implicit if obj.options["shift_jis"] else mwcc_implicit
),
) )
# Add ctx build rule # Add ctx build rule
ctx_path = build_src_path / f"{obj.base_name}.ctx"
n.build( n.build(
outputs=ctx_path, outputs=obj.ctx_path,
rule="decompctx", rule="decompctx",
inputs=src_path, inputs=src_path,
implicit=decompctx, implicit=decompctx,
) )
# Add host build rule # Add host build rule
if options.get("host", False): if obj.options["host"]:
host_obj_path = build_host_path / f"{obj.base_name}.o"
host_base_path = build_host_path / obj.base_name
n.build( n.build(
outputs=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",
inputs=src_path, inputs=src_path,
variables={ variables={
"basedir": os.path.dirname(host_base_path), "basedir": os.path.dirname(obj.host_obj_path),
"basefile": host_base_path, "basefile": obj.host_obj_path.with_suffix(""),
}, },
) )
if options["add_to_all"]: if obj.options["add_to_all"]:
host_source_inputs.append(host_obj_path) host_source_inputs.append(obj.host_obj_path)
n.newline() n.newline()
if options["add_to_all"]: if obj.options["add_to_all"]:
source_inputs.append(src_obj_path) source_inputs.append(obj.src_obj_path)
return src_obj_path return obj.src_obj_path
def asm_build( def asm_build(obj: Object, src_path: Path, obj_path: Path) -> Optional[Path]:
obj: Object, if obj.options["asflags"] is None:
options: Dict[str, Any],
lib_name: str,
src_path: Path,
build_path: Path,
) -> Optional[Path]:
asflags = options["asflags"] or config.asflags
if asflags is None:
sys.exit("ProjectConfig.asflags missing") sys.exit("ProjectConfig.asflags missing")
asflags_str = make_flags_str(asflags) asflags_str = make_flags_str(obj.options["asflags"])
if options["extra_asflags"] is not None: if obj.options["extra_asflags"] is not None:
extra_asflags_str = make_flags_str(options["extra_asflags"]) extra_asflags_str = make_flags_str(obj.options["extra_asflags"])
asflags_str += " " + extra_asflags_str asflags_str += " " + extra_asflags_str
asm_obj_path = build_path / f"{obj.base_name}.o"
# Avoid creating duplicate build rules # Avoid creating duplicate build rules
if asm_obj_path in source_added: if obj_path in source_added:
return asm_obj_path return obj_path
source_added.add(asm_obj_path) source_added.add(obj_path)
# Add assembler build rule # Add assembler build rule
lib_name = obj.options["lib"]
n.comment(f"{obj.name}: {lib_name} (linked {obj.completed})") n.comment(f"{obj.name}: {lib_name} (linked {obj.completed})")
n.build( n.build(
outputs=asm_obj_path, outputs=obj_path,
rule="as", rule="as",
inputs=src_path, inputs=src_path,
variables={"asflags": asflags_str}, variables={"asflags": asflags_str},
@ -769,61 +809,40 @@ def generate_build_ninja(
) )
n.newline() n.newline()
if options["add_to_all"]: if obj.options["add_to_all"]:
source_inputs.append(asm_obj_path) source_inputs.append(obj_path)
return asm_obj_path return obj_path
def add_unit(build_obj, link_step: LinkStep): def add_unit(build_obj, link_step: LinkStep):
obj_path, obj_name = build_obj["object"], build_obj["name"] obj_path, obj_name = build_obj["object"], build_obj["name"]
result = config.find_object(obj_name) obj = objects.get(obj_name)
if not result: if obj is None:
if config.warn_missing_config and not build_obj["autogenerated"]: if config.warn_missing_config and not build_obj["autogenerated"]:
print(f"Missing configuration for {obj_name}") print(f"Missing configuration for {obj_name}")
link_step.add(obj_path) link_step.add(obj_path)
return return
lib, obj = result
lib_name = lib["lib"]
# Use object options, then library options
options = lib.copy()
for key, value in obj.options.items():
if value is not None or key not in options:
options[key] = value
unit_src_path = Path(lib.get("src_dir", config.src_dir)) / options["source"]
unit_asm_path: Optional[Path] = None
if config.asm_dir is not None:
unit_asm_path = (
Path(lib.get("asm_dir", config.asm_dir)) / options["source"]
).with_suffix(".s")
link_built_obj = obj.completed link_built_obj = obj.completed
built_obj_path: Optional[Path] = None built_obj_path: Optional[Path] = None
if unit_src_path.exists(): if obj.src_path.exists():
if unit_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, options, lib_name, unit_src_path) built_obj_path = c_build(obj, obj.src_path)
elif unit_src_path.suffix == ".s": elif obj.src_path.suffix == ".s":
# Add assembler build rule # Add assembler build rule
built_obj_path = asm_build( built_obj_path = asm_build(obj, obj.src_path, obj.src_obj_path)
obj, options, lib_name, unit_src_path, build_src_path
)
else: else:
sys.exit(f"Unknown source file type {unit_src_path}") sys.exit(f"Unknown source file type {obj.src_path}")
else: else:
if config.warn_missing_source or obj.completed: if config.warn_missing_source or obj.completed:
print(f"Missing source file {unit_src_path}") print(f"Missing source file {obj.src_path}")
link_built_obj = False link_built_obj = False
# Assembly overrides # Assembly overrides
if unit_asm_path is not None and unit_asm_path.exists(): if obj.asm_path is not None and obj.asm_path.exists():
link_built_obj = True link_built_obj = True
built_obj_path = asm_build( built_obj_path = asm_build(obj, obj.asm_path, obj.asm_obj_path)
obj, options, lib_name, unit_asm_path, build_asm_path
)
if link_built_obj and built_obj_path is not None: if link_built_obj and built_obj_path is not None:
# Use the source-built object # Use the source-built object
@ -832,7 +851,10 @@ def generate_build_ninja(
# Use the original (extracted) object # Use the original (extracted) object
link_step.add(obj_path) link_step.add(obj_path)
else: else:
sys.exit(f"Missing object for {obj_name}: {unit_src_path} {lib} {obj}") lib_name = obj.options["lib"]
sys.exit(
f"Missing object for {obj_name}: {obj.src_path} {lib_name} {obj}"
)
# Add DOL link step # Add DOL link step
link_step = LinkStep(build_config) link_step = LinkStep(build_config)
@ -848,7 +870,7 @@ def generate_build_ninja(
add_unit(unit, module_link_step) add_unit(unit, module_link_step)
# Add empty object to empty RELs # Add empty object to empty RELs
if len(module_link_step.inputs) == 0: if len(module_link_step.inputs) == 0:
if not config.rel_empty_file: if config.rel_empty_file is None:
sys.exit("ProjectConfig.rel_empty_file missing") sys.exit("ProjectConfig.rel_empty_file missing")
add_unit( add_unit(
{ {
@ -1078,6 +1100,7 @@ def generate_build_ninja(
### ###
# Split DOL # Split DOL
### ###
build_config_path = build_path / "config.json"
n.comment("Split DOL into relocatable objects") n.comment("Split DOL into relocatable objects")
n.rule( n.rule(
name="split", name="split",
@ -1138,9 +1161,11 @@ def generate_build_ninja(
# Generate objdiff.json # Generate objdiff.json
def generate_objdiff_config( def generate_objdiff_config(
config: ProjectConfig, build_config: Optional[Dict[str, Any]] config: ProjectConfig,
objects: Dict[str, Object],
build_config: Optional[Dict[str, Any]],
) -> None: ) -> None:
if not build_config: if build_config is None:
return return
objdiff_config: Dict[str, Any] = { objdiff_config: Dict[str, Any] = {
@ -1170,6 +1195,7 @@ def generate_objdiff_config(
"GC/1.2.5": "mwcc_233_163", "GC/1.2.5": "mwcc_233_163",
"GC/1.2.5e": "mwcc_233_163e", "GC/1.2.5e": "mwcc_233_163e",
"GC/1.2.5n": "mwcc_233_163n", "GC/1.2.5n": "mwcc_233_163n",
"GC/1.3": "mwcc_242_53",
"GC/1.3.2": "mwcc_242_81", "GC/1.3.2": "mwcc_242_81",
"GC/1.3.2r": "mwcc_242_81r", "GC/1.3.2r": "mwcc_242_81r",
"GC/2.0": "mwcc_247_92", "GC/2.0": "mwcc_247_92",
@ -1208,30 +1234,12 @@ def generate_objdiff_config(
"target_path": obj_path, "target_path": obj_path,
} }
result = config.find_object(obj_name) obj = objects.get(obj_name)
if not result: if obj is None or not obj.src_path.exists():
objdiff_config["units"].append(unit_config) objdiff_config["units"].append(unit_config)
return return
lib, obj = result cflags = obj.options["cflags"]
src_dir = Path(lib.get("src_dir", config.src_dir))
# Use object options, then library options
options = lib.copy()
for key, value in obj.options.items():
if value is not None or key not in options:
options[key] = value
unit_src_path = src_dir / str(options["source"])
if not unit_src_path.exists():
objdiff_config["units"].append(unit_config)
return
cflags = options["cflags"]
src_obj_path = build_path / "src" / f"{obj.base_name}.o"
src_ctx_path = build_path / "src" / f"{obj.base_name}.ctx"
reverse_fn_order = False reverse_fn_order = False
if type(cflags) is list: if type(cflags) is list:
for flag in cflags: for flag in cflags:
@ -1250,29 +1258,38 @@ def generate_objdiff_config(
cflags = list(filter(keep_flag, cflags)) cflags = list(filter(keep_flag, cflags))
# Add appropriate lang flag # Add appropriate lang flag
if unit_src_path.suffix in (".cp", ".cpp"): if obj.src_path.suffix in (".cp", ".cpp"):
cflags.insert(0, "-lang=c++") cflags.insert(0, "-lang=c++")
else: else:
cflags.insert(0, "-lang=c") cflags.insert(0, "-lang=c")
unit_config["base_path"] = src_obj_path unit_config["base_path"] = obj.src_obj_path
unit_config["reverse_fn_order"] = reverse_fn_order unit_config["reverse_fn_order"] = reverse_fn_order
unit_config["complete"] = obj.completed unit_config["complete"] = obj.completed
compiler_version = COMPILER_MAP.get(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 {options['mw_version']}") print(f"Missing scratch compiler mapping for {obj.options['mw_version']}")
else: else:
cflags_str = make_flags_str(cflags) cflags_str = make_flags_str(cflags)
if options["extra_cflags"] is not None: if obj.options["extra_cflags"] is not None:
extra_cflags_str = make_flags_str(options["extra_cflags"]) extra_cflags_str = make_flags_str(obj.options["extra_cflags"])
cflags_str += " " + extra_cflags_str cflags_str += " " + extra_cflags_str
unit_config["scratch"] = { unit_config["scratch"] = {
"platform": "gc_wii", "platform": "gc_wii",
"compiler": compiler_version, "compiler": compiler_version,
"c_flags": cflags_str, "c_flags": cflags_str,
"ctx_path": src_ctx_path, "ctx_path": obj.ctx_path,
"build_ctx": True, "build_ctx": True,
} }
metadata = {
"source_path": obj.src_path,
}
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
@ -1295,9 +1312,11 @@ def generate_objdiff_config(
# Calculate, print and write progress to progress.json # Calculate, print and write progress to progress.json
def calculate_progress(config: ProjectConfig) -> None: def calculate_progress(config: ProjectConfig) -> None:
config.validate()
objects = config.objects()
out_path = config.out_path() out_path = config.out_path()
build_config = load_build_config(config, out_path / "config.json") build_config = load_build_config(config, out_path / "config.json")
if not build_config: if build_config is None:
return return
class ProgressUnit: class ProgressUnit:
@ -1329,12 +1348,8 @@ def calculate_progress(config: ProjectConfig) -> None:
# Skip autogenerated objects # Skip autogenerated objects
return return
result = config.find_object(build_obj["name"]) obj = objects.get(build_obj["name"])
if not result: if obj is None or not obj.completed:
return
_, obj = result
if not obj.completed:
return return
self.code_progress += build_obj["code_size"] self.code_progress += build_obj["code_size"]
@ -1348,26 +1363,53 @@ def calculate_progress(config: ProjectConfig) -> None:
def data_frac(self) -> float: def data_frac(self) -> float:
return self.data_progress / self.data_total return self.data_progress / self.data_total
progress_units: Dict[str, ProgressUnit] = {}
if len(config.progress_categories) > 0:
for category in config.progress_categories:
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:
for module in build_config["modules"]:
progress_units[module["name"]] = ProgressUnit(module["name"])
def add_unit(id: str, unit: Dict[str, Any]) -> None:
progress = progress_units.get(id)
if progress is not None:
progress.add(unit)
# Add DOL units # Add DOL units
all_progress = ProgressUnit("All") if config.progress_all else None
dol_progress = ProgressUnit("DOL")
for unit in build_config["units"]: for unit in build_config["units"]:
if all_progress: add_unit("all", unit)
all_progress.add(unit) add_unit("dol", unit)
dol_progress.add(unit) obj = objects.get(unit["name"])
if obj is not None:
category = obj.options["progress_category"]
if isinstance(category, list):
for category in category:
add_unit(category, unit)
elif category is not None:
add_unit(category, unit)
# Add REL units # Add REL units
rels_progress = ProgressUnit("Modules") if config.progress_modules else None
modules_progress: List[ProgressUnit] = []
for module in build_config["modules"]: for module in build_config["modules"]:
progress = ProgressUnit(module["name"])
modules_progress.append(progress)
for unit in module["units"]: for unit in module["units"]:
if all_progress: add_unit("all", unit)
all_progress.add(unit) add_unit("modules", unit)
if rels_progress: add_unit(module["name"], unit)
rels_progress.add(unit) obj = objects.get(unit["name"])
progress.add(unit) if obj is not None:
category = obj.options["progress_category"]
if isinstance(category, list):
for category in category:
add_unit(category, unit)
elif category is not None:
add_unit(category, unit)
# Print human-readable progress # Print human-readable progress
print("Progress:") print("Progress:")
@ -1395,14 +1437,7 @@ def calculate_progress(config: ProjectConfig) -> None:
) )
) )
if all_progress: for progress in progress_units.values():
print_category(all_progress)
print_category(dol_progress)
module_count = len(build_config["modules"])
if module_count > 0:
print_category(rels_progress)
if config.progress_each_module:
for progress in modules_progress:
print_category(progress) print_category(progress)
# Generate and write progress.json # Generate and write progress.json
@ -1416,14 +1451,7 @@ def calculate_progress(config: ProjectConfig) -> None:
"data/total": unit.data_total, "data/total": unit.data_total,
} }
if all_progress: for id, progress in progress_units.items():
add_category("all", all_progress) add_category(id, progress)
add_category("dol", dol_progress)
if len(build_config["modules"]) > 0:
if rels_progress:
add_category("modules", rels_progress)
if config.progress_each_module:
for progress in modules_progress:
add_category(progress.name, progress)
with open(out_path / "progress.json", "w", encoding="utf-8") as w: with open(out_path / "progress.json", "w", encoding="utf-8") as w:
json.dump(progress_json, w, indent=4) json.dump(progress_json, w, indent=4)