diff options
| author | historia <historiavg@proton.me> | 2026-08-25 01:47:32 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-25 01:47:32 -0400 |
| commit | a76896ec7ce60baaefcf5dd8062776be01dbc57a (patch) | |
| tree | 126985294645ab412cc2bae72a64539663222e7b | |
| parent | fe4b2b9eb7fb8aac81f65630720c9079d0a3121a (diff) | |
| download | tts-audiobook-generator-a76896ec7ce60baaefcf5dd8062776be01dbc57a.tar.gz | |
fix: automatic patch for top-k.cu to file ggml build error
| -rwxr-xr-x | app/backends/audiocpp.py | 109 | ||||
| -rw-r--r-- | app/backends/patches/ggml-top-k-cuda-iterator.patch | 10 |
2 files changed, 113 insertions, 6 deletions
diff --git a/app/backends/audiocpp.py b/app/backends/audiocpp.py index 7a8eb75..9cb8e24 100755 --- a/app/backends/audiocpp.py +++ b/app/backends/audiocpp.py @@ -95,6 +95,12 @@ TASK_VDES = "vdes" AUDIOCPP_DIR_NAME = "audio.cpp" AUDIOCPP_GIT_URL = "https://github.com/0xShug0/audio.cpp" +# ggml build patches shipped in this repo and applied to the (gitignored) +# audio.cpp checkout before building, so a fresh clone survives known ggml +# build bugs the audio.cpp fork has not re-vendored yet. See +# apply_ggml_patches() below. +PATCH_DIR = Path(__file__).resolve().parent / "patches" + # Sentinel returned by tui.confirm (via its cancel_value) when the user # presses Esc on an overwrite prompt to go back to the wav-directory browser # instead of aborting the wizard. @@ -1358,18 +1364,24 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser audiocpp_dir = find_local_checkout() if audiocpp_dir is None: target = APP_DIR / AUDIOCPP_DIR_NAME - rc = taskview.run_steps(stdscr, "Clone audio.cpp", [taskview.TaskStep( - f"Cloning audio.cpp into {target}", - lambda emit, cancel: common.git_clone( - AUDIOCPP_GIT_URL, target, emit=emit, cancel=cancel))]) + rc = taskview.run_steps(stdscr, "Clone audio.cpp", [ + taskview.TaskStep( + f"Cloning audio.cpp into {target}", + lambda emit, cancel: common.git_clone( + AUDIOCPP_GIT_URL, target, emit=emit, cancel=cancel)), + taskview.TaskStep( + "Apply ggml build patches", + lambda emit, cancel: apply_ggml_patches( + target, emit=emit, cancel=cancel)), + ]) if rc == 130: # Cancelled from the task view: abort the wizard quietly. return None if rc != 0: raise _TuiError( - f"git clone failed (exit {rc}). Clone " + f"audio.cpp setup step failed (exit {rc}). Clone " f"audio.cpp manually: git clone " - f"{AUDIOCPP_GIT_URL} {target}") + f"{AUDIOCPP_GIT_URL} {target}, then re-run") audiocpp_dir = target resolve_checkout(audiocpp_dir) first = _after_families() @@ -1818,6 +1830,75 @@ def find_build_script(audiocpp_dir: Path) -> Optional[Path]: return candidates[0] if candidates else None +# Each entry pairs a shipped patch with the vendored file it touches and a +# regex marker proving the fix is already present (so the patch is skipped +# idempotently once applied, or once the fork re-vendors a fixed ggml). +GGML_PATCHES = [ + { + "file": "ggml-top-k-cuda-iterator.patch", + "target": "external/ggml/src/ggml-cuda/top-k.cu", + "marker": r"#\s*include\s*<cuda/iterator>", + "label": "top-k.cu: add #include <cuda/iterator> (CCCL 3.x build fix)", + }, +] + + +def apply_ggml_patches(audiocpp_dir: Path, *, emit=None, cancel=None) -> int: + """Apply the shipped ggml build patches to an audio.cpp checkout. + + Idempotent: a patch whose marker already matches its target is skipped + (it is either already applied, or the fork re-vendored a fixed ggml). A + patch that no longer applies because the vendored file changed shape is a + loud, non-interactive failure — the build is aborted so the user + re-evaluates the patch instead of hitting a known nvcc break minutes + later. Returns 0 when every patch is applied or already present, 1 on + drift, 130 when cancelled. + """ + for patch in GGML_PATCHES: + if cancel is not None and cancel.is_set(): + return 130 + target = audiocpp_dir / patch["target"] + if not target.is_file(): + print(f"[INFO] {patch['file']}: target {patch['target']} not " + f"present in this checkout; skipping") + continue + try: + text = target.read_text(encoding="utf-8", errors="ignore") + except OSError as exc: + print(f"[WARNING] {patch['file']}: could not read {target}: " + f"{exc}; skipping") + continue + if re.search(patch["marker"], text): + print(f"[OK] {patch['file']}: fix already present, skipping") + continue + patch_path = PATCH_DIR / patch["file"] + if not patch_path.is_file(): + print(f"[ERROR] {patch['file']}: patch file not found at " + f"{patch_path}; cannot apply") + return 1 + check_argv = ["git", "-C", str(audiocpp_dir), "apply", "--check", + "--whitespace=nowarn", str(patch_path)] + check_rc = common.run_console_subprocess( + check_argv, emit=emit, cancel=cancel) + if check_rc != 0: + print(f"[ERROR] {patch['file']}: no longer applies to " + f"{patch['target']} (git apply --check exit {check_rc}). " + f"The audio.cpp fork's vendored ggml changed shape and " + f"still lacks the fix. Re-evaluate {patch_path}: " + f"regenerate the patch, or drop this entry if the fork " + f"now ships the fix.") + return 1 + apply_argv = ["git", "-C", str(audiocpp_dir), "apply", + "--whitespace=nowarn", str(patch_path)] + rc = common.run_console_subprocess( + apply_argv, emit=emit, cancel=cancel) + if rc != 0: + print(f"[ERROR] {patch['file']}: git apply failed (exit {rc})") + return rc + print(f"[OK] {patch['file']}: applied ({patch['label']})") + return 0 + + def build_audiocpp(audiocpp_dir: Path, backend: str, *, emit=None, cancel=None) -> int: """Build audiocpp_server for BACKEND, streaming output. @@ -1843,6 +1924,15 @@ def build_audiocpp(audiocpp_dir: Path, backend: str, *, if emit is not None: common.record_post_tui_notice(message) return 1 + patch_rc = apply_ggml_patches(audiocpp_dir, emit=emit, cancel=cancel) + if patch_rc != 0: + message = ("[ERROR] ggml build patches could not be applied; " + "aborting audiocpp_server build. See the messages above " + "and re-evaluate app/backends/patches/.") + print(message) + if emit is not None: + common.record_post_tui_notice(message) + return patch_rc argv = ["sh", str(script), "--backend", backend, "--target", "audiocpp_server"] command = f"cd {audiocpp_dir} && {shlex.join(argv)}" @@ -2159,6 +2249,13 @@ def _collect_from_flags(args: argparse.Namespace, if rc != 0: parser.error(f"git clone failed (exit {rc}); clone audio.cpp " f"manually: git clone {AUDIOCPP_GIT_URL} {target}") + patch_rc = apply_ggml_patches(target) + if patch_rc != 0: + parser.error( + f"ggml build patches could not be applied to {target} " + f"(exit {patch_rc}); see messages above. The audio.cpp " + f"fork's vendored ggml may have changed — re-evaluate " + f"app/backends/patches/.") audiocpp_dir = target if audiocpp_dir is None: parser.error( diff --git a/app/backends/patches/ggml-top-k-cuda-iterator.patch b/app/backends/patches/ggml-top-k-cuda-iterator.patch new file mode 100644 index 0000000..0eb89a5 --- /dev/null +++ b/app/backends/patches/ggml-top-k-cuda-iterator.patch @@ -0,0 +1,10 @@ +--- a/external/ggml/src/ggml-cuda/top-k.cu ++++ b/external/ggml/src/ggml-cuda/top-k.cu +@@ -4,6 +4,7 @@ + #ifdef GGML_CUDA_USE_CUB + # include <cub/cub.cuh> + # if (CCCL_MAJOR_VERSION >= 3 && CCCL_MINOR_VERSION >= 2) + # define CUB_TOP_K_AVAILABLE ++# include <cuda/iterator> + using namespace cub; + # endif // CCCL_MAJOR_VERSION >= 3 && CCCL_MINOR_VERSION >= 2 |
