#!/usr/bin/env python3 from pathlib import Path from yaml import safe_load def files(p): for i in p: if i.is_file(): yield i class Patchwork(dict): patches_path = None patchwork_path = None def __init__(self, *arkg, **kwargs): self.patches_path = Path(kwargs.pop("patches_path")) self.patchwork_path = Path(kwargs.pop("patchwork_path")) super().__init__(*arkg, **kwargs) patchwork_mkdir = 'mkdir -p {}/{}' def prerequisites(self, vanilla_path): for i in self.values(): yield self.patches_path/i for i in self.keys(): yield vanilla_path/i def targets(self): for i in self.keys(): yield self.patchwork_path/i def action_generator(self, key, value, vanilla_path): yield f"mkdir -p {self.patchwork_path}/{Path(key).parent}" yield f"patch -u --binary -N -o '{self.patchwork_path}/{key}' "\ f"'{vanilla_path}/{key}' '{self.patches_path}/{value}'" def actions(self, vanilla_path): for k, v in self.items(): yield from self.action_generator(k, v, vanilla_path) def tasks(self, vanilla_path): for k, v in self.items(): yield ( [ self.patches_path/v, vanilla_path/k ], # src ( patch file, orig file ) [ self.patchwork_path/k ], #dst ( patched file ) [ f"mkdir -p {self.patchwork_path}/{Path(k).parent}", f"patch -u --binary -N -o '{self.patchwork_path}/{k}' "\ f"'{vanilla_path}/{k}' '{self.patches_path}/{v}'" ] # action ( mkdir, patch ) ) mod_name = 'randchgs' mod_install_path = 'C:/Users/User/Documents/Paradox Interactive/Hearts of Iron IV/mod/randchgs' dir_vanilla = Path("../vanilla/v1.13.4") dir_image = Path('build/image') dir_modimage = Path(f"{dir_image}/{mod_name}") dir_src_raw = Path('src/raw') dir_src_raw_files = list(files(Path(dir_src_raw).rglob("*"))) dir_patches_path = Path('src/patches') dir_patchwork_path = Path('build/patched') patchwork = Patchwork({ 'map/definition.csv': 'map_definition.patch', 'history/countries/ISR - Israel.txt': 'history_countries_ISR.patch', # 'history/states/728-Guangzhouwan.txt' : 'history_states_728.patch', }, patches_path = dir_patches_path, patchwork_path = dir_patchwork_path ) patchwork_scripted_template_input_file = dir_patches_path/'extrapoint.yaml' with open(patchwork_scripted_template_input_file) as s: patchwork_scripted_template_input_data = safe_load(s) dir_image_files = [ f"{dir_modimage}/{f.relative_to(dir_src_raw)}" for f in dir_src_raw_files ] + \ [ f"{dir_modimage}/{f.relative_to(patchwork.patchwork_path)}" for f in patchwork.targets() ] DOIT_CONFIG = { 'default_tasks': ['build'], 'action_string_formatting': 'both' } def history_states_path(state_id): history_state_file = next((Path(dir_vanilla)/'history'/'states').glob(f"{state_id}*")) return { 'src' : history_state_file, 'dest': Path(dir_patchwork_path)/history_state_file.relative_to(dir_vanilla) } def task_patch_history_states_scripted(): def history_state_patch(task, provinces): source = Path(next(iter(task.file_dep))) target = Path(task.targets[0]) target.parent.mkdir(parents=True, exist_ok=True) seen_history = False wrote_history = False with source.open(newline='') as infile: with target.open('w') as outfile: for line_in in infile: outfile.write(line_in) seen_history = seen_history or ( 'history' in line_in ) if not wrote_history and seen_history and '{' in line_in: for province_id, province_info in provinces.items(): outfile.write( "\t\tvictory_points = {\n"\ f"\t\t\t{province_id} {province_info['points']}\n"\ "\t\t}\n"\ ) wrote_history = True target.touch() for state_id, state_data in patchwork_scripted_template_input_data.items(): files = history_states_path(state_id) yield { 'name' : f"{state_id}", 'file_dep' : [ files['src'] ], 'targets' : [ files['dest'] ], 'actions' : [ ( history_state_patch, [], {'provinces' : state_data}) ], 'clean' : True } def task_patch(): for src, dest, patch in patchwork.tasks(dir_vanilla): yield { 'name' : f"{dest[0]}", 'file_dep' : src, 'targets' : dest, 'actions' : patch, 'clean' : True } def task_image(): filelist = dir_src_raw_files + list(patchwork.targets()) return { 'file_dep' : filelist + [ history_states_path(state_id)['dest'] for state_id in patchwork_scripted_template_input_data.keys() ], 'targets' : dir_image_files, 'actions' : [f"mkdir -p {dir_modimage}", f"rsync -rv {dir_src_raw}/ {patchwork.patchwork_path}/"\ f" {dir_modimage}/" ], 'clean' : True } def task_image_mod(): dep = 'src/raw/descriptor.mod' def prepare_modname_mod(task): source = Path(next(iter(task.file_dep))) target = Path(task.targets[0]) target.parent.mkdir(parents=True, exist_ok=True) with source.open() as s: with target.open('w') as o: o.write(s.read()) o.write(f"path=\"{mod_install_path}\"") return { 'file_dep' : [ dep ], 'targets' : [f"{dir_image}/{mod_name}.mod" ], 'actions' : [ prepare_modname_mod ], 'clean' : True } def task_build(): return { 'task_dep' : [ 'image' ], 'file_dep' : [f"{dir_image}/{mod_name}.mod" ] + dir_image_files, 'targets' : [f"{dir_image}/{mod_name}.zip" ], 'actions' : [f"cd {dir_image} && zip -r {mod_name}.zip {mod_name}", f"cd {dir_image} && zip -r {mod_name}.zip {mod_name}.mod" ], 'clean' : True } def task_purge_build(): return { 'actions' : [ 'rm -rf build' ] }