llama/compat: make patch-apply idempotent

FetchContent's PATCH_COMMAND runs after each update step — including on
incremental rebuilds. `git apply` fails when the patch is already applied,
which bricks the build until the dev wipes build/ entirely.

Fix by routing the apply through a small apply-patch.cmake helper that
checks `git apply --reverse --check` first. If the patch cleanly reverses,
it's already applied and we skip. Otherwise apply forward. Both branches
surface real errors (drift against upstream, missing patch file, etc.).

Verified: fresh configure+build applies the patch once; re-running the
same commands is a no-op with no errors.
This commit is contained in:
jmorganca 2026-04-18 23:34:54 -07:00
parent 7449b539ab
commit 436f2e2b15
2 changed files with 50 additions and 5 deletions

View file

@ -0,0 +1,44 @@
# Idempotent patch applier used by compat.cmake.
#
# Invocation (from a CMake PATCH_COMMAND):
# cmake -DPATCH_FILE=<abs path> -P apply-patch.cmake
#
# The patch is applied in the current working directory (which ExternalProject
# / FetchContent sets to the fetched source's SOURCE_DIR). If the patch is
# already applied detected via `git apply --reverse --check` this script
# is a no-op. This makes re-configuring and re-building the project safe.
if(NOT DEFINED PATCH_FILE)
message(FATAL_ERROR "apply-patch.cmake: PATCH_FILE not set")
endif()
if(NOT EXISTS "${PATCH_FILE}")
message(FATAL_ERROR "apply-patch.cmake: PATCH_FILE does not exist: ${PATCH_FILE}")
endif()
find_package(Git QUIET REQUIRED)
# If the patch can be REVERSED cleanly, it's already applied. Skip.
execute_process(
COMMAND ${GIT_EXECUTABLE} apply --reverse --check "${PATCH_FILE}"
RESULT_VARIABLE _reverse_check
OUTPUT_QUIET ERROR_QUIET
)
if(_reverse_check EQUAL 0)
message(STATUS "llama/compat: patch already applied, skipping")
return()
endif()
# Otherwise, apply forward.
execute_process(
COMMAND ${GIT_EXECUTABLE} apply --whitespace=nowarn "${PATCH_FILE}"
RESULT_VARIABLE _apply_result
)
if(NOT _apply_result EQUAL 0)
message(FATAL_ERROR
"llama/compat: failed to apply ${PATCH_FILE}\n"
"This usually means upstream llama.cpp has drifted. "
"Regenerate the patch (see llama/compat/README.md) against the "
"pinned LLAMA_CPP_VERSION and retry.")
endif()
message(STATUS "llama/compat: applied patch")

View file

@ -24,9 +24,9 @@
set(_compat_dir ${CMAKE_CURRENT_LIST_DIR})
# Expose a single variable the main CMakeLists passes into FetchContent's
# PATCH_COMMAND. Uses CMake's own `-E copy` so it's cross-platform; uses
# `git apply` because the patch is in unified git-diff format (same as what
# `git diff` produces regeneration is one command, see README).
# PATCH_COMMAND. Uses CMake's own `-E copy` for cross-platform file copy.
# The patch itself is applied via a small CMake script so the step is
# idempotent re-configuring or rebuilding won't fail with "already applied".
set(OLLAMA_LLAMA_CPP_COMPAT_PATCH_COMMAND
${CMAKE_COMMAND} -E copy
"${_compat_dir}/llama-ollama-compat.h"
@ -34,8 +34,9 @@ set(OLLAMA_LLAMA_CPP_COMPAT_PATCH_COMMAND
COMMAND ${CMAKE_COMMAND} -E copy
"${_compat_dir}/llama-ollama-compat.cpp"
"src/llama-ollama-compat.cpp"
COMMAND git apply --whitespace=nowarn
"${_compat_dir}/upstream-edits.patch"
COMMAND ${CMAKE_COMMAND}
-DPATCH_FILE=${_compat_dir}/upstream-edits.patch
-P ${_compat_dir}/apply-patch.cmake
CACHE INTERNAL "llama.cpp compat patch command for FetchContent")
# Also export the individual paths in case callers want to do something