Files
EVEngine/cmake/patch_third_party.cmake

83 lines
3.3 KiB
CMake

# Idempotent git-apply wrapper used by ExternalProject PATCH_COMMAND.
#
# Usage:
# cmake -DPATCH=<abs path to .patch>
# -DPATCH_DIR=<git checkout root the patch paths are relative to>
# -P cmake/patch_third_party.cmake
#
# The patch is applied with `git apply` inside PATCH_DIR. If it is already
# applied (reverse-apply succeeds) the step is a no-op so rebuilds do not fail.
# If the working tree drifted so the patch no longer applies, configure/build
# fails loudly instead of silently compiling different third-party code.
# In particular, do not relax whitespace matching here: that can turn a real
# source drift into an apparently successful but different third-party build.
if(NOT DEFINED PATCH OR NOT DEFINED PATCH_DIR)
message(FATAL_ERROR "patch_third_party.cmake requires -DPATCH=... and -DPATCH_DIR=...")
endif()
get_filename_component(PATCH_ABS "${PATCH}" ABSOLUTE)
get_filename_component(PATCH_DIR_ABS "${PATCH_DIR}" ABSOLUTE)
if(NOT EXISTS "${PATCH_ABS}")
message(FATAL_ERROR "Third-party patch file not found: ${PATCH_ABS}")
endif()
if(NOT IS_DIRECTORY "${PATCH_DIR_ABS}")
message(FATAL_ERROR "Third-party patch target dir not found: ${PATCH_DIR_ABS}")
endif()
find_package(Git QUIET)
if(NOT GIT_EXECUTABLE)
message(FATAL_ERROR "git is required to apply third-party patches")
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" -C "${PATCH_DIR_ABS}" rev-parse --show-toplevel
RESULT_VARIABLE _eve_patch_repo_result
OUTPUT_VARIABLE _eve_patch_repo_root
ERROR_VARIABLE _eve_patch_repo_error
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _eve_patch_repo_result EQUAL 0)
message(FATAL_ERROR
"Third-party patch target is not a git checkout: ${PATCH_DIR_ABS}\n"
"${_eve_patch_repo_error}")
endif()
# Already applied? `git apply --reverse --check` succeeds only then.
execute_process(
COMMAND "${GIT_EXECUTABLE}" apply --reverse --check --unidiff-zero "${PATCH_ABS}"
WORKING_DIRECTORY "${PATCH_DIR_ABS}"
RESULT_VARIABLE _eve_patch_reverse
OUTPUT_VARIABLE _eve_patch_reverse_out
ERROR_VARIABLE _eve_patch_reverse_err)
if(_eve_patch_reverse EQUAL 0)
message(STATUS "Third-party patch already applied: ${PATCH_ABS}")
return()
endif()
# Apply cleanly, or fail loudly so a stale patch cannot silently bit-rot.
execute_process(
COMMAND "${GIT_EXECUTABLE}" apply --check --unidiff-zero "${PATCH_ABS}"
WORKING_DIRECTORY "${PATCH_DIR_ABS}"
RESULT_VARIABLE _eve_patch_check
OUTPUT_VARIABLE _eve_patch_check_out
ERROR_VARIABLE _eve_patch_check_err)
if(NOT _eve_patch_check EQUAL 0)
message(FATAL_ERROR
"Third-party patch no longer applies cleanly: ${PATCH_ABS}\n"
"The vendored source changed upstream or the checkout is polluted; update or drop the patch.\n"
"git apply --check output:\n${_eve_patch_check_out}${_eve_patch_check_err}")
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" apply --unidiff-zero "${PATCH_ABS}"
WORKING_DIRECTORY "${PATCH_DIR_ABS}"
RESULT_VARIABLE _eve_patch_result
OUTPUT_VARIABLE _eve_patch_out
ERROR_VARIABLE _eve_patch_err)
if(NOT _eve_patch_result EQUAL 0)
message(FATAL_ERROR
"git apply failed for ${PATCH_ABS} (exit ${_eve_patch_result})\n"
"${_eve_patch_out}${_eve_patch_err}")
endif()
message(STATUS "Applied third-party patch: ${PATCH_ABS}")