summaryrefslogtreecommitdiffstats
path: root/Modules/ExternalProject
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/ExternalProject')
-rw-r--r--Modules/ExternalProject/RepositoryInfo.txt.in1
-rw-r--r--Modules/ExternalProject/cfgcmd.txt.in1
-rw-r--r--Modules/ExternalProject/copydir.cmake.in10
-rw-r--r--Modules/ExternalProject/download.cmake.in187
-rw-r--r--Modules/ExternalProject/extractfile.cmake.in63
-rw-r--r--Modules/ExternalProject/gitclone.cmake.in67
-rw-r--r--Modules/ExternalProject/gitupdate.cmake.in277
-rw-r--r--Modules/ExternalProject/hgclone.cmake.in45
-rw-r--r--Modules/ExternalProject/hgupdate.cmake.in16
-rw-r--r--Modules/ExternalProject/mkdirs.cmake.in19
-rw-r--r--Modules/ExternalProject/verify.cmake.in48
11 files changed, 734 insertions, 0 deletions
diff --git a/Modules/ExternalProject/RepositoryInfo.txt.in b/Modules/ExternalProject/RepositoryInfo.txt.in
new file mode 100644
index 0000000..d82f04c
--- /dev/null
+++ b/Modules/ExternalProject/RepositoryInfo.txt.in
@@ -0,0 +1 @@
+@repo_info_content@
diff --git a/Modules/ExternalProject/cfgcmd.txt.in b/Modules/ExternalProject/cfgcmd.txt.in
new file mode 100644
index 0000000..b3f09ef
--- /dev/null
+++ b/Modules/ExternalProject/cfgcmd.txt.in
@@ -0,0 +1 @@
+cmd='@cmd@'
diff --git a/Modules/ExternalProject/copydir.cmake.in b/Modules/ExternalProject/copydir.cmake.in
new file mode 100644
index 0000000..5dd3891
--- /dev/null
+++ b/Modules/ExternalProject/copydir.cmake.in
@@ -0,0 +1,10 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.5)
+
+file(REMOVE_RECURSE "@to_dir@")
+
+# Copy the _contents_ of the source dir into the destination dir, hence the
+# trailing slash on the from_dir
+file(COPY "@from_dir@/" DESTINATION "@to_dir@")
diff --git a/Modules/ExternalProject/download.cmake.in b/Modules/ExternalProject/download.cmake.in
new file mode 100644
index 0000000..6ef4eb1
--- /dev/null
+++ b/Modules/ExternalProject/download.cmake.in
@@ -0,0 +1,187 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.5)
+
+function(check_file_hash has_hash hash_is_good)
+ if("${has_hash}" STREQUAL "")
+ message(FATAL_ERROR "has_hash Can't be empty")
+ endif()
+
+ if("${hash_is_good}" STREQUAL "")
+ message(FATAL_ERROR "hash_is_good Can't be empty")
+ endif()
+
+ if("@ALGO@" STREQUAL "")
+ # No check
+ set("${has_hash}" FALSE PARENT_SCOPE)
+ set("${hash_is_good}" FALSE PARENT_SCOPE)
+ return()
+ endif()
+
+ set("${has_hash}" TRUE PARENT_SCOPE)
+
+ message(STATUS "verifying file...
+ file='@LOCAL@'")
+
+ file("@ALGO@" "@LOCAL@" actual_value)
+
+ if(NOT "${actual_value}" STREQUAL "@EXPECT_VALUE@")
+ set("${hash_is_good}" FALSE PARENT_SCOPE)
+ message(STATUS "@ALGO@ hash of
+ @LOCAL@
+ does not match expected value
+ expected: '@EXPECT_VALUE@'
+ actual: '${actual_value}'")
+ else()
+ set("${hash_is_good}" TRUE PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(sleep_before_download attempt)
+ if(attempt EQUAL 0)
+ return()
+ endif()
+
+ if(attempt EQUAL 1)
+ message(STATUS "Retrying...")
+ return()
+ endif()
+
+ set(sleep_seconds 0)
+
+ if(attempt EQUAL 2)
+ set(sleep_seconds 5)
+ elseif(attempt EQUAL 3)
+ set(sleep_seconds 5)
+ elseif(attempt EQUAL 4)
+ set(sleep_seconds 15)
+ elseif(attempt EQUAL 5)
+ set(sleep_seconds 60)
+ elseif(attempt EQUAL 6)
+ set(sleep_seconds 90)
+ elseif(attempt EQUAL 7)
+ set(sleep_seconds 300)
+ else()
+ set(sleep_seconds 1200)
+ endif()
+
+ message(STATUS "Retry after ${sleep_seconds} seconds (attempt #${attempt}) ...")
+
+ execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep "${sleep_seconds}")
+endfunction()
+
+if("@LOCAL@" STREQUAL "")
+ message(FATAL_ERROR "LOCAL can't be empty")
+endif()
+
+if("@REMOTE@" STREQUAL "")
+ message(FATAL_ERROR "REMOTE can't be empty")
+endif()
+
+function(download_and_verify)
+ if(EXISTS "@LOCAL@")
+ check_file_hash(has_hash hash_is_good)
+ if(has_hash)
+ if(hash_is_good)
+ message(STATUS
+"File already exists and hash match (skip download):
+ file='@LOCAL@'
+ @ALGO@='@EXPECT_VALUE@'"
+ )
+ return()
+ else()
+ message(STATUS "File already exists but hash mismatch. Removing...")
+ file(REMOVE "@LOCAL@")
+ endif()
+ else()
+ message(STATUS
+"File already exists but no hash specified (use URL_HASH):
+ file='@LOCAL@'
+Old file will be removed and new file downloaded from URL."
+ )
+ file(REMOVE "@LOCAL@")
+ endif()
+ endif()
+
+ set(retry_number 5)
+
+ message(STATUS "Downloading...
+ dst='@LOCAL@'
+ timeout='@TIMEOUT_MSG@'
+ inactivity timeout='@INACTIVITY_TIMEOUT_MSG@'"
+ )
+ set(download_retry_codes 7 6 8 15)
+ set(skip_url_list)
+ set(status_code)
+ foreach(i RANGE ${retry_number})
+ if(status_code IN_LIST download_retry_codes)
+ sleep_before_download(${i})
+ endif()
+ foreach(url @REMOTE@)
+ if(NOT url IN_LIST skip_url_list)
+ message(STATUS "Using src='${url}'")
+
+ @TLS_VERIFY_CODE@
+ @TLS_CAINFO_CODE@
+ @NETRC_CODE@
+ @NETRC_FILE_CODE@
+
+ file(
+ DOWNLOAD
+ "${url}" "@LOCAL@"
+ @SHOW_PROGRESS@
+ @TIMEOUT_ARGS@
+ @INACTIVITY_TIMEOUT_ARGS@
+ STATUS status
+ LOG log
+ @USERPWD_ARGS@
+ @HTTP_HEADERS_ARGS@
+ )
+
+ list(GET status 0 status_code)
+ list(GET status 1 status_string)
+
+ if(status_code EQUAL 0)
+ check_file_hash(has_hash hash_is_good)
+ if(has_hash AND NOT hash_is_good)
+ message(STATUS "Hash mismatch, removing...")
+ file(REMOVE "@LOCAL@")
+ else()
+ message(STATUS "Downloading... done")
+ return()
+ endif()
+ else()
+ string(APPEND logFailedURLs
+"error: downloading '${url}' failed
+ status_code: ${status_code}
+ status_string: ${status_string}
+ log:
+ --- LOG BEGIN ---
+ ${log}
+ --- LOG END ---
+ "
+ )
+ if(NOT status_code IN_LIST download_retry_codes)
+ list(APPEND skip_url_list "${url}")
+ break()
+ endif()
+ endif()
+ endif()
+ endforeach()
+ endforeach()
+
+ message(FATAL_ERROR
+"Each download failed!
+ ${logFailedURLs}
+ "
+ )
+
+endfunction()
+
+download_and_verify()
+
+set(extract_script @extract_script_filename@)
+if(NOT "${extract_script}" STREQUAL "")
+ include(${extract_script})
+endif()
diff --git a/Modules/ExternalProject/extractfile.cmake.in b/Modules/ExternalProject/extractfile.cmake.in
new file mode 100644
index 0000000..d9e07f1
--- /dev/null
+++ b/Modules/ExternalProject/extractfile.cmake.in
@@ -0,0 +1,63 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.5)
+
+# Make file names absolute:
+#
+get_filename_component(filename "@filename@" ABSOLUTE)
+get_filename_component(directory "@directory@" ABSOLUTE)
+
+message(STATUS "extracting...
+ src='${filename}'
+ dst='${directory}'")
+
+if(NOT EXISTS "${filename}")
+ message(FATAL_ERROR "File to extract does not exist: '${filename}'")
+endif()
+
+# Prepare a space for extracting:
+#
+set(i 1234)
+while(EXISTS "${directory}/../ex-@name@${i}")
+ math(EXPR i "${i} + 1")
+endwhile()
+set(ut_dir "${directory}/../ex-@name@${i}")
+file(MAKE_DIRECTORY "${ut_dir}")
+
+# Extract it:
+#
+message(STATUS "extracting... [tar @args@]")
+execute_process(COMMAND ${CMAKE_COMMAND} -E tar @args@ ${filename}
+ WORKING_DIRECTORY ${ut_dir}
+ RESULT_VARIABLE rv)
+
+if(NOT rv EQUAL 0)
+ message(STATUS "extracting... [error clean up]")
+ file(REMOVE_RECURSE "${ut_dir}")
+ message(FATAL_ERROR "Extract of '${filename}' failed")
+endif()
+
+# Analyze what came out of the tar file:
+#
+message(STATUS "extracting... [analysis]")
+file(GLOB contents "${ut_dir}/*")
+list(REMOVE_ITEM contents "${ut_dir}/.DS_Store")
+list(LENGTH contents n)
+if(NOT n EQUAL 1 OR NOT IS_DIRECTORY "${contents}")
+ set(contents "${ut_dir}")
+endif()
+
+# Move "the one" directory to the final directory:
+#
+message(STATUS "extracting... [rename]")
+file(REMOVE_RECURSE ${directory})
+get_filename_component(contents ${contents} ABSOLUTE)
+file(RENAME ${contents} ${directory})
+
+# Clean up:
+#
+message(STATUS "extracting... [clean up]")
+file(REMOVE_RECURSE "${ut_dir}")
+
+message(STATUS "extracting... done")
diff --git a/Modules/ExternalProject/gitclone.cmake.in b/Modules/ExternalProject/gitclone.cmake.in
new file mode 100644
index 0000000..5e5c415
--- /dev/null
+++ b/Modules/ExternalProject/gitclone.cmake.in
@@ -0,0 +1,67 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.5)
+
+if(NOT "@gitclone_infofile@" IS_NEWER_THAN "@gitclone_stampfile@")
+ message(STATUS "Avoiding repeated git clone, stamp file is up to date: '@gitclone_stampfile@'")
+ return()
+endif()
+
+execute_process(
+ COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@"
+ RESULT_VARIABLE error_code
+ )
+if(error_code)
+ message(FATAL_ERROR "Failed to remove directory: '@source_dir@'")
+endif()
+
+# try the clone 3 times in case there is an odd git clone issue
+set(error_code 1)
+set(number_of_tries 0)
+while(error_code AND number_of_tries LESS 3)
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" @git_options@ clone @git_clone_options@ "@git_repository@" "@src_name@"
+ WORKING_DIRECTORY "@work_dir@"
+ RESULT_VARIABLE error_code
+ )
+ math(EXPR number_of_tries "${number_of_tries} + 1")
+endwhile()
+if(number_of_tries GREATER 1)
+ message(STATUS "Had to git clone more than once:
+ ${number_of_tries} times.")
+endif()
+if(error_code)
+ message(FATAL_ERROR "Failed to clone repository: '@git_repository@'")
+endif()
+
+execute_process(
+ COMMAND "@git_EXECUTABLE@" @git_options@ checkout "@git_tag@" @git_checkout_explicit--@
+ WORKING_DIRECTORY "@work_dir@/@src_name@"
+ RESULT_VARIABLE error_code
+ )
+if(error_code)
+ message(FATAL_ERROR "Failed to checkout tag: '@git_tag@'")
+endif()
+
+set(init_submodules @init_submodules@)
+if(init_submodules)
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" @git_options@ submodule update @git_submodules_recurse@ --init @git_submodules@
+ WORKING_DIRECTORY "@work_dir@/@src_name@"
+ RESULT_VARIABLE error_code
+ )
+endif()
+if(error_code)
+ message(FATAL_ERROR "Failed to update submodules in: '@work_dir@/@src_name@'")
+endif()
+
+# Complete success, update the script-last-run stamp file:
+#
+execute_process(
+ COMMAND ${CMAKE_COMMAND} -E copy "@gitclone_infofile@" "@gitclone_stampfile@"
+ RESULT_VARIABLE error_code
+ )
+if(error_code)
+ message(FATAL_ERROR "Failed to copy script-last-run stamp file: '@gitclone_stampfile@'")
+endif()
diff --git a/Modules/ExternalProject/gitupdate.cmake.in b/Modules/ExternalProject/gitupdate.cmake.in
new file mode 100644
index 0000000..7033918
--- /dev/null
+++ b/Modules/ExternalProject/gitupdate.cmake.in
@@ -0,0 +1,277 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.5)
+
+function(get_hash_for_ref ref out_var err_var)
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" rev-parse "${ref}"
+ WORKING_DIRECTORY "@work_dir@"
+ RESULT_VARIABLE error_code
+ OUTPUT_VARIABLE ref_hash
+ ERROR_VARIABLE error_msg
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ if(error_code)
+ set(${out_var} "" PARENT_SCOPE)
+ else()
+ set(${out_var} "${ref_hash}" PARENT_SCOPE)
+ endif()
+ set(${err_var} "${error_msg}" PARENT_SCOPE)
+endfunction()
+
+get_hash_for_ref(HEAD head_sha error_msg)
+if(head_sha STREQUAL "")
+ message(FATAL_ERROR "Failed to get the hash for HEAD:\n${error_msg}")
+endif()
+
+
+execute_process(
+ COMMAND "@git_EXECUTABLE@" show-ref "@git_tag@"
+ WORKING_DIRECTORY "@work_dir@"
+ OUTPUT_VARIABLE show_ref_output
+)
+if(show_ref_output MATCHES "^[a-z0-9]+[ \\t]+refs/remotes/")
+ # Given a full remote/branch-name and we know about it already. Since
+ # branches can move around, we always have to fetch.
+ set(fetch_required YES)
+ set(checkout_name "@git_tag@")
+
+elseif(show_ref_output MATCHES "^[a-z0-9]+[ \\t]+refs/tags/")
+ # Given a tag name that we already know about. We don't know if the tag we
+ # have matches the remote though (tags can move), so we should fetch.
+ set(fetch_required YES)
+ set(checkout_name "@git_tag@")
+
+ # Special case to preserve backward compatibility: if we are already at the
+ # same commit as the tag we hold locally, don't do a fetch and assume the tag
+ # hasn't moved on the remote.
+ # FIXME: We should provide an option to always fetch for this case
+ get_hash_for_ref("@git_tag@" tag_sha error_msg)
+ if(tag_sha STREQUAL head_sha)
+ message(VERBOSE "Already at requested tag: ${tag_sha}")
+ return()
+ endif()
+
+elseif(show_ref_output MATCHES "^[a-z0-9]+[ \\t]+refs/heads/")
+ # Given a branch name without any remote and we already have a branch by that
+ # name. We might already have that branch checked out or it might be a
+ # different branch. It isn't safe to use a bare branch name without the
+ # remote, so do a fetch and replace the ref with one that includes the remote.
+ set(fetch_required YES)
+ set(checkout_name "@git_remote_name@/@git_tag@")
+
+else()
+ get_hash_for_ref("@git_tag@" tag_sha error_msg)
+ if(tag_sha STREQUAL head_sha)
+ # Have the right commit checked out already
+ message(VERBOSE "Already at requested ref: ${tag_sha}")
+ return()
+
+ elseif(tag_sha STREQUAL "")
+ # We don't know about this ref yet, so we have no choice but to fetch.
+ # We deliberately swallow any error message at the default log level
+ # because it can be confusing for users to see a failed git command.
+ # That failure is being handled here, so it isn't an error.
+ set(fetch_required YES)
+ set(checkout_name "@git_tag@")
+ if(NOT error_msg STREQUAL "")
+ message(VERBOSE "${error_msg}")
+ endif()
+
+ else()
+ # We have the commit, so we know we were asked to find a commit hash
+ # (otherwise it would have been handled further above), but we don't
+ # have that commit checked out yet
+ set(fetch_required NO)
+ set(checkout_name "@git_tag@")
+ if(NOT error_msg STREQUAL "")
+ message(WARNING "${error_msg}")
+ endif()
+
+ endif()
+endif()
+
+if(fetch_required)
+ message(VERBOSE "Fetching latest from the remote @git_remote_name@")
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" fetch --tags --force "@git_remote_name@"
+ WORKING_DIRECTORY "@work_dir@"
+ COMMAND_ERROR_IS_FATAL ANY
+ )
+endif()
+
+set(git_update_strategy "@git_update_strategy@")
+if(git_update_strategy STREQUAL "")
+ # Backward compatibility requires REBASE as the default behavior
+ set(git_update_strategy REBASE)
+endif()
+
+if(git_update_strategy MATCHES "^REBASE(_CHECKOUT)?$")
+ # Asked to potentially try to rebase first, maybe with fallback to checkout.
+ # We can't if we aren't already on a branch and we shouldn't if that local
+ # branch isn't tracking the one we want to checkout.
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" symbolic-ref -q HEAD
+ WORKING_DIRECTORY "@work_dir@"
+ OUTPUT_VARIABLE current_branch
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ # Don't test for an error. If this isn't a branch, we get a non-zero error
+ # code but empty output.
+ )
+
+ if(current_branch STREQUAL "")
+ # Not on a branch, checkout is the only sensible option since any rebase
+ # would always fail (and backward compatibility requires us to checkout in
+ # this situation)
+ set(git_update_strategy CHECKOUT)
+
+ else()
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" for-each-ref "--format='%(upstream:short)'" "${current_branch}"
+ WORKING_DIRECTORY "@work_dir@"
+ OUTPUT_VARIABLE upstream_branch
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ COMMAND_ERROR_IS_FATAL ANY # There is no error if no upstream is set
+ )
+ if(NOT upstream_branch STREQUAL checkout_name)
+ # Not safe to rebase when asked to checkout a different branch to the one
+ # we are tracking. If we did rebase, we could end up with arbitrary
+ # commits added to the ref we were asked to checkout if the current local
+ # branch happens to be able to rebase onto the target branch. There would
+ # be no error message and the user wouldn't know this was occurring.
+ set(git_update_strategy CHECKOUT)
+ endif()
+
+ endif()
+elseif(NOT git_update_strategy STREQUAL "CHECKOUT")
+ message(FATAL_ERROR "Unsupported git update strategy: ${git_update_strategy}")
+endif()
+
+
+# Check if stash is needed
+execute_process(
+ COMMAND "@git_EXECUTABLE@" status --porcelain
+ WORKING_DIRECTORY "@work_dir@"
+ RESULT_VARIABLE error_code
+ OUTPUT_VARIABLE repo_status
+)
+if(error_code)
+ message(FATAL_ERROR "Failed to get the status")
+endif()
+string(LENGTH "${repo_status}" need_stash)
+
+# If not in clean state, stash changes in order to be able to perform a
+# rebase or checkout without losing those changes permanently
+if(need_stash)
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" stash save @git_stash_save_options@
+ WORKING_DIRECTORY "@work_dir@"
+ COMMAND_ERROR_IS_FATAL ANY
+ )
+endif()
+
+if(git_update_strategy STREQUAL "CHECKOUT")
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" checkout "${checkout_name}"
+ WORKING_DIRECTORY "@work_dir@"
+ COMMAND_ERROR_IS_FATAL ANY
+ )
+else()
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" rebase "${checkout_name}"
+ WORKING_DIRECTORY "@work_dir@"
+ RESULT_VARIABLE error_code
+ OUTPUT_VARIABLE rebase_output
+ ERROR_VARIABLE rebase_output
+ )
+ if(error_code)
+ # Rebase failed, undo the rebase attempt before continuing
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" rebase --abort
+ WORKING_DIRECTORY "@work_dir@"
+ )
+
+ if(NOT git_update_strategy STREQUAL "REBASE_CHECKOUT")
+ # Not allowed to do a checkout as a fallback, so cannot proceed
+ if(need_stash)
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" stash pop --index --quiet
+ WORKING_DIRECTORY "@work_dir@"
+ )
+ endif()
+ message(FATAL_ERROR "\nFailed to rebase in: '@work_dir@'."
+ "\nOutput from the attempted rebase follows:"
+ "\n${rebase_output}"
+ "\n\nYou will have to resolve the conflicts manually")
+ endif()
+
+ # Fall back to checkout. We create an annotated tag so that the user
+ # can manually inspect the situation and revert if required.
+ # We can't log the failed rebase output because MSVC sees it and
+ # intervenes, causing the build to fail even though it completes.
+ # Write it to a file instead.
+ string(TIMESTAMP tag_timestamp "%Y%m%dT%H%M%S" UTC)
+ set(tag_name _cmake_ExternalProject_moved_from_here_${tag_timestamp}Z)
+ set(error_log_file ${CMAKE_CURRENT_LIST_DIR}/rebase_error_${tag_timestamp}Z.log)
+ file(WRITE ${error_log_file} "${rebase_output}")
+ message(WARNING "Rebase failed, output has been saved to ${error_log_file}"
+ "\nFalling back to checkout, previous commit tagged as ${tag_name}")
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" tag -a
+ -m "ExternalProject attempting to move from here to ${checkout_name}"
+ ${tag_name}
+ WORKING_DIRECTORY "@work_dir@"
+ COMMAND_ERROR_IS_FATAL ANY
+ )
+
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" checkout "${checkout_name}"
+ WORKING_DIRECTORY "@work_dir@"
+ COMMAND_ERROR_IS_FATAL ANY
+ )
+ endif()
+endif()
+
+if(need_stash)
+ # Put back the stashed changes
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" stash pop --index --quiet
+ WORKING_DIRECTORY "@work_dir@"
+ RESULT_VARIABLE error_code
+ )
+ if(error_code)
+ # Stash pop --index failed: Try again dropping the index
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" reset --hard --quiet
+ WORKING_DIRECTORY "@work_dir@"
+ )
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" stash pop --quiet
+ WORKING_DIRECTORY "@work_dir@"
+ RESULT_VARIABLE error_code
+ )
+ if(error_code)
+ # Stash pop failed: Restore previous state.
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" reset --hard --quiet ${head_sha}
+ WORKING_DIRECTORY "@work_dir@"
+ )
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" stash pop --index --quiet
+ WORKING_DIRECTORY "@work_dir@"
+ )
+ message(FATAL_ERROR "\nFailed to unstash changes in: '@work_dir@'."
+ "\nYou will have to resolve the conflicts manually")
+ endif()
+ endif()
+endif()
+
+set(init_submodules "@init_submodules@")
+if(init_submodules)
+ execute_process(
+ COMMAND "@git_EXECUTABLE@" submodule update @git_submodules_recurse@ --init @git_submodules@
+ WORKING_DIRECTORY "@work_dir@"
+ COMMAND_ERROR_IS_FATAL ANY
+ )
+endif()
diff --git a/Modules/ExternalProject/hgclone.cmake.in b/Modules/ExternalProject/hgclone.cmake.in
new file mode 100644
index 0000000..09395cc
--- /dev/null
+++ b/Modules/ExternalProject/hgclone.cmake.in
@@ -0,0 +1,45 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.5)
+
+if(NOT "@hgclone_infofile@" IS_NEWER_THAN "@hgclone_stampfile@")
+ message(STATUS "Avoiding repeated hg clone, stamp file is up to date: '@hgclone_stampfile@'")
+ return()
+endif()
+
+execute_process(
+ COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@"
+ RESULT_VARIABLE error_code
+ )
+if(error_code)
+ message(FATAL_ERROR "Failed to remove directory: '@source_dir@'")
+endif()
+
+execute_process(
+ COMMAND "@hg_EXECUTABLE@" clone -U "@hg_repository@" "@src_name@"
+ WORKING_DIRECTORY "@work_dir@"
+ RESULT_VARIABLE error_code
+ )
+if(error_code)
+ message(FATAL_ERROR "Failed to clone repository: '@hg_repository@'")
+endif()
+
+execute_process(
+ COMMAND "@hg_EXECUTABLE@" update @hg_tag@
+ WORKING_DIRECTORY "@work_dir@/@src_name@"
+ RESULT_VARIABLE error_code
+ )
+if(error_code)
+ message(FATAL_ERROR "Failed to checkout tag: '@hg_tag@'")
+endif()
+
+# Complete success, update the script-last-run stamp file:
+#
+execute_process(
+ COMMAND ${CMAKE_COMMAND} -E copy "@hgclone_infofile@" "@hgclone_stampfile@"
+ RESULT_VARIABLE error_code
+ )
+if(error_code)
+ message(FATAL_ERROR "Failed to copy script-last-run stamp file: '@hgclone_stampfile@'")
+endif()
diff --git a/Modules/ExternalProject/hgupdate.cmake.in b/Modules/ExternalProject/hgupdate.cmake.in
new file mode 100644
index 0000000..f88e1ee
--- /dev/null
+++ b/Modules/ExternalProject/hgupdate.cmake.in
@@ -0,0 +1,16 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.19)
+
+execute_process(
+ COMMAND "@hg_EXECUTABLE@" pull
+ COMMAND_ERROR_IS_FATAL ANY
+ WORKING_DIRECTORY "@work_dir@"
+)
+
+execute_process(
+ COMMAND "@hg_EXECUTABLE@" update @hg_tag@
+ COMMAND_ERROR_IS_FATAL ANY
+ WORKING_DIRECTORY "@work_dir@"
+)
diff --git a/Modules/ExternalProject/mkdirs.cmake.in b/Modules/ExternalProject/mkdirs.cmake.in
new file mode 100644
index 0000000..73e80fa
--- /dev/null
+++ b/Modules/ExternalProject/mkdirs.cmake.in
@@ -0,0 +1,19 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.5)
+
+file(MAKE_DIRECTORY
+ "@source_dir@"
+ "@binary_dir@"
+ "@install_dir@"
+ "@tmp_dir@"
+ "@stamp_dir@"
+ "@download_dir@"
+ "@log_dir@"
+)
+
+set(configSubDirs @CMAKE_CONFIGURATION_TYPES@)
+foreach(subDir IN LISTS configSubDirs)
+ file(MAKE_DIRECTORY "@stamp_dir@/${subDir}")
+endforeach()
diff --git a/Modules/ExternalProject/verify.cmake.in b/Modules/ExternalProject/verify.cmake.in
new file mode 100644
index 0000000..f37059b
--- /dev/null
+++ b/Modules/ExternalProject/verify.cmake.in
@@ -0,0 +1,48 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.5)
+
+if("@LOCAL@" STREQUAL "")
+ message(FATAL_ERROR "LOCAL can't be empty")
+endif()
+
+if(NOT EXISTS "@LOCAL@")
+ message(FATAL_ERROR "File not found: @LOCAL@")
+endif()
+
+function(do_verify)
+ if("@ALGO@" STREQUAL "")
+ message(WARNING "File will not be verified since no URL_HASH specified")
+ return()
+ endif()
+
+ if("@EXPECT_VALUE@" STREQUAL "")
+ message(FATAL_ERROR "EXPECT_VALUE can't be empty")
+ endif()
+
+ message(STATUS
+"verifying file...
+ file='@LOCAL@'")
+
+ file("@ALGO@" "@LOCAL@" actual_value)
+
+ if(NOT "${actual_value}" STREQUAL "@EXPECT_VALUE@")
+ message(FATAL_ERROR
+"error: @ALGO@ hash of
+ @LOCAL@
+does not match expected value
+ expected: '@EXPECT_VALUE@'
+ actual: '${actual_value}'
+")
+ endif()
+
+ message(STATUS "verifying file... done")
+endfunction()
+
+do_verify()
+
+set(extract_script "@extract_script_filename@")
+if(NOT "${extract_script}" STREQUAL "")
+ include("${extract_script}")
+endif()