@@ -112,6 +112,62 @@ macro(cpm_set_policies)
112112endmacro ()
113113cpm_set_policies()
114114
115+ macro (cpm_generate_apply_patches_script)
116+ set (_cpm_patch_script "${CPM_CURRENT_DIRECTORY} /cpm_apply_patches.cmake" )
117+
118+ file (
119+ WRITE "${_cpm_patch_script} "
120+ [=[
121+ # Auto-generated patch application script
122+ separate_arguments(PATCH_FILES)
123+
124+ foreach(patch_file IN LISTS PATCH_FILES)
125+ message(STATUS "Checking patch: ${patch_file}")
126+
127+ execute_process(
128+ COMMAND "${PATCH_EXECUTABLE}" --dry-run -p1
129+ INPUT_FILE "${patch_file}"
130+ RESULT_VARIABLE dry_run_result
131+ OUTPUT_VARIABLE dry_out
132+ ERROR_VARIABLE dry_err
133+ )
134+
135+ if(dry_run_result EQUAL 0)
136+ message(STATUS "Applying patch: ${patch_file}")
137+ execute_process(
138+ COMMAND "${PATCH_EXECUTABLE}" -p1
139+ INPUT_FILE "${patch_file}"
140+ RESULT_VARIABLE apply_result
141+ OUTPUT_VARIABLE apply_out
142+ ERROR_VARIABLE apply_err
143+ )
144+ if(apply_result EQUAL 0)
145+ message(STATUS "Applied patch: ${patch_file}")
146+ else()
147+ message(FATAL_ERROR "Patch failed: ${patch_file}\n${apply_err}")
148+ endif()
149+ else()
150+ execute_process(
151+ COMMAND "${PATCH_EXECUTABLE}" --dry-run -p1 --reverse
152+ INPUT_FILE "${patch_file}"
153+ RESULT_VARIABLE reverse_result
154+ OUTPUT_VARIABLE reverse_out
155+ ERROR_VARIABLE reverse_err
156+ )
157+ if(reverse_result EQUAL 0)
158+ message(STATUS "Patch already applied: ${patch_file}")
159+ else()
160+ message(
161+ FATAL_ERROR "Patch cannot be applied and is not already applied: ${patch_file}\n${dry_err}"
162+ )
163+ endif()
164+ endif()
165+ endforeach()
166+ ]=]
167+ )
168+ endmacro ()
169+ cpm_generate_apply_patches_script()
170+
115171option (CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies"
116172 $ENV{CPM_USE_LOCAL_PACKAGES}
117173)
@@ -541,66 +597,69 @@ endfunction()
541597# then generates a `PATCH_COMMAND` appropriate for `ExternalProject_Add()`. This command is appended
542598# to the parent scope's `CPM_ARGS_UNPARSED_ARGUMENTS`.
543599function (cpm_add_patches)
544- # Return if no patch files are supplied.
600+ # Return early if no patch files are provided
545601 if (NOT ARGN)
546602 return ()
547603 endif ()
548604
549- # Find the patch program.
605+ # -----------------------------------------------------------------------------------------------
606+ # Locate the 'patch' executable
607+ # -----------------------------------------------------------------------------------------------
550608 find_program (PATCH_EXECUTABLE patch)
609+
551610 if (CMAKE_HOST_WIN32 AND NOT PATCH_EXECUTABLE)
552611 # The Windows git executable is distributed with patch.exe. Find the path to the executable, if
553612 # it exists, then search `../usr/bin` and `../../usr/bin` for patch.exe.
554613 find_package (Git QUIET )
555614 if (GIT_EXECUTABLE)
556- get_filename_component (extra_search_path ${GIT_EXECUTABLE} DIRECTORY )
557- get_filename_component (extra_search_path_1up ${extra_search_path} DIRECTORY )
558- get_filename_component (extra_search_path_2up ${extra_search_path_1up} DIRECTORY )
615+ get_filename_component (_git_bin_dir "${GIT_EXECUTABLE} " DIRECTORY )
616+ get_filename_component (_git_root_1up "${_git_bin_dir} " DIRECTORY )
617+ get_filename_component (_git_root_2up "${_git_root_1up} " DIRECTORY )
618+
559619 find_program (
560- PATCH_EXECUTABLE patch HINTS "${extra_search_path_1up} /usr/bin"
561- "${extra_search_path_2up} /usr/bin"
620+ PATCH_EXECUTABLE patch HINTS "${_git_root_1up} /usr/bin" "${_git_root_2up} /usr/bin"
562621 )
563622 endif ()
564623 endif ()
624+
565625 if (NOT PATCH_EXECUTABLE)
566626 message (FATAL_ERROR "Couldn't find `patch` executable to use with PATCHES keyword." )
567627 endif ()
568628
569- # Create a temporary
570- set (temp_list ${CPM_ARGS_UNPARSED_ARGUMENTS} )
629+ # -----------------------------------------------------------------------------------------------
630+ # Resolve and validate all patch file paths
631+ # -----------------------------------------------------------------------------------------------
632+ set (resolved_patch_files)
571633
572- # Ensure each file exists (or error out) and add it to the list.
573- set (first_item True )
574- foreach (PATCH_FILE ${ARGN} )
634+ foreach (PATCH_FILE IN LISTS ARGN)
575635 # Make sure the patch file exists, if we can't find it, try again in the current directory.
576636 if (NOT EXISTS "${PATCH_FILE} " )
577- if (NOT EXISTS "${CMAKE_CURRENT_LIST_DIR} /${PATCH_FILE} " )
637+ set (_fallback_path "${CMAKE_CURRENT_LIST_DIR} /${PATCH_FILE} " )
638+ if (NOT EXISTS "${_fallback_path} " )
578639 message (FATAL_ERROR "Couldn't find patch file: '${PATCH_FILE} '" )
579640 endif ()
580- set (PATCH_FILE "${CMAKE_CURRENT_LIST_DIR} / ${PATCH_FILE } " )
641+ set (PATCH_FILE "${_fallback_path } " )
581642 endif ()
582643
583644 # Convert to absolute path for use with patch file command.
584645 get_filename_component (PATCH_FILE "${PATCH_FILE} " ABSOLUTE )
585-
586- # The first patch entry must be preceded by "PATCH_COMMAND" while the following items are
587- # preceded by "&&".
588- if (first_item)
589- set (first_item False )
590- list (APPEND temp_list "PATCH_COMMAND" )
591- else ()
592- list (APPEND temp_list "&&" )
593- endif ()
594- # Add the patch command to the list
595- list (APPEND temp_list "${PATCH_EXECUTABLE} " "-p1" "<" "${PATCH_FILE} " )
646+ list (APPEND resolved_patch_files "${PATCH_FILE} " )
596647 endforeach ()
597648
598- # Move temp out into parent scope.
649+ # -----------------------------------------------------------------------------------------------
650+ # Construct the patch command
651+ # -----------------------------------------------------------------------------------------------
652+ string (JOIN " " joined_patch_files ${resolved_patch_files} )
653+
654+ set (_patch_command cmake -D "PATCH_FILES=${joined_patch_files} " -D
655+ "PATCH_EXECUTABLE=${PATCH_EXECUTABLE} " -P "${_cpm_patch_script} "
656+ )
657+
658+ list (APPEND CPM_ARGS_UNPARSED_ARGUMENTS PATCH_COMMAND ${_patch_command} )
599659 set (CPM_ARGS_UNPARSED_ARGUMENTS
600- ${temp_list}
660+ " ${CPM_ARGS_UNPARSED_ARGUMENTS} "
601661 PARENT_SCOPE
602662 )
603-
604663endfunction ()
605664
606665# method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload
0 commit comments