import fs from "node:fs"; import path from "node:path"; import { spawnSync } from "node:child_process"; const root = path.resolve(import.meta.dir, ".."); const krita = path.join(root, "krita"); const patchDir = path.join(root, "krita-patches"); const patchFile = path.join(patchDir, "xiaomi-eink.patch"); // Keep generated patches limited to this project. The nested Krita checkout may carry // line-ending or formatter changes that are not part of the E Ink product patch. const patchPaths = [ "CMakeLists.txt", "libs/ui/KisApplication.cpp", "libs/ui/KisMainWindow.cpp", "libs/ui/thememanager.cpp", "packaging/android/apk/AndroidManifest.xml", "packaging/android/apk/build.gradle", "packaging/android/apk/proguard-rules.pro", "packaging/android/apk/src/org/krita/android/MainActivity.java", "plugins/CMakeLists.txt", "plugins/dockers/CMakeLists.txt", "plugins/filters/CMakeLists.txt", "plugins/impex/CMakeLists.txt", "plugins/paintops/CMakeLists.txt", "plugins/tools/CMakeLists.txt", ]; const untrackedPatchPaths = [ "EINK_SLIM.md", "packaging/android/apk/.gitignore", "packaging/android/apk/settings.gradle", "packaging/android/apk/lib/arm64-v8a/libfakeRKEpdUtils.so", "packaging/android/apk/src/com/moan/fakerkepdlib/EpdUtil.java", "packaging/android/apk/src/org/krita/android/XiaomiEinkController.java", "plugins/dockers/einkmonochromeselector/CMakeLists.txt", "plugins/dockers/einkmonochromeselector/einkmonochromeselector.cpp", "plugins/dockers/einkmonochromeselector/krita_einkmonochromeselector.json", "plugins/extensions/einksync/CMakeLists.txt", "plugins/extensions/einksync/EinkSyncPlugin.action", "plugins/extensions/einksync/EinkSyncPlugin.cpp", "plugins/extensions/einksync/kritaeinksync.json", ]; function git(args: string[], allowDifference = false): string { const result = spawnSync("git", ["-C", krita, ...args], { encoding: "utf8", maxBuffer: 128 * 1024 * 1024, }); if (result.error) throw result.error; if (result.status !== 0 && !(allowDifference && result.status === 1)) { throw new Error(result.stderr || `git ${args.join(" ")} failed`); } return result.stdout; } function generate(): void { fs.mkdirSync(patchDir, { recursive: true }); let output = git([ "diff", "--binary", "--ignore-space-at-eol", "--", ...patchPaths, ]); const available = new Set( git(["ls-files", "--others", "--exclude-standard"]) .split("\n") .filter(Boolean), ); const untracked = untrackedPatchPaths.filter((file) => available.has(file)); for (const file of untracked) { output += git( ["diff", "--no-index", "--binary", "--", "/dev/null", file], true, ); } if (!output.trim()) throw new Error("Krita has no changes to package"); fs.writeFileSync(patchFile, output); console.log(`[ok] Generated ${patchFile} with ${untracked.length} new files`); } function apply(): void { if (!fs.existsSync(path.join(krita, ".git"))) throw new Error("Initialize the Krita submodule first"); if (!fs.existsSync(patchFile)) throw new Error(`Missing ${patchFile}`); const applyArgs = ["apply", "--ignore-space-change", "--recount"]; const check = spawnSync( "git", ["-C", krita, ...applyArgs, "--check", patchFile], { encoding: "utf8", }, ); if (check.status === 0) { git([...applyArgs, "--whitespace=nowarn", patchFile]); console.log("[ok] Applied E Ink Krita patch set"); return; } const reverse = spawnSync( "git", ["-C", krita, ...applyArgs, "--reverse", "--check", patchFile], { encoding: "utf8" }, ); if (reverse.status === 0) { console.log("[ok] E Ink Krita patch set is already applied"); return; } throw new Error(`Patch does not apply cleanly\n${check.stderr}`); } if (process.argv.includes("--generate")) generate(); else apply();