From 036517fe0b50f13574ff1e28ac946d36d46307d9 Mon Sep 17 00:00:00 2001 From: Craig Scott Date: Sat, 22 Jan 2022 16:19:07 +1100 Subject: ExternalProject: Move existing step scripts to separate subdirectory This is in preparation for moving more inline content to separate scripts as well. Having them in a separate subdirectory is consistent with other modules and will make them easier to find and work with. --- Modules/ExternalProject-download.cmake.in | 173 ---------------- Modules/ExternalProject-gitupdate.cmake.in | 277 -------------------------- Modules/ExternalProject-verify.cmake.in | 37 ---- Modules/ExternalProject.cmake | 16 +- Modules/ExternalProject/RepositoryInfo.txt.in | 3 + Modules/ExternalProject/download.cmake.in | 173 ++++++++++++++++ Modules/ExternalProject/gitupdate.cmake.in | 277 ++++++++++++++++++++++++++ Modules/ExternalProject/verify.cmake.in | 37 ++++ Modules/RepositoryInfo.txt.in | 3 - 9 files changed, 498 insertions(+), 498 deletions(-) delete mode 100644 Modules/ExternalProject-download.cmake.in delete mode 100644 Modules/ExternalProject-gitupdate.cmake.in delete mode 100644 Modules/ExternalProject-verify.cmake.in create mode 100644 Modules/ExternalProject/RepositoryInfo.txt.in create mode 100644 Modules/ExternalProject/download.cmake.in create mode 100644 Modules/ExternalProject/gitupdate.cmake.in create mode 100644 Modules/ExternalProject/verify.cmake.in delete mode 100644 Modules/RepositoryInfo.txt.in diff --git a/Modules/ExternalProject-download.cmake.in b/Modules/ExternalProject-download.cmake.in deleted file mode 100644 index ff8c659..0000000 --- a/Modules/ExternalProject-download.cmake.in +++ /dev/null @@ -1,173 +0,0 @@ -# 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() - -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} - " -) diff --git a/Modules/ExternalProject-gitupdate.cmake.in b/Modules/ExternalProject-gitupdate.cmake.in deleted file mode 100644 index 0de2372..0000000 --- a/Modules/ExternalProject-gitupdate.cmake.in +++ /dev/null @@ -1,277 +0,0 @@ -# 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}^0" - 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-verify.cmake.in b/Modules/ExternalProject-verify.cmake.in deleted file mode 100644 index c06da4e..0000000 --- a/Modules/ExternalProject-verify.cmake.in +++ /dev/null @@ -1,37 +0,0 @@ -# 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() - -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") diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index fc15a0f..b2f89c7 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -1443,7 +1443,7 @@ function(_ep_write_gitupdate_script script_filename git_EXECUTABLE git_tag git_r endif() configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-gitupdate.cmake.in" + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitupdate.cmake.in" "${script_filename}" @ONLY ) @@ -1553,7 +1553,7 @@ function(_ep_write_downloadfile_script script_filename REMOTE LOCAL timeout inac # * USERPWD_ARGS # * HTTP_HEADERS_ARGS configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-download.cmake.in" + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/download.cmake.in" "${script_filename}" @ONLY ) @@ -1574,7 +1574,7 @@ function(_ep_write_verifyfile_script script_filename LOCAL hash) # * EXPECT_VALUE # * LOCAL configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-verify.cmake.in" + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/verify.cmake.in" "${script_filename}" @ONLY ) @@ -2613,7 +2613,7 @@ function(_ep_add_download_command name) set(module ${cvs_module}) set(tag ${cvs_tag}) configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in" "${stamp_dir}/${name}-cvsinfo.txt" @ONLY ) @@ -2638,7 +2638,7 @@ function(_ep_add_download_command name) set(module) set(tag ${svn_revision}) configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in" "${stamp_dir}/${name}-svninfo.txt" @ONLY ) @@ -2714,7 +2714,7 @@ function(_ep_add_download_command name) set(module) set(tag ${git_remote_name}) configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in" "${stamp_dir}/${name}-gitinfo.txt" @ONLY ) @@ -2754,7 +2754,7 @@ function(_ep_add_download_command name) set(module) set(tag) configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in" "${stamp_dir}/${name}-hginfo.txt" @ONLY ) @@ -2795,7 +2795,7 @@ function(_ep_add_download_command name) set(module "${url}") set(tag "${hash}") configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in" "${stamp_dir}/${name}-urlinfo.txt" @ONLY ) diff --git a/Modules/ExternalProject/RepositoryInfo.txt.in b/Modules/ExternalProject/RepositoryInfo.txt.in new file mode 100644 index 0000000..df8e322 --- /dev/null +++ b/Modules/ExternalProject/RepositoryInfo.txt.in @@ -0,0 +1,3 @@ +repository='@repository@' +module='@module@' +tag='@tag@' diff --git a/Modules/ExternalProject/download.cmake.in b/Modules/ExternalProject/download.cmake.in new file mode 100644 index 0000000..ff8c659 --- /dev/null +++ b/Modules/ExternalProject/download.cmake.in @@ -0,0 +1,173 @@ +# 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() + +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} + " +) diff --git a/Modules/ExternalProject/gitupdate.cmake.in b/Modules/ExternalProject/gitupdate.cmake.in new file mode 100644 index 0000000..0de2372 --- /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}^0" + 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/verify.cmake.in b/Modules/ExternalProject/verify.cmake.in new file mode 100644 index 0000000..c06da4e --- /dev/null +++ b/Modules/ExternalProject/verify.cmake.in @@ -0,0 +1,37 @@ +# 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() + +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") diff --git a/Modules/RepositoryInfo.txt.in b/Modules/RepositoryInfo.txt.in deleted file mode 100644 index df8e322..0000000 --- a/Modules/RepositoryInfo.txt.in +++ /dev/null @@ -1,3 +0,0 @@ -repository='@repository@' -module='@module@' -tag='@tag@' -- cgit v0.12 From 8feaa41ee90166ca69afebcd588b62c11f13e1a4 Mon Sep 17 00:00:00 2001 From: Craig Scott Date: Sat, 22 Jan 2022 17:25:33 +1100 Subject: ExternalProject: Ensure _ep_set_directories records cmake paths --- Modules/ExternalProject.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index b2f89c7..ee26662 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -1674,6 +1674,7 @@ function(_ep_set_directories name) endif() endif() if(prefix) + file(TO_CMAKE_PATH "${prefix}" prefix) set(tmp_default "${prefix}/tmp") set(download_default "${prefix}/src") set(source_default "${prefix}/src/${name}") @@ -1681,6 +1682,7 @@ function(_ep_set_directories name) set(stamp_default "${prefix}/src/${name}-stamp") set(install_default "${prefix}") else() + file(TO_CMAKE_PATH "${base}" base) set(tmp_default "${base}/tmp/${name}") set(download_default "${base}/Download/${name}") set(source_default "${base}/Source/${name}") @@ -1709,6 +1711,7 @@ function(_ep_set_directories name) if(NOT IS_ABSOLUTE "${${var}_dir}") get_filename_component(${var}_dir "${top}/${${var}_dir}" ABSOLUTE) endif() + file(TO_CMAKE_PATH "${${var}_dir}" ${var}_dir) set_property(TARGET ${name} PROPERTY _EP_${VAR}_DIR "${${var}_dir}") endforeach() @@ -1720,6 +1723,7 @@ function(_ep_set_directories name) if(NOT IS_ABSOLUTE "${log_dir}") get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE) endif() + file(TO_CMAKE_PATH "${log_dir}" log_dir) set_property(TARGET ${name} PROPERTY _EP_LOG_DIR "${log_dir}") get_property(source_subdir TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR) @@ -1731,6 +1735,7 @@ function(_ep_set_directories name) else() # Prefix with a slash so that when appended to the source directory, it # behaves as expected. + file(TO_CMAKE_PATH "${source_subdir}" source_subdir) set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "/${source_subdir}") endif() if(build_in_source) -- cgit v0.12 From 5fbac2bb24250eeeb64e2fb4868dcf976ee29d64 Mon Sep 17 00:00:00 2001 From: Craig Scott Date: Sat, 22 Jan 2022 22:36:14 +1100 Subject: ExternalProject: Move inline scripts to separate files This makes the scripts easier to work on, since the separate files don't require the extra level of escaping that the inlined code did. This also means the scripts can be rendered with appropriate syntax highlighting in IDEs, etc. --- Modules/ExternalProject.cmake | 352 ++++++++++----------------- Modules/ExternalProject/cfgcmd.txt.in | 1 + Modules/ExternalProject/extractfile.cmake.in | 65 +++++ Modules/ExternalProject/gitclone.cmake.in | 73 ++++++ Modules/ExternalProject/hgclone.cmake.in | 49 ++++ Modules/ExternalProject/mkdirs.cmake.in | 19 ++ 6 files changed, 337 insertions(+), 222 deletions(-) create mode 100644 Modules/ExternalProject/cfgcmd.txt.in create mode 100644 Modules/ExternalProject/extractfile.cmake.in create mode 100644 Modules/ExternalProject/gitclone.cmake.in create mode 100644 Modules/ExternalProject/hgclone.cmake.in create mode 100644 Modules/ExternalProject/mkdirs.cmake.in diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index ee26662..411a1a9 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -1254,7 +1254,25 @@ define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED "ExternalProject module." ) -function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git_repository git_tag git_remote_name init_submodules git_submodules_recurse git_submodules git_shallow git_progress git_config src_name work_dir gitclone_infofile gitclone_stampfile tls_verify) +function(_ep_write_gitclone_script + script_filename + source_dir + git_EXECUTABLE + git_repository + git_tag + git_remote_name + init_submodules + git_submodules_recurse + git_submodules + git_shallow + git_progress + git_config + src_name + work_dir + gitclone_infofile + gitclone_stampfile + tls_verify) + if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5) # Use `git checkout --` to avoid ambiguity with a local path. set(git_checkout_explicit-- "--") @@ -1300,136 +1318,48 @@ function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git endif() string (REPLACE ";" " " git_options "${git_options}") - file(WRITE ${script_filename} -" -if(EXISTS \"${gitclone_stampfile}\" AND EXISTS \"${gitclone_infofile}\" AND - \"${gitclone_stampfile}\" IS_NEWER_THAN \"${gitclone_infofile}\") - 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 + configure_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitclone.cmake.in + ${script_filename} + @ONLY ) -if(error_code) - message(FATAL_ERROR \"Failed to copy script-last-run stamp file: '${gitclone_stampfile}'\") -endif() - -" -) - endfunction() -function(_ep_write_hgclone_script script_filename source_dir hg_EXECUTABLE hg_repository hg_tag src_name work_dir hgclone_infofile hgclone_stampfile) +function(_ep_write_hgclone_script + script_filename + source_dir + hg_EXECUTABLE + hg_repository + hg_tag + src_name + work_dir + hgclone_infofile + hgclone_stampfile) + if("${hg_tag}" STREQUAL "") message(FATAL_ERROR "Tag for hg checkout should not be empty.") endif() - file(WRITE ${script_filename} -" -if(EXISTS \"${hgclone_stampfile}\" AND EXISTS \"${hgclone_infofile}\" AND - \"${hgclone_stampfile}\" IS_NEWER_THAN \"${hgclone_infofile}\") - 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 + configure_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/hgclone.cmake.in + ${script_filename} + @ONLY ) -if(error_code) - message(FATAL_ERROR \"Failed to copy script-last-run stamp file: '${hgclone_stampfile}'\") -endif() - -" -) - endfunction() -function(_ep_write_gitupdate_script script_filename git_EXECUTABLE git_tag git_remote_name init_submodules git_submodules_recurse git_submodules git_repository work_dir git_update_strategy) +function(_ep_write_gitupdate_script + script_filename + git_EXECUTABLE + git_tag + git_remote_name + init_submodules + git_submodules_recurse + git_submodules + git_repository + work_dir + git_update_strategy) + if("${git_tag}" STREQUAL "") message(FATAL_ERROR "Tag for git checkout should not be empty.") endif() @@ -1449,7 +1379,21 @@ function(_ep_write_gitupdate_script script_filename git_EXECUTABLE git_tag git_r ) endfunction() -function(_ep_write_downloadfile_script script_filename REMOTE LOCAL timeout inactivity_timeout no_progress hash tls_verify tls_cainfo userpwd http_headers netrc netrc_file) +function(_ep_write_downloadfile_script + script_filename + REMOTE + LOCAL + timeout + inactivity_timeout + no_progress + hash + tls_verify + tls_cainfo + userpwd + http_headers + netrc + netrc_file) + if(timeout) set(TIMEOUT_ARGS TIMEOUT ${timeout}) set(TIMEOUT_MSG "${timeout} seconds") @@ -1465,7 +1409,6 @@ function(_ep_write_downloadfile_script script_filename REMOTE LOCAL timeout inac set(INACTIVITY_TIMEOUT_MSG "none") endif() - if(no_progress) set(SHOW_PROGRESS "") else() @@ -1597,68 +1540,11 @@ function(_ep_write_extractfile_script script_filename name filename directory) return() endif() - file(WRITE ${script_filename} -"# 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 \"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 \"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\") -" -) - + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/extractfile.cmake.in" + "${script_filename}" + @ONLY + ) endfunction() @@ -1747,22 +1633,19 @@ function(_ep_set_directories name) endif() endif() - # Make the directories at CMake configure time *and* add a custom command - # to make them at build time. They need to exist at makefile generation - # time for Borland make and wmake so that CMake may generate makefiles - # with "cd C:\short\paths\with\no\spaces" commands in them. - # - # Additionally, the add_custom_command is still used in case somebody - # removes one of the necessary directories and tries to rebuild without - # re-running cmake. - foreach(var ${places}) - string(TOUPPER "${var}" VAR) - get_property(dir TARGET ${name} PROPERTY _EP_${VAR}_DIR) - file(MAKE_DIRECTORY "${dir}") - if(NOT EXISTS "${dir}") - message(FATAL_ERROR "dir '${dir}' does not exist after file(MAKE_DIRECTORY)") - endif() - endforeach() + # This script will be used both here and by the mkdir step. We create the + # directories now at configure time and ensure they exist again at build + # time (since somebody might remove one of the required directories and try + # to rebuild without re-running cmake). They need to exist now at makefile + # generation time for Borland make and wmake so that CMake may generate + # makefiles with "cd C:\short\paths\with\no\spaces" commands in them. + set(script_filename "${tmp_dir}/${name}-mkdirs.cmake") + configure_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/mkdirs.cmake.in + ${script_filename} + @ONLY + ) + include(${script_filename}) endfunction() @@ -2528,22 +2411,14 @@ endfunction() function(_ep_add_mkdir_command name) - ExternalProject_Get_Property(${name} - source_dir binary_dir install_dir stamp_dir download_dir tmp_dir log_dir) - - _ep_get_configuration_subdir_suffix(cfgdir) + ExternalProject_Get_Property(${name} tmp_dir) + set(script_filename "${tmp_dir}/${name}-mkdirs.cmake") ExternalProject_Add_Step(${name} mkdir INDEPENDENT TRUE COMMENT "Creating directories for '${name}'" - COMMAND ${CMAKE_COMMAND} -E make_directory ${source_dir} - COMMAND ${CMAKE_COMMAND} -E make_directory ${binary_dir} - COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir} - COMMAND ${CMAKE_COMMAND} -E make_directory ${tmp_dir} - COMMAND ${CMAKE_COMMAND} -E make_directory ${stamp_dir}${cfgdir} - COMMAND ${CMAKE_COMMAND} -E make_directory ${download_dir} - COMMAND ${CMAKE_COMMAND} -E make_directory ${log_dir} - ) + COMMAND ${CMAKE_COMMAND} -P ${script_filename} + ) endfunction() @@ -2855,7 +2730,21 @@ function(_ep_add_download_command name) get_property(http_password TARGET ${name} PROPERTY _EP_HTTP_PASSWORD) get_property(http_headers TARGET ${name} PROPERTY _EP_HTTP_HEADER) set(download_script "${stamp_dir}/download-${name}.cmake") - _ep_write_downloadfile_script("${download_script}" "${url}" "${file}" "${timeout}" "${inactivity_timeout}" "${no_progress}" "${hash}" "${tls_verify}" "${tls_cainfo}" "${http_username}:${http_password}" "${http_headers}" "${netrc}" "${netrc_file}") + _ep_write_downloadfile_script( + "${download_script}" + "${url}" + "${file}" + "${timeout}" + "${inactivity_timeout}" + "${no_progress}" + "${hash}" + "${tls_verify}" + "${tls_cainfo}" + "${http_username}:${http_password}" + "${http_headers}" + "${netrc}" + "${netrc_file}" + ) set(cmd ${CMAKE_COMMAND} -P "${download_script}" COMMAND) if (no_extract) @@ -2873,11 +2762,20 @@ function(_ep_add_download_command name) set(steps "verify and extract") endif () set(comment "Performing download step (${steps}) for '${name}'") - _ep_write_verifyfile_script("${stamp_dir}/verify-${name}.cmake" "${file}" "${hash}") + _ep_write_verifyfile_script( + "${stamp_dir}/verify-${name}.cmake" + "${file}" + "${hash}" + ) endif() list(APPEND cmd ${CMAKE_COMMAND} -P ${stamp_dir}/verify-${name}.cmake) if (NOT no_extract) - _ep_write_extractfile_script("${stamp_dir}/extract-${name}.cmake" "${name}" "${file}" "${source_dir}") + _ep_write_extractfile_script( + "${stamp_dir}/extract-${name}.cmake" + "${name}" + "${file}" + "${source_dir}" + ) list(APPEND cmd COMMAND ${CMAKE_COMMAND} -P ${stamp_dir}/extract-${name}.cmake) else () set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file}) @@ -3040,9 +2938,18 @@ function(_ep_add_update_command name) _ep_get_git_submodules_recurse(git_submodules_recurse) - _ep_write_gitupdate_script(${tmp_dir}/${name}-gitupdate.cmake - ${GIT_EXECUTABLE} ${git_tag} ${git_remote_name} ${git_init_submodules} "${git_submodules_recurse}" "${git_submodules}" ${git_repository} ${work_dir} ${git_update_strategy} - ) + _ep_write_gitupdate_script( + "${tmp_dir}/${name}-gitupdate.cmake" + "${GIT_EXECUTABLE}" + "${git_tag}" + "${git_remote_name}" + "${git_init_submodules}" + "${git_submodules_recurse}" + "${git_submodules}" + "${git_repository}" + "${work_dir}" + "${git_update_strategy}" + ) set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitupdate.cmake) set(always 1) elseif(hg_repository) @@ -3284,10 +3191,11 @@ function(_ep_add_configure_command name) # used, cmake args or cmake generator) then re-run the configure step. # Fixes issue https://gitlab.kitware.com/cmake/cmake/-/issues/10258 # - if(NOT EXISTS ${tmp_dir}/${name}-cfgcmd.txt.in) - file(WRITE ${tmp_dir}/${name}-cfgcmd.txt.in "cmd='\@cmd\@'\n") - endif() - configure_file(${tmp_dir}/${name}-cfgcmd.txt.in ${tmp_dir}/${name}-cfgcmd.txt) + configure_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/cfgcmd.txt.in + ${tmp_dir}/${name}-cfgcmd.txt + @ONLY + ) list(APPEND file_deps ${tmp_dir}/${name}-cfgcmd.txt) list(APPEND file_deps ${_ep_cache_args_script}) 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/extractfile.cmake.in b/Modules/ExternalProject/extractfile.cmake.in new file mode 100644 index 0000000..d7f5756 --- /dev/null +++ b/Modules/ExternalProject/extractfile.cmake.in @@ -0,0 +1,65 @@ +# 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..3312171 --- /dev/null +++ b/Modules/ExternalProject/gitclone.cmake.in @@ -0,0 +1,73 @@ +# 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(EXISTS "@gitclone_stampfile@" AND EXISTS "@gitclone_infofile@" AND + "@gitclone_stampfile@" IS_NEWER_THAN "@gitclone_infofile@") + 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/hgclone.cmake.in b/Modules/ExternalProject/hgclone.cmake.in new file mode 100644 index 0000000..e2b55ba --- /dev/null +++ b/Modules/ExternalProject/hgclone.cmake.in @@ -0,0 +1,49 @@ +# 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(EXISTS "@hgclone_stampfile@" AND EXISTS "@hgclone_infofile@" AND + "@hgclone_stampfile@" IS_NEWER_THAN "@hgclone_infofile@") + 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/mkdirs.cmake.in b/Modules/ExternalProject/mkdirs.cmake.in new file mode 100644 index 0000000..d30a2e7 --- /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() -- cgit v0.12