ykit/buildtool/polly/patcher.py

72 lines
2.7 KiB
Python

from .parser import parse_patch_file
from pathlib import Path
from task.util import *
def resolve_position(location, src: str):
if location["type"] == "char":
return location["index"]
elif location["type"] == "lncol":
j = 0
for i, x in enumerate(src.split('\n')):
if (i+1) == location["line"]:
if location["column"] > len(x):
print(f"Cannot insert at line {location['line']} column {location['column']}. The line is not that long.")
exit(-1)
return j + location["column"]
j += len(x) + 1
print(f"Cannot insert at line {location['line']}. The file is not long enough.")
exit(-1)
def read_insertion(action, src):
position = resolve_position(action["at"], src)
return {"content": action["content"], "pos": position}
def process_patch_file(patch_file: str, check_date: bool = True):
patches = parse_patch_file(patch_file)
for patch in patches:
target_src = EXTRACTED_DIR / patch["target"]
target_dest = PATCHED_RSC_DIR / patch["target"]
# Skip old patches
if check_date and target_dest.exists() and target_dest.stat().st_mtime > patch["timestamp"]:
print(f"Skipping patch {patch['target']}...")
continue
print(f"Patching {patch['target']} with {len(patch['actions'])} actions")
# Read the source of the file
with open(target_src, 'r') as f:
src = f.read()
# Resolve the actual char locations in the patches
insertions = [read_insertion(action, src) for action in patch["actions"]]
insertions = [x for x in insertions if x is not None]
for insertion in insertions:
src = src[:insertion["pos"]] + insertion["content"] + src[insertion["pos"]:]
# Correct insertions that follow this one
for i2 in insertions:
# Only update insertions that come *after* this one
if i2 == insertion or i2["pos"] < insertion["pos"]:
continue
i2["pos"] += len(insertion["content"])
# Make parent dirs
target_dest.parent.mkdir(parents=True, exist_ok=True)
# Write the changed output
with open(target_dest, 'w') as f:
f.write(src)
def process_all_patches(dir: Path | str, check_date: bool = True):
dir = Path(dir)
for patch_file in dir.rglob("*.patch"):
print("Processing patch {}".format(patch_file.relative_to(dir)))
process_patch_file(str(patch_file), check_date=check_date)