From 57d442e182bcb9a4426912ed7ba68b58c4dcc940 Mon Sep 17 00:00:00 2001 From: Craig Scott Date: Wed, 10 Mar 2021 08:59:59 +1100 Subject: Revert ExternalProject and FetchContent refactoring Refactoring of the ExternalProject and FetchContent modules moved the commands into CMake scripts. This broke custom commands that used shell redirection or special build tool variables of the form $(MakeVar). Undo the sequence of commits that performed this refactoring and follow-up fixes associated with it. The following commits are reverted by this change: 4f3d1abbb4 (ExternalProject: Refactor pre-configure steps to support no-target uses, 2021-02-05) 17e5516e60 (FetchContent: Invoke steps directly and avoid a separate sub-build, 2021-01-29) bd876f3849 (FetchContent: Restore patch command support, 2021-02-18) 404cddb7bb (ExternalProject: Fix misuse of IS_NEWER_THAN in timestamp checks, 2021-02-21) b0da671243 (FetchContent: Don't update timestamps if files don't change, 2021-02-18) Fixes: #21892 --- Help/release/3.20.rst | 12 - Modules/ExternalProject-download.cmake.in | 173 ++ Modules/ExternalProject-gitupdate.cmake.in | 277 +++ Modules/ExternalProject-verify.cmake.in | 37 + Modules/ExternalProject.cmake | 1792 ++++++++------------ Modules/ExternalProject/RepositoryInfo.txt.in | 1 - .../ExternalProject/captured_process_setup.cmake | 55 - Modules/ExternalProject/cfgcmd.txt.in | 1 - Modules/ExternalProject/copydir.cmake.in | 10 - Modules/ExternalProject/customcommand.cmake.in | 8 - .../customcommand_preamble.cmake.in | 8 - Modules/ExternalProject/download.cmake.in | 205 --- Modules/ExternalProject/extractfile.cmake.in | 73 - Modules/ExternalProject/gitclone.cmake.in | 93 - Modules/ExternalProject/gitupdate.cmake.in | 315 ---- Modules/ExternalProject/hgclone.cmake.in | 59 - Modules/ExternalProject/hgupdate.cmake.in | 24 - Modules/ExternalProject/mkdirs.cmake.in | 19 - Modules/ExternalProject/verify.cmake.in | 58 - Modules/FetchContent.cmake | 131 +- Modules/FetchContent/CMakeLists.cmake.in | 27 + Modules/RepositoryInfo.txt.in | 3 + .../NO_DEPENDS-CMP0114-NEW-stderr.txt | 2 +- .../NO_DEPENDS-CMP0114-WARN-stderr.txt | 4 +- .../RunCMake/ExternalProject/NoOptions-stderr.txt | 2 +- .../ExternalProject/SourceEmpty-stderr.txt | 2 +- .../ExternalProject/SourceMissing-stderr.txt | 2 +- .../ExternalProject/UsesTerminal-check.cmake | 2 +- .../RunCMake/FetchContent/MultiCommand-stdout.txt | 6 - Tests/RunCMake/FetchContent/MultiCommand.cmake | 18 - Tests/RunCMake/FetchContent/RunCMakeTest.cmake | 32 +- Tests/RunCMake/FetchContent/SameGenerator.cmake | 17 + Tests/RunCMake/FetchContent/TimeStamps-stdout.txt | 2 - Tests/RunCMake/FetchContent/TimeStamps.cmake | 14 - .../FetchContent/TimeStampsRerun-check.cmake | 38 - Tests/RunCMake/FetchContent/TimeStampsRerun.cmake | 1 - 36 files changed, 1362 insertions(+), 2161 deletions(-) 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/ExternalProject/RepositoryInfo.txt.in delete mode 100644 Modules/ExternalProject/captured_process_setup.cmake delete mode 100644 Modules/ExternalProject/cfgcmd.txt.in delete mode 100644 Modules/ExternalProject/copydir.cmake.in delete mode 100644 Modules/ExternalProject/customcommand.cmake.in delete mode 100644 Modules/ExternalProject/customcommand_preamble.cmake.in delete mode 100644 Modules/ExternalProject/download.cmake.in delete mode 100644 Modules/ExternalProject/extractfile.cmake.in delete mode 100644 Modules/ExternalProject/gitclone.cmake.in delete mode 100644 Modules/ExternalProject/gitupdate.cmake.in delete mode 100644 Modules/ExternalProject/hgclone.cmake.in delete mode 100644 Modules/ExternalProject/hgupdate.cmake.in delete mode 100644 Modules/ExternalProject/mkdirs.cmake.in delete mode 100644 Modules/ExternalProject/verify.cmake.in create mode 100644 Modules/FetchContent/CMakeLists.cmake.in create mode 100644 Modules/RepositoryInfo.txt.in delete mode 100644 Tests/RunCMake/FetchContent/MultiCommand-stdout.txt delete mode 100644 Tests/RunCMake/FetchContent/MultiCommand.cmake create mode 100644 Tests/RunCMake/FetchContent/SameGenerator.cmake delete mode 100644 Tests/RunCMake/FetchContent/TimeStamps-stdout.txt delete mode 100644 Tests/RunCMake/FetchContent/TimeStamps.cmake delete mode 100644 Tests/RunCMake/FetchContent/TimeStampsRerun-check.cmake delete mode 100644 Tests/RunCMake/FetchContent/TimeStampsRerun.cmake diff --git a/Help/release/3.20.rst b/Help/release/3.20.rst index eede362..176447d 100644 --- a/Help/release/3.20.rst +++ b/Help/release/3.20.rst @@ -318,18 +318,6 @@ Other Changes * Ninja generators now transform the ``DEPFILE`` generated by an :command:`add_custom_command`. See policy :policy:`CMP0116` for details. -* The implementation of the :module:`ExternalProject` module was - significantly refactored. The patch step gained support for - using the terminal with a new ``USES_TERMINAL_PATCH`` keyword - as a by-product of that work. - -* The :module:`FetchContent` module no longer creates a separate - sub-build to implement the content population. It now invokes - the step scripts directly from within the main project's - configure stage. This significantly speeds up the configure - phase when the required content is already populated and - up-to-date. - * The precompiled Linux binaries provided on `cmake.org `_ have changed their naming pattern to ``cmake-$ver-linux-$arch``, where ``$arch`` is either ``x86_64`` or 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..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-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/ExternalProject.cmake b/Modules/ExternalProject.cmake index 987b69a..56525080 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -407,7 +407,7 @@ External Project Definition ``CVS_TAG `` Tag to checkout from the CVS repository. - **Update Step Options:** + **Update/Patch Step Options:** Whenever CMake is re-run, by default the external project's sources will be updated if the download method supports updates (e.g. a git repository would be checked if the ``GIT_TAG`` does not refer to a specific commit). @@ -442,7 +442,6 @@ External Project Definition This may cause a step target to be created automatically for the ``download`` step. See policy :policy:`CMP0114`. - **Patch Step Options:** ``PATCH_COMMAND ...`` Specifies a custom command to patch the sources after an update. By default, no patch command is defined. Note that it can be quite difficult @@ -718,11 +717,6 @@ External Project Definition ``USES_TERMINAL_UPDATE `` Give the update step access to the terminal. - ``USES_TERMINAL_PATCH `` - .. versionadded:: 3.20 - - Give the patch step access to the terminal. - ``USES_TERMINAL_CONFIGURE `` Give the configure step access to the terminal. @@ -1140,17 +1134,16 @@ macro(_ep_get_hash_regex out_var) set(${out_var} "^(${${out_var}})=([0-9A-Fa-f]+)$") endmacro() -function(_ep_parse_arguments_to_vars keywords name ns args) - # Transfer the arguments into variables in the calling scope. +function(_ep_parse_arguments f keywords name ns args) + # Transfer the arguments to this function into target properties for the + # new custom target we just added so that we can set up all the build steps + # correctly based on target properties. + # # Because some keywords can be repeated, we can't use cmake_parse_arguments(). - # Instead, we loop through the args and consider the namespace starting with - # an upper-case letter followed by at least two more upper-case letters, + # Instead, we loop through ARGN and consider the namespace starting with an + # upper-case letter followed by at least two more upper-case letters, # numbers or underscores to be keywords. - foreach(key IN LISTS keywords) - unset(${ns}${key}) - endforeach() - set(key) foreach(arg IN LISTS args) @@ -1167,100 +1160,68 @@ function(_ep_parse_arguments_to_vars keywords name ns args) if(is_value) if(key) # Value - list(APPEND ${ns}${key} "${arg}") + if(NOT arg STREQUAL "") + set_property(TARGET ${name} APPEND PROPERTY ${ns}${key} "${arg}") + else() + get_property(have_key TARGET ${name} PROPERTY ${ns}${key} SET) + if(have_key) + get_property(value TARGET ${name} PROPERTY ${ns}${key}) + set_property(TARGET ${name} PROPERTY ${ns}${key} "${value};${arg}") + else() + set_property(TARGET ${name} PROPERTY ${ns}${key} "${arg}") + endif() + endif() else() # Missing Keyword - message(AUTHOR_WARNING "value '${arg}' with no previous keyword") + message(AUTHOR_WARNING "value '${arg}' with no previous keyword in ${f}") endif() else() set(key "${arg}") endif() endforeach() - - foreach(key IN LISTS keywords) - if(DEFINED ${ns}${key}) - set(${ns}${key} "${${ns}${key}}" PARENT_SCOPE) - else() - unset(${ns}${key} PARENT_SCOPE) - endif() - endforeach() - -endfunction() - -function(_ep_parse_arguments keywords name ns args) - _ep_parse_arguments_to_vars("${keywords}" ${name} ${ns} "${args}") - - # Transfer the arguments to the target as target properties. These are - # read by the various steps, potentially from different scopes. - foreach(key IN LISTS keywords) - if(DEFINED ${ns}${key}) - set_property(TARGET ${name} PROPERTY ${ns}${key} "${${ns}${key}}") - endif() - endforeach() - endfunction() -if(NOT DEFINED CMAKE_SCRIPT_MODE_FILE) - define_property(DIRECTORY PROPERTY "EP_BASE" INHERITED - BRIEF_DOCS "Base directory for External Project storage." - FULL_DOCS - "See documentation of the ExternalProject_Add() function in the " - "ExternalProject module." - ) - define_property(DIRECTORY PROPERTY "EP_PREFIX" INHERITED - BRIEF_DOCS "Top prefix for External Project storage." - FULL_DOCS - "See documentation of the ExternalProject_Add() function in the " - "ExternalProject module." - ) +define_property(DIRECTORY PROPERTY "EP_BASE" INHERITED + BRIEF_DOCS "Base directory for External Project storage." + FULL_DOCS + "See documentation of the ExternalProject_Add() function in the " + "ExternalProject module." + ) - define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED - BRIEF_DOCS - "List of ExternalProject steps that automatically get corresponding targets" - FULL_DOCS - "These targets will be dependent on the main target dependencies. " - "See documentation of the ExternalProject_Add_StepTargets() function in the " - "ExternalProject module." - ) +define_property(DIRECTORY PROPERTY "EP_PREFIX" INHERITED + BRIEF_DOCS "Top prefix for External Project storage." + FULL_DOCS + "See documentation of the ExternalProject_Add() function in the " + "ExternalProject module." + ) - define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED - BRIEF_DOCS - "List of ExternalProject steps that automatically get corresponding targets" - FULL_DOCS - "These targets will not be dependent on the main target dependencies. " - "See documentation of the ExternalProject_Add_StepTargets() function in the " - "ExternalProject module." - ) +define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED + BRIEF_DOCS + "List of ExternalProject steps that automatically get corresponding targets" + FULL_DOCS + "These targets will be dependent on the main target dependencies. " + "See documentation of the ExternalProject_Add_StepTargets() function in the " + "ExternalProject module." + ) - define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED - BRIEF_DOCS "Never update automatically from the remote repo." - FULL_DOCS - "See documentation of the ExternalProject_Add() function in the " - "ExternalProject module." - ) -endif() +define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED + BRIEF_DOCS + "List of ExternalProject steps that automatically get corresponding targets" + FULL_DOCS + "These targets will not be dependent on the main target dependencies. " + "See documentation of the ExternalProject_Add_StepTargets() function in the " + "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 - quiet) +define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED + BRIEF_DOCS "Never update automatically from the remote repo." + FULL_DOCS + "See documentation of the ExternalProject_Add() function in the " + "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) if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5) # Use `git checkout --` to avoid ambiguity with a local path. set(git_checkout_explicit-- "--") @@ -1306,52 +1267,134 @@ function(_ep_write_gitclone_script endif() string (REPLACE ";" " " git_options "${git_options}") - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitclone.cmake.in - ${script_filename} - @ONLY + file(WRITE ${script_filename} +" +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() -endfunction() +# 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() -function(_ep_write_hgclone_script - script_filename - source_dir - hg_EXECUTABLE - hg_repository - hg_tag - src_name - work_dir - hgclone_infofile - hgclone_stampfile - quiet) +" +) + +endfunction() +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(NOT \"${hgclone_infofile}\" IS_NEWER_THAN \"${hgclone_stampfile}\") + message(STATUS \"Avoiding repeated hg clone, stamp file is up to date: '${hgclone_stampfile}'\") + return() +endif() - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/hgclone.cmake.in - ${script_filename} - @ONLY +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() -endfunction() +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() -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 - quiet) +" +) +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) if("${git_tag}" STREQUAL "") message(FATAL_ERROR "Tag for git checkout should not be empty.") endif() @@ -1365,56 +1408,13 @@ function(_ep_write_gitupdate_script endif() configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitupdate.cmake.in" - "${script_filename}" - @ONLY - ) -endfunction() - -function(_ep_write_hgupdate_script - script_filename - hg_EXECUTABLE - hg_tag - work_dir - quiet) - - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/hgupdate.cmake.in - ${script_filename} - @ONLY - ) - -endfunction() - -function(_ep_write_copydir_script - script_filename - from_dir - to_dir) - - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/copydir.cmake.in" - "${script_filename}" - @ONLY + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-gitupdate.cmake.in" + "${script_filename}" + @ONLY ) 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 - extract_script_filename - quiet) - +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") @@ -1431,7 +1431,7 @@ function(_ep_write_downloadfile_script endif() - if(no_progress OR quiet) + if(no_progress) set(SHOW_PROGRESS "") else() set(SHOW_PROGRESS "SHOW_PROGRESS") @@ -1518,19 +1518,13 @@ function(_ep_write_downloadfile_script # * USERPWD_ARGS # * HTTP_HEADERS_ARGS configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/download.cmake.in" - "${script_filename}" - @ONLY + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-download.cmake.in" + "${script_filename}" + @ONLY ) endfunction() -function(_ep_write_verifyfile_script - script_filename - LOCAL - hash - extract_script_filename - quiet) - +function(_ep_write_verifyfile_script script_filename LOCAL hash) _ep_get_hash_regex(_ep_hash_regex) if("${hash}" MATCHES "${_ep_hash_regex}") set(ALGO "${CMAKE_MATCH_1}") @@ -1544,22 +1538,15 @@ function(_ep_write_verifyfile_script # * ALGO # * EXPECT_VALUE # * LOCAL - # * extract_script_filename configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/verify.cmake.in" - "${script_filename}" - @ONLY + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-verify.cmake.in" + "${script_filename}" + @ONLY ) endfunction() -function(_ep_write_extractfile_script - script_filename - name - filename - directory - quiet) - +function(_ep_write_extractfile_script script_filename name filename directory) set(args "") if(filename MATCHES "(\\.|=)(7z|tar\\.bz2|tar\\.gz|tar\\.xz|tbz2|tgz|txz|zip)$") @@ -1571,34 +1558,77 @@ function(_ep_write_extractfile_script endif() if(args STREQUAL "") - message(FATAL_ERROR - "Do not know how to extract '${filename}' -- known types are: " - ".7z, .tar, .tar.bz2, .tar.gz, .tar.xz, .tbz2, .tgz, .txz and .zip") + message(SEND_ERROR "error: do not know how to extract '${filename}' -- known types are .7z, .tar, .tar.bz2, .tar.gz, .tar.xz, .tbz2, .tgz, .txz and .zip") + return() endif() - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/extractfile.cmake.in" - "${script_filename}" - @ONLY - ) + file(WRITE ${script_filename} +"# Make file names absolute: +# +get_filename_component(filename \"${filename}\" ABSOLUTE) +get_filename_component(directory \"${directory}\" ABSOLUTE) -endfunction() +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() -# This function is an implementation detail of ExternalProject_Add() and -# _ep_do_preconfigure_steps_now(). +# Analyze what came out of the tar file: # -# The function expects keyword arguments to have already been parsed into -# variables of the form _EP_. It will create the various directories -# before returning and it will populate variables of the form -# _EP__DIR in the calling scope. +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: # -# Variables will also be set in the calling scope to enable subsequently -# calling _ep_add_preconfigure_command() for the mkdir step. +message(STATUS \"extracting... [rename]\") +file(REMOVE_RECURSE \${directory}) +get_filename_component(contents \${contents} ABSOLUTE) +file(RENAME \${contents} \${directory}) + +# Clean up: # -function(_ep_prepare_directories name) +message(STATUS \"extracting... [clean up]\") +file(REMOVE_RECURSE \"\${ut_dir}\") + +message(STATUS \"extracting... done\") +" +) + +endfunction() - set(prefix ${_EP_PREFIX}) + +function(_ep_set_directories name) + get_property(prefix TARGET ${name} PROPERTY _EP_PREFIX) if(NOT prefix) get_property(prefix DIRECTORY PROPERTY EP_PREFIX) if(NOT prefix) @@ -1609,7 +1639,6 @@ function(_ep_prepare_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}") @@ -1617,7 +1646,6 @@ function(_ep_prepare_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}") @@ -1625,10 +1653,10 @@ function(_ep_prepare_directories name) set(stamp_default "${base}/Stamp/${name}") set(install_default "${base}/Install/${name}") endif() - - set(build_in_source "${_EP_BUILD_IN_SOURCE}") + get_property(build_in_source TARGET ${name} PROPERTY _EP_BUILD_IN_SOURCE) if(build_in_source) - if(DEFINED _EP_BINARY_DIR) + get_property(have_binary_dir TARGET ${name} PROPERTY _EP_BINARY_DIR SET) + if(have_binary_dir) message(FATAL_ERROR "External project ${name} has both BINARY_DIR and BUILD_IN_SOURCE!") endif() @@ -1639,78 +1667,65 @@ function(_ep_prepare_directories name) set(places stamp download source binary install tmp) foreach(var ${places}) string(TOUPPER "${var}" VAR) - set(${var}_dir "${_EP_${VAR}_DIR}") + get_property(${var}_dir TARGET ${name} PROPERTY _EP_${VAR}_DIR) if(NOT ${var}_dir) set(${var}_dir "${${var}_default}") endif() 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() # Special case for default log directory based on stamp directory. - set(log_dir "${_EP_LOG_DIR}") + get_property(log_dir TARGET ${name} PROPERTY _EP_LOG_DIR) if(NOT log_dir) - set(log_dir "${stamp_dir}") - else() - if(NOT IS_ABSOLUTE "${log_dir}") - get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE) - endif() + get_property(log_dir TARGET ${name} PROPERTY _EP_STAMP_DIR) endif() - file(TO_CMAKE_PATH "${log_dir}" log_dir) - list(APPEND places log) + if(NOT IS_ABSOLUTE "${log_dir}") + get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE) + endif() + set_property(TARGET ${name} PROPERTY _EP_LOG_DIR "${log_dir}") - set(source_subdir "${_EP_SOURCE_SUBDIR}") - if(source_subdir) - if(IS_ABSOLUTE "${source_subdir}") - message(FATAL_ERROR - "External project ${name} has non-relative SOURCE_SUBDIR!") - endif() - string(REPLACE "\\" "/" source_subdir "${source_subdir}") + get_property(source_subdir TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR) + if(NOT source_subdir) + set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "") + elseif(IS_ABSOLUTE "${source_subdir}") + message(FATAL_ERROR + "External project ${name} has non-relative SOURCE_SUBDIR!") + else() # Prefix with a slash so that when appended to the source directory, it # behaves as expected. - string(PREPEND source_subdir "/") + set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "/${source_subdir}") endif() - if(build_in_source) - set(binary_dir "${source_dir}${source_subdir}") + get_property(source_dir TARGET ${name} PROPERTY _EP_SOURCE_DIR) + if(source_subdir) + set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}/${source_subdir}") + else() + set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}") + endif() endif() - # This script will be used both here and by the mkdir step. We create the - # directories now at configure time and ensure they exists 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}) - - set(comment "Creating directories for '${name}'") - set(cmd ${CMAKE_COMMAND} -P ${script_filename}) - - # Provide variables that can be used later to create a custom command or - # invoke the step directly - set(_EPcomment_MKDIR "${comment}" PARENT_SCOPE) - set(_EPcommand_MKDIR "${cmd}" PARENT_SCOPE) - set(_EPalways_MKDIR FALSE PARENT_SCOPE) - set(_EPexcludefrommain_MKDIR FALSE PARENT_SCOPE) - set(_EPdepends_MKDIR "" PARENT_SCOPE) - set(_EPdependees_MKDIR "" PARENT_SCOPE) - + # 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) - set(_EP_${VAR}_DIR "${${var}_dir}" PARENT_SCOPE) + 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() - set(_EP_SOURCE_SUBDIR "${source_subdir}" PARENT_SCOPE) - endfunction() + # IMPORTANT: this MUST be a macro and not a function because of the # in-place replacements that occur in each ${var} # @@ -1726,17 +1741,6 @@ macro(_ep_replace_location_tags target_name) endforeach() endmacro() -macro(_ep_replace_location_tags_from_vars) - set(vars ${ARGN}) - foreach(var ${vars}) - if(${var}) - foreach(dir SOURCE_DIR SOURCE_SUBDIR BINARY_DIR INSTALL_DIR TMP_DIR DOWNLOAD_DIR DOWNLOADED_FILE LOG_DIR) - string(REPLACE "<${dir}>" "${_EP_${dir}}" ${var} "${${var}}") - endforeach() - endif() - endforeach() -endmacro() - function(_ep_command_line_to_initial_cache var args force) set(script_initial_cache "") @@ -1919,24 +1923,17 @@ function(_ep_get_build_command name step cmd_var) set(${cmd_var} "${cmd}" PARENT_SCOPE) endfunction() -function(_ep_write_log_script name step genex_supported cmd_var) - - set(log_dir "${_EP_LOG_DIR}") - set(tmp_dir "${_EP_TMP_DIR}") - - if(genex_supported) - set(script_base ${tmp_dir}/${name}-${step}-$) - else() - set(script_base ${tmp_dir}/${name}-${step}) - endif() +function(_ep_write_log_script name step cmd_var) + ExternalProject_Get_Property(${name} log_dir) + ExternalProject_Get_Property(${name} stamp_dir) set(command "${${cmd_var}}") set(make "") set(code_cygpath_make "") - if(command MATCHES [[^\$\(MAKE\)]]) + if(command MATCHES "^\\$\\(MAKE\\)") # GNU make recognizes the string "$(MAKE)" as recursive make, so # ensure that it appears directly in the makefile. - string(REGEX REPLACE [[^\$\(MAKE\)]] [[${make}]] command "${command}") + string(REGEX REPLACE "^\\$\\(MAKE\\)" "\${make}" command "${command}") set(make "-Dmake=$(MAKE)") if(WIN32 AND NOT CYGWIN) @@ -1958,8 +1955,8 @@ endif() endif() set(config "") - if("${CMAKE_CFG_INTDIR}" MATCHES [[^\$]]) - string(REPLACE "${CMAKE_CFG_INTDIR}" [[${config}]] command "${command}") + if("${CMAKE_CFG_INTDIR}" MATCHES "^\\$") + string(REPLACE "${CMAKE_CFG_INTDIR}" "\${config}" command "${command}") set(config "-Dconfig=${CMAKE_CFG_INTDIR}") endif() @@ -1993,22 +1990,15 @@ endif() endif() endforeach() string(APPEND code "set(command \"${cmd}\")${code_execute_process}") - if(genex_supported) - file(GENERATE OUTPUT "${script_base}-impl.cmake" CONTENT "${code}") - else() - file(WRITE "${script_base}-impl.cmake" "${code}") - endif() - set(command ${CMAKE_COMMAND} - -D "make=\${make}" - -D "config=\${config}" - -P ${script_base}-impl.cmake - ) + file(GENERATE OUTPUT "${stamp_dir}/${name}-${step}-$-impl.cmake" CONTENT "${code}") + set(command ${CMAKE_COMMAND} "-Dmake=\${make}" "-Dconfig=\${config}" -P ${stamp_dir}/${name}-${step}-$-impl.cmake) endif() # Wrap the command in a script to log output to files. + set(script ${stamp_dir}/${name}-${step}-$.cmake) set(logbase ${log_dir}/${name}-${step}) - set(log_merged "${_EP_LOG_MERGED_STDOUTERR}") - set(log_output_on_failure "${_EP_LOG_OUTPUT_ON_FAILURE}") + get_property(log_merged TARGET ${name} PROPERTY _EP_LOG_MERGED_STDOUTERR) + get_property(log_output_on_failure TARGET ${name} PROPERTY _EP_LOG_OUTPUT_ON_FAILURE) if (log_merged) set(stdout_log "${logbase}.log") set(stderr_log "${logbase}.log") @@ -2067,19 +2057,14 @@ if(result) message(FATAL_ERROR \"\${msg}\") endif() else() - if(NOT \"${CMAKE_GENERATOR}\" MATCHES \"Ninja\" AND NOT \"${_EP_QUIET}\") + if(NOT \"${CMAKE_GENERATOR}\" MATCHES \"Ninja\") set(msg \"${name} ${step} command succeeded. See also ${logbase}-*.log\") message(STATUS \"\${msg}\") endif() endif() ") - set(script_filename ${script_base}.cmake) - if(genex_supported) - file(GENERATE OUTPUT ${script_filename} CONTENT "${code}") - else() - file(WRITE ${script_filename} "${code}") - endif() - set(command ${CMAKE_COMMAND} ${make} ${config} -P ${script_filename}) + file(GENERATE OUTPUT "${script}" CONTENT "${code}") + set(command ${CMAKE_COMMAND} ${make} ${config} -P ${script}) set(${cmd_var} "${command}" PARENT_SCOPE) endfunction() @@ -2257,7 +2242,8 @@ function(ExternalProject_Add_Step name step) LOG USES_TERMINAL ) - _ep_parse_arguments("${keywords}" ${name} _EP_${step}_ "${ARGN}") + _ep_parse_arguments(ExternalProject_Add_Step "${keywords}" + ${name} _EP_${step}_ "${ARGN}") get_property(independent TARGET ${name} PROPERTY _EP_${step}_INDEPENDENT) if(independent STREQUAL "") @@ -2368,8 +2354,7 @@ function(ExternalProject_Add_Step name step) # Wrap with log script? get_property(log TARGET ${name} PROPERTY _EP_${step}_LOG) if(command AND log) - set(genex_supported TRUE) - _ep_write_log_script(${name} ${step} ${genex_supported} command) + _ep_write_log_script(${name} ${step} command) endif() if("${command}" STREQUAL "") @@ -2501,6 +2486,27 @@ function(ExternalProject_Add_StepDependencies name step) 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_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} + ) +endfunction() + + function(_ep_is_dir_empty dir empty_var) file(GLOB gr "${dir}/*") if("${gr}" STREQUAL "") @@ -2511,277 +2517,114 @@ function(_ep_is_dir_empty dir empty_var) endfunction() function(_ep_get_git_submodules_recurse git_submodules_recurse) - - if(NOT DEFINED _EP_GIT_SUBMODULES_RECURSE OR _EP_GIT_SUBMODULES_RECURSE) - # The git submodule update '--recursive' flag requires git >= v1.6.5 - if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5) - message(FATAL_ERROR - "git version 1.6.5 or later required for --recursive flag with " - "'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'") - endif() - set(${git_submodules_recurse} "--recursive" PARENT_SCOPE) + # Checks for GIT_SUBMODULES_RECURSE property + # Default is ON, which sets git_submodules_recurse output variable to "--recursive" + # Otherwise, the output variable is set to an empty value "" + get_property(git_submodules_recurse_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES_RECURSE SET) + if(NOT git_submodules_recurse_set) + set(recurseFlag "--recursive") else() - set(${git_submodules_recurse} "" PARENT_SCOPE) - endif() - -endfunction() - -function(_ep_write_command_script - script_filename - commands - work_dir - genex_supported - quiet - have_commands_var) - - set(sep "${_EP_LIST_SEPARATOR}") - if(sep AND commands) - string(REPLACE "${sep}" "\\;" commands "${commands}") - endif() - _ep_replace_location_tags_from_vars(commands) - - file(READ - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/customcommand.cmake.in - exec_command_template - ) - set(script_content) - set(this_command) - foreach(token IN LISTS commands) - if(token STREQUAL "COMMAND") - if("${this_command}" STREQUAL "") - # Silently skip empty commands - continue() - endif() - string(CONFIGURE "${exec_command_template}" content @ONLY) - string(APPEND script_content "${content}") - set(this_command) + get_property(git_submodules_recurse_value TARGET ${name} PROPERTY _EP_GIT_SUBMODULES_RECURSE) + if(git_submodules_recurse_value) + set(recurseFlag "--recursive") else() - # Ensure we quote every token so we preserve empty items, quotes, etc - string(APPEND this_command " [==[${token}]==]") + set(recurseFlag "") endif() - endforeach() - - if(NOT "${this_command}" STREQUAL "") - string(CONFIGURE "${exec_command_template}" content @ONLY) - string(APPEND script_content "${content}") - endif() - - if(script_content STREQUAL "") - set(${have_commands_var} FALSE PARENT_SCOPE) - else() - set(${have_commands_var} TRUE PARENT_SCOPE) - file(READ - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/customcommand_preamble.cmake.in - exec_command_preamble - ) - string(CONFIGURE "${exec_command_preamble}" exec_command_preamble @ONLY) - string(PREPEND script_content "${exec_command_preamble}") endif() + set(${git_submodules_recurse} "${recurseFlag}" PARENT_SCOPE) - if(genex_supported) - # Only written at generation phase. This will only change the file's - # timestamp if the contents change. - file(GENERATE OUTPUT "${script_filename}" CONTENT "${script_content}") - else() - # Update the file immediately, needed if script has to be invoked in the - # configure phase (e.g. via FetchContent). We need to be careful to avoid - # updating the timestamp if the file contents don't change. The file(WRITE) - # command always updates the file, so avoid it if we don't need to call it. - set(doWrite TRUE) - if(EXISTS "${script_filename}") - file(READ "${script_filename}" existing_content) - if(existing_content STREQUAL script_content) - set(doWrite FALSE) - endif() - endif() - if(doWrite) - file(WRITE "${script_filename}" "${script_content}") - endif() + # The git submodule update '--recursive' flag requires git >= v1.6.5 + if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5) + message(FATAL_ERROR "error: git version 1.6.5 or later required for --recursive flag with 'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'") endif() - endfunction() -function(_ep_add_preconfigure_command name step) - string(TOUPPER "${step}" STEP) - set(uses_terminal "${_EP_USES_TERMINAL_${STEP}}") - if(uses_terminal) - set(uses_terminal TRUE) - else() - set(uses_terminal FALSE) - endif() - - # Pre-configure steps are expected to set their own work_dir - ExternalProject_Add_Step(${name} ${step} - INDEPENDENT TRUE - COMMENT "${_EPcomment_${STEP}}" - COMMAND ${_EPcommand_${STEP}} - ALWAYS ${_EPalways_${STEP}} - EXCLUDE_FROM_MAIN ${_EPexcludefrommain_${STEP}} - DEPENDS ${_EPdepends_${STEP}} - DEPENDEES ${_EPdependees_${STEP}} - USES_TERMINAL ${uses_terminal} - ) -endfunction() +function(_ep_add_download_command name) + ExternalProject_Get_Property(${name} source_dir stamp_dir download_dir tmp_dir) -# This function is an implementation detail of ExternalProject_Add() and -# _ep_do_preconfigure_steps_now(). -# -# The function expects keyword arguments to have already been parsed into -# variables of the form _EP_. It will populate the variable -# _EP_DOWNLOADED_FILE in the calling scope only if the download method is -# URL-based and extraction has been turned off. -# -# Variables will also be set in the calling scope to enable subsequently -# calling _ep_add_preconfigure_command() for the download step. -# -function(_ep_prepare_download name genex_supported) + get_property(cmd_set TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND SET) + get_property(cmd TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND) + get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY) + get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY) + get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY) + get_property(hg_repository TARGET ${name} PROPERTY _EP_HG_REPOSITORY ) + get_property(url TARGET ${name} PROPERTY _EP_URL) + get_property(fname TARGET ${name} PROPERTY _EP_DOWNLOAD_NAME) - set(stamp_dir "${_EP_STAMP_DIR}") - set(tmp_dir "${_EP_TMP_DIR}") - set(source_dir "${_EP_SOURCE_DIR}") - set(download_dir "${_EP_DOWNLOAD_DIR}") - set(quiet "${_EP_QUIET}") + # TODO: Perhaps file:// should be copied to download dir before extraction. + string(REGEX REPLACE "file://" "" url "${url}") + set(depends) set(comment) + set(work_dir) - # We handle the log setting directly here rather than deferring it to - # be handled by ExternalProject_Add_Step() - set(log "${_EP_LOG_DOWNLOAD}") - if(log) - set(script_filename ${tmp_dir}/${name}-download-impl.cmake) - set(log TRUE) - set(quiet FALSE) # Already quiet as a result of log being enabled - else() - set(script_filename ${tmp_dir}/${name}-download.cmake) - set(log FALSE) - endif() - - set(repo_info_file ${tmp_dir}/${name}-download-repoinfo.txt) - set(last_run_file ${stamp_dir}/${name}-download-lastrun.txt) - set(script_does_something TRUE) - - # We use configure_file() to write the repo_info_file below so that the - # file's timestamp is not updated if we don't change the contents of an - # existing file. - - if(DEFINED _EP_DOWNLOAD_COMMAND) + if(cmd_set) set(work_dir ${download_dir}) - set(repo_info_content -"method=custom -command=${_EP_DOWNLOAD_COMMAND} -source_dir=${source_dir} -work_dir=${work_dir} -") - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} - @ONLY - ) - - _ep_write_command_script( - "${script_filename}" - "${_EP_DOWNLOAD_COMMAND}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) - set(comment "Performing download step (custom command) for '${name}'") - - elseif(DEFINED _EP_CVS_REPOSITORY) + elseif(cvs_repository) find_package(CVS QUIET) if(NOT CVS_EXECUTABLE) message(FATAL_ERROR "error: could not find cvs for checkout of ${name}") endif() - if("${_EP_CVS_MODULE}" STREQUAL "") + get_target_property(cvs_module ${name} _EP_CVS_MODULE) + if(NOT cvs_module) message(FATAL_ERROR "error: no CVS_MODULE") endif() - set(repo_info_content -"method=cvs -repository=${_EP_CVS_REPOSITORY} -module=${_EP_CVS_MODULE} -tag=${_EP_CVS_TAG} -source_dir=${source_dir} -") + get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG) + + set(repository ${cvs_repository}) + set(module ${cvs_module}) + set(tag ${cvs_tag}) configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-cvsinfo.txt" @ONLY - ) + ) get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) - - set(cmd "${CVS_EXECUTABLE}" -d "${_EP_CVS_REPOSITORY}" -q - co ${_EP_CVS_TAG} -d "${src_name}" "${_EP_CVS_MODULE}" - ) - _ep_write_command_script( - "${script_filename}" - "${cmd}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) set(comment "Performing download step (CVS checkout) for '${name}'") - - elseif(DEFINED _EP_SVN_REPOSITORY) + set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q co ${cvs_tag} -d ${src_name} ${cvs_module}) + list(APPEND depends ${stamp_dir}/${name}-cvsinfo.txt) + elseif(svn_repository) find_package(Subversion QUIET) if(NOT Subversion_SVN_EXECUTABLE) message(FATAL_ERROR "error: could not find svn for checkout of ${name}") endif() - set(svn_repository "${_EP_SVN_REPOSITORY}") - set(svn_revision "${_EP_SVN_REVISION}") - set(svn_username "${_EP_SVN_USERNAME}") - set(svn_password "${_EP_SVN_PASSWORD}") - set(svn_trust_cert "${_EP_SVN_TRUST_CERT}") - - set(svn_options --non-interactive) - if(DEFINED _EP_SVN_USERNAME) - list(APPEND svn_options "--username=${svn_username}") - endif() - if(DEFINED _EP_SVN_PASSWORD) - list(APPEND svn_options "--password=${svn_password}") - endif() - if(svn_trust_cert) - list(APPEND svn_options --trust-server-cert) - endif() + get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION) + get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME) + get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD) + get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT) - set(repo_info_content -"method=svn -repository=${svn_repository} -user=${svn_username} -password=${svn_password} -revision=${svn_revision} -source_dir=${source_dir} -") + set(repository "${svn_repository} user=${svn_username} password=${svn_password}") + set(module) + set(tag ${svn_revision}) configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-svninfo.txt" @ONLY - ) + ) get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) - - set(cmd "${Subversion_SVN_EXECUTABLE}" co "${svn_repository}" - ${svn_revision} ${svn_options} "${src_name}" - ) - _ep_write_command_script( - "${script_filename}" - "${cmd}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) set(comment "Performing download step (SVN checkout) for '${name}'") - - elseif(DEFINED _EP_GIT_REPOSITORY) + set(svn_user_pw_args "") + if(DEFINED svn_username) + set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") + endif() + if(DEFINED svn_password) + set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") + endif() + if(svn_trust_cert) + set(svn_trust_cert_args --trust-server-cert) + endif() + set(cmd ${Subversion_SVN_EXECUTABLE} co ${svn_repository} ${svn_revision} + --non-interactive ${svn_trust_cert_args} ${svn_user_pw_args} ${src_name}) + list(APPEND depends ${stamp_dir}/${name}-svninfo.txt) + elseif(git_repository) # FetchContent gives us these directly, so don't try to recompute them if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) unset(CMAKE_MODULE_PATH) # Use CMake builtin find module @@ -2791,133 +2634,111 @@ source_dir=${source_dir} endif() endif() - set(git_tag "${_EP_GIT_TAG}") + _ep_get_git_submodules_recurse(git_submodules_recurse) + + get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) if(NOT git_tag) set(git_tag "master") endif() - set(git_remote_name "${_EP_GIT_REMOTE_NAME}") - if(NOT git_remote_name) - set(git_remote_name "origin") - endif() - set(git_init_submodules TRUE) - if(DEFINED _EP_GIT_SUBMODULES) - set(git_submodules "${_EP_GIT_SUBMODULES}") - if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") + get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET) + if(git_submodules_set) + get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES) + if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") set(git_init_submodules FALSE) endif() endif() - _ep_get_git_submodules_recurse(git_submodules_recurse) - set(tls_verify "${_EP_TLS_VERIFY}") + get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME) + if(NOT git_remote_name) + set(git_remote_name "origin") + endif() + + get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY) if("x${tls_verify}" STREQUAL "x" AND DEFINED CMAKE_TLS_VERIFY) set(tls_verify "${CMAKE_TLS_VERIFY}") endif() - set(git_shallow "${_EP_GIT_SHALLOW}") - set(git_progress "${_EP_GIT_PROGRESS}") - set(git_config "${_EP_GIT_CONFIG}") + get_property(git_shallow TARGET ${name} PROPERTY _EP_GIT_SHALLOW) + get_property(git_progress TARGET ${name} PROPERTY _EP_GIT_PROGRESS) + get_property(git_config TARGET ${name} PROPERTY _EP_GIT_CONFIG) # Make checkouts quiet when checking out a git hash (this avoids the # very noisy detached head message) list(PREPEND git_config advice.detachedHead=false) - # For the git clone operation, only the repository and remote should be - # recorded in a configured repository info file. If the repo or remote - # name changes, the clone script should be run again. But if only the tag + # For the download step, and the git clone operation, only the repository + # should be recorded in a configured RepositoryInfo file. If the repo + # changes, the clone script should be run again. But if only the tag # changes, avoid running the clone script again. Let the 'always' running # update step checkout the new tag. - set(repo_info_content -"method=git -repository=${_EP_GIT_REPOSITORY} -remote=${git_remote_name} -source_dir=${source_dir} -") + # + set(repository ${git_repository}) + set(module) + set(tag ${git_remote_name}) configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-gitinfo.txt" @ONLY - ) + ) get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) # Since git clone doesn't succeed if the non-empty source_dir exists, - # the script will delete the source directory and then call git clone. - _ep_write_gitclone_script( - "${script_filename}" - "${source_dir}" - "${GIT_EXECUTABLE}" - "${_EP_GIT_REPOSITORY}" - "${git_tag}" - "${git_remote_name}" - "${git_init_submodules}" - "${git_submodules_recurse}" - "${git_submodules}" - "${git_shallow}" - "${git_progress}" - "${git_config}" - "${src_name}" - "${work_dir}" - "${repo_info_file}" - "${last_run_file}" - "${tls_verify}" - "${quiet}" - ) + # create a cmake script to invoke as download command. + # The script will delete the source directory and then call git clone. + # + _ep_write_gitclone_script(${tmp_dir}/${name}-gitclone.cmake ${source_dir} + ${GIT_EXECUTABLE} ${git_repository} ${git_tag} ${git_remote_name} ${git_init_submodules} "${git_submodules_recurse}" "${git_submodules}" "${git_shallow}" "${git_progress}" "${git_config}" ${src_name} ${work_dir} + ${stamp_dir}/${name}-gitinfo.txt ${stamp_dir}/${name}-gitclone-lastrun.txt "${tls_verify}" + ) set(comment "Performing download step (git clone) for '${name}'") - - elseif(DEFINED _EP_HG_REPOSITORY) + set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitclone.cmake) + list(APPEND depends ${stamp_dir}/${name}-gitinfo.txt) + elseif(hg_repository) find_package(Hg QUIET) if(NOT HG_EXECUTABLE) message(FATAL_ERROR "error: could not find hg for clone of ${name}") endif() - set(hg_tag "${_EP_HG_TAG}") + get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG) if(NOT hg_tag) set(hg_tag "tip") endif() - # For the hg clone operation, only the repository should be recorded in a - # configured repository info file. If the repo changes, the clone script - # should be run again. But if only the tag changes, avoid running the - # clone script again. Let the 'always' running update step checkout the - # new tag. - set(repo_info_content -"method=hg -repository=${_EP_HG_REPOSITORY} -source_dir=${source_dir} -") + # For the download step, and the hg clone operation, only the repository + # should be recorded in a configured RepositoryInfo file. If the repo + # changes, the clone script should be run again. But if only the tag + # changes, avoid running the clone script again. Let the 'always' running + # update step checkout the new tag. + # + set(repository ${hg_repository}) + set(module) + set(tag) configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-hginfo.txt" @ONLY - ) + ) get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) # Since hg clone doesn't succeed if the non-empty source_dir exists, - # the script will delete the source directory and then call hg clone. - _ep_write_hgclone_script( - "${script_filename}" - "${source_dir}" - "${HG_EXECUTABLE}" - "${_EP_HG_REPOSITORY}" - "${hg_tag}" - "${src_name}" - "${work_dir}" - "${repo_info_file}" - "${last_run_file}" - "${quiet}" - ) + # create a cmake script to invoke as download command. + # The script will delete the source directory and then call hg clone. + # + _ep_write_hgclone_script(${tmp_dir}/${name}-hgclone.cmake ${source_dir} + ${HG_EXECUTABLE} ${hg_repository} ${hg_tag} ${src_name} ${work_dir} + ${stamp_dir}/${name}-hginfo.txt ${stamp_dir}/${name}-hgclone-lastrun.txt + ) set(comment "Performing download step (hg clone) for '${name}'") - - elseif(DEFINED _EP_URL) - set(url "${_EP_URL}") - # TODO: Perhaps file:// should be copied to download dir before extraction. - string(REGEX REPLACE "file://" "" url "${url}") - - set(hash "${_EP_URL_HASH}") + set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-hgclone.cmake) + list(APPEND depends ${stamp_dir}/${name}-hginfo.txt) + elseif(url) + get_filename_component(work_dir "${source_dir}" PATH) + get_property(hash TARGET ${name} PROPERTY _EP_URL_HASH) _ep_get_hash_regex(_ep_hash_regex) if(hash AND NOT "${hash}" MATCHES "${_ep_hash_regex}") _ep_get_hash_algos(_ep_hash_algos) @@ -2926,27 +2747,22 @@ source_dir=${source_dir} "but must be ALGO=value where ALGO is\n ${_ep_hash_algos}\n" "and value is a hex string.") endif() - set(md5 "${_EP_URL_MD5}") + get_property(md5 TARGET ${name} PROPERTY _EP_URL_MD5) if(md5 AND NOT "MD5=${md5}" MATCHES "${_ep_hash_regex}") message(FATAL_ERROR "URL_MD5 is set to\n ${md5}\nbut must be a hex string.") endif() if(md5 AND NOT hash) set(hash "MD5=${md5}") endif() - - set(repo_info_content -"method=url -url=${url} -hash=${hash} -source_dir=${source_dir} -") + set(repository "external project URL") + set(module "${url}") + set(tag "${hash}") configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-urlinfo.txt" @ONLY - ) - - set(fname "${_EP_DOWNLOAD_NAME}") + ) + list(APPEND depends ${stamp_dir}/${name}-urlinfo.txt) list(LENGTH url url_list_length) if(NOT "${url_list_length}" STREQUAL "1") @@ -2961,21 +2777,12 @@ source_dir=${source_dir} endif() if(IS_DIRECTORY "${url}") - get_filename_component(from_dir "${url}" ABSOLUTE) - _ep_write_copydir_script( - ${script_filename} - ${from_dir} - ${source_dir} - ) - set(steps "DIR copy") + get_filename_component(abs_dir "${url}" ABSOLUTE) + set(comment "Performing download step (DIR copy) for '${name}'") + set(cmd ${CMAKE_COMMAND} -E rm -rf ${source_dir} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir}) else() - set(no_extract "${_EP_DOWNLOAD_NO_EXTRACT}") - if(no_extract) - set(extract_script) - else() - set(extract_script "${tmp_dir}/extract-${name}.cmake") - endif() - + get_property(no_extract TARGET "${name}" PROPERTY _EP_DOWNLOAD_NO_EXTRACT) if("${url}" MATCHES "^[a-z]+://") # TODO: Should download and extraction be different steps? if("x${fname}" STREQUAL "x") @@ -2989,70 +2796,55 @@ source_dir=${source_dir} # Fall back to a default file name. The actual file name does not # matter because it is used only internally and our extraction tool # inspects the file content directly. If it turns out the wrong URL - # was given, that will be revealed when the download is attempted - # (during the build unless we are being invoked by FetchContent) - # which is an easier place for users to diagnose than an error here. + # was given that will be revealed during the build which is an easier + # place for users to diagnose than an error here anyway. set(fname "archive.tar") endif() string(REPLACE ";" "-" fname "${fname}") set(file ${download_dir}/${fname}) - _ep_write_downloadfile_script( - "${script_filename}" - "${url}" - "${file}" - "${_EP_TIMEOUT}" - "${_EP_INACTIVITY_TIMEOUT}" - "${_EP_DOWNLOAD_NO_PROGRESS}" - "${hash}" - "${_EP_TLS_VERIFY}" - "${_EP_TLS_CAINFO}" - "${_EP_HTTP_USERNAME}:${_EP_HTTP_PASSWORD}" - "${_EP_HTTP_HEADER}" - "${_EP_NETRC}" - "${_EP_NETRC_FILE}" - "${extract_script}" - "${quiet}" - ) - if(no_extract) + get_property(timeout TARGET ${name} PROPERTY _EP_TIMEOUT) + get_property(inactivity_timeout TARGET ${name} PROPERTY _EP_INACTIVITY_TIMEOUT) + get_property(no_progress TARGET ${name} PROPERTY _EP_DOWNLOAD_NO_PROGRESS) + get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY) + get_property(tls_cainfo TARGET ${name} PROPERTY _EP_TLS_CAINFO) + get_property(netrc TARGET ${name} PROPERTY _EP_NETRC) + get_property(netrc_file TARGET ${name} PROPERTY _EP_NETRC_FILE) + get_property(http_username TARGET ${name} PROPERTY _EP_HTTP_USERNAME) + 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}") + set(cmd ${CMAKE_COMMAND} -P "${download_script}" + COMMAND) + if (no_extract) set(steps "download and verify") - else() + else () set(steps "download, verify and extract") - endif() + endif () + set(comment "Performing download step (${steps}) for '${name}'") + file(WRITE "${stamp_dir}/verify-${name}.cmake" "") # already verified by 'download_script' else() set(file "${url}") - _ep_write_verifyfile_script( - "${script_filename}" - "${file}" - "${hash}" - "${extract_script}" - "${quiet}" - ) - if(no_extract) + if (no_extract) set(steps "verify") - else() + else () set(steps "verify and extract") - endif() - endif() - - if(no_extract) - set(_EP_DOWNLOADED_FILE ${file} PARENT_SCOPE) - else() - # This will be pulled in by the download/verify script written above - _ep_write_extractfile_script( - "${extract_script}" - "${name}" - "${file}" - "${source_dir}" - "${quiet}" - ) + endif () + set(comment "Performing download step (${steps}) for '${name}'") + _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}") + list(APPEND cmd COMMAND ${CMAKE_COMMAND} -P ${stamp_dir}/extract-${name}.cmake) + else () + set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file}) + endif () endif() - set(comment "Performing download step (${steps}) for '${name}'") - else() _ep_is_dir_empty("${source_dir}" empty) if(${empty}) - message(FATAL_ERROR + message(SEND_ERROR "No download info given for '${name}' and its source directory:\n" " ${source_dir}\n" "is not an existing non-empty directory. Please specify one of:\n" @@ -3065,149 +2857,105 @@ source_dir=${source_dir} " * CVS_REPOSITORY and CVS_MODULE" ) endif() - set(repo_info_content -"method=source_dir -source_dir=${source_dir} -") - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} - @ONLY - ) - - set(comment "Skipping download step (SOURCE_DIR given) for '${name}'") - set(script_does_something FALSE) endif() - # Provide variables that can be used later to create a custom command or - # invoke the step directly - if(script_does_something) - set(cmd ${CMAKE_COMMAND} -P ${script_filename}) - if(log) - _ep_write_log_script(${name} download "${genex_supported}" cmd) - endif() - set(depends ${repo_info_file}) + get_property(log TARGET ${name} PROPERTY _EP_LOG_DOWNLOAD) + if(log) + set(log LOG 1) else() - set(cmd) - set(depends) - string(REPLACE "Performing" "Skipping" comment "${comment}") + set(log "") endif() - set(_EPcomment_DOWNLOAD "${comment}" PARENT_SCOPE) - set(_EPcommand_DOWNLOAD "${cmd}" PARENT_SCOPE) - set(_EPalways_DOWNLOAD FALSE PARENT_SCOPE) - set(_EPexcludefrommain_DOWNLOAD FALSE PARENT_SCOPE) - set(_EPdepends_DOWNLOAD "${depends}" PARENT_SCOPE) - set(_EPdependees_DOWNLOAD mkdir PARENT_SCOPE) + get_property(uses_terminal TARGET ${name} PROPERTY + _EP_USES_TERMINAL_DOWNLOAD) + if(uses_terminal) + set(uses_terminal USES_TERMINAL 1) + else() + set(uses_terminal "") + endif() + set(__cmdQuoted) + foreach(__item IN LISTS cmd) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + cmake_language(EVAL CODE " + ExternalProject_Add_Step(\${name} download + INDEPENDENT TRUE + COMMENT \${comment} + COMMAND ${__cmdQuoted} + WORKING_DIRECTORY \${work_dir} + DEPENDS \${depends} + DEPENDEES mkdir + ${log} + ${uses_terminal} + )" + ) endfunction() -function(_ep_get_update_disconnected var) - if(DEFINED _EP_UPDATE_DISCONNECTED) - set(update_disconnected "${_EP_UPDATE_DISCONNECTED}") +function(_ep_get_update_disconnected var name) + get_property(update_disconnected_set TARGET ${name} PROPERTY _EP_UPDATE_DISCONNECTED SET) + if(update_disconnected_set) + get_property(update_disconnected TARGET ${name} PROPERTY _EP_UPDATE_DISCONNECTED) else() get_property(update_disconnected DIRECTORY PROPERTY EP_UPDATE_DISCONNECTED) endif() set(${var} "${update_disconnected}" PARENT_SCOPE) endfunction() -# This function is an implementation detail of ExternalProject_Add() and -# _ep_do_preconfigure_steps_now(). -# -# The function expects keyword arguments to have already been parsed into -# variables of the form _EP_. -# -# Variables will also be set in the calling scope to enable subsequently -# calling _ep_add_preconfigure_command() for the updated step. -# -function(_ep_prepare_update name genex_supported) - - set(tmp_dir "${_EP_TMP_DIR}") - set(source_dir "${_EP_SOURCE_DIR}") - set(quiet "${_EP_QUIET}") +function(_ep_add_update_command name) + ExternalProject_Get_Property(${name} source_dir tmp_dir) - set(comment) + get_property(cmd_set TARGET ${name} PROPERTY _EP_UPDATE_COMMAND SET) + get_property(cmd TARGET ${name} PROPERTY _EP_UPDATE_COMMAND) + get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY) + get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY) + get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY) + get_property(hg_repository TARGET ${name} PROPERTY _EP_HG_REPOSITORY ) - _ep_get_update_disconnected(update_disconnected) + _ep_get_update_disconnected(update_disconnected ${name}) - # We handle the log setting directly here rather than deferring it to - # be handled by ExternalProject_Add_Step() - set(log "${_EP_LOG_UPDATE}") - if(log) - set(script_filename ${tmp_dir}/${name}-update-impl.cmake) - set(log TRUE) - set(quiet FALSE) # Already quiet as a result of log being enabled - else() - set(script_filename ${tmp_dir}/${name}-update.cmake) - set(log FALSE) - endif() + set(work_dir) + set(comment) + set(always) - if(DEFINED _EP_UPDATE_COMMAND) + if(cmd_set) set(work_dir ${source_dir}) - _ep_write_command_script( - "${script_filename}" - "${_EP_UPDATE_COMMAND}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) - set(comment "Performing update step (custom command) for '${name}'") - - elseif(DEFINED _EP_CVS_REPOSITORY) + if(NOT "x${cmd}" STREQUAL "x") + set(always 1) + endif() + elseif(cvs_repository) if(NOT CVS_EXECUTABLE) message(FATAL_ERROR "error: could not find cvs for update of ${name}") endif() - set(work_dir ${source_dir}) - set(cmd "${CVS_EXECUTABLE}" -d "${_EP_CVS_REPOSITORY}" -q - up -dP ${_EP_CVS_TAG} - ) - _ep_write_command_script( - "${script_filename}" - "${cmd}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) set(comment "Performing update step (CVS update) for '${name}'") - - elseif(DEFINED _EP_SVN_REPOSITORY) + get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG) + set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag}) + set(always 1) + elseif(svn_repository) if(NOT Subversion_SVN_EXECUTABLE) message(FATAL_ERROR "error: could not find svn for update of ${name}") endif() - - set(svn_revision "${_EP_SVN_REVISION}") - set(svn_username "${_EP_SVN_USERNAME}") - set(svn_password "${_EP_SVN_PASSWORD}") - set(svn_trust_cert "${_EP_SVN_TRUST_CERT}") - - set(svn_options --non-interactive) - if(DEFINED _EP_SVN_USERNAME) - list(APPEND svn_options "--username=${svn_username}") + set(work_dir ${source_dir}) + set(comment "Performing update step (SVN update) for '${name}'") + get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION) + get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME) + get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD) + get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT) + set(svn_user_pw_args "") + if(DEFINED svn_username) + set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") endif() - if(DEFINED _EP_SVN_PASSWORD) - list(APPEND svn_options "--password=${svn_password}") + if(DEFINED svn_password) + set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") endif() if(svn_trust_cert) - list(APPEND svn_options --trust-server-cert) + set(svn_trust_cert_args --trust-server-cert) endif() - - set(work_dir ${source_dir}) - set(cmd "${Subversion_SVN_EXECUTABLE}" up ${svn_revision} ${svn_options}) - - _ep_write_command_script( - "${script_filename}" - "${cmd}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) - set(comment "Performing update step (SVN update) for '${name}'") - - elseif(DEFINED _EP_GIT_REPOSITORY) + set(cmd ${Subversion_SVN_EXECUTABLE} up ${svn_revision} + --non-interactive ${svn_trust_cert_args} ${svn_user_pw_args}) + set(always 1) + elseif(git_repository) # FetchContent gives us these directly, so don't try to recompute them if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) unset(CMAKE_MODULE_PATH) # Use CMake builtin find module @@ -3216,27 +2964,27 @@ function(_ep_prepare_update name genex_supported) message(FATAL_ERROR "error: could not find git for fetch of ${name}") endif() endif() - - set(git_tag "${_EP_GIT_TAG}") + set(work_dir ${source_dir}) + set(comment "Performing update step for '${name}'") + get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) if(NOT git_tag) set(git_tag "master") endif() - - set(git_remote_name "${_EP_GIT_REMOTE_NAME}") + get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME) if(NOT git_remote_name) set(git_remote_name "origin") endif() set(git_init_submodules TRUE) - if(DEFINED _EP_GIT_SUBMODULES) - set(git_submodules "${_EP_GIT_SUBMODULES}") + get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET) + if(git_submodules_set) + get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES) if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") set(git_init_submodules FALSE) endif() endif() - _ep_get_git_submodules_recurse(git_submodules_recurse) - set(git_update_strategy "${_EP_GIT_REMOTE_UPDATE_STRATEGY}") + get_property(git_update_strategy TARGET ${name} PROPERTY _EP_GIT_REMOTE_UPDATE_STRATEGY) if(NOT git_update_strategy) set(git_update_strategy "${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}") endif() @@ -3248,28 +2996,23 @@ function(_ep_prepare_update name genex_supported) message(FATAL_ERROR "'${git_update_strategy}' is not one of the supported strategies: ${strategies}") endif() - set(work_dir ${source_dir}) - _ep_write_gitupdate_script( - "${script_filename}" - "${GIT_EXECUTABLE}" - "${git_tag}" - "${git_remote_name}" - "${git_init_submodules}" - "${git_submodules_recurse}" - "${git_submodules}" - "${_EP_GIT_REPOSITORY}" - "${work_dir}" - "${git_update_strategy}" - "${quiet}" - ) - set(script_does_something TRUE) - set(comment "Performing update step (git update) for '${name}'") + _ep_get_git_submodules_recurse(git_submodules_recurse) - elseif(DEFINED _EP_HG_REPOSITORY) + _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) if(NOT HG_EXECUTABLE) message(FATAL_ERROR "error: could not find hg for pull of ${name}") endif() - + set(work_dir ${source_dir}) + set(comment "Performing update step (hg pull) for '${name}'") + get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG) + if(NOT hg_tag) + set(hg_tag "tip") + endif() if("${HG_VERSION_STRING}" STREQUAL "2.1") message(WARNING "Mercurial 2.1 does not distinguish an empty pull from a failed pull: http://mercurial.selenic.com/wiki/UpgradeNotes#A2.1.1:_revert_pull_return_code_change.2C_compile_issue_on_OS_X @@ -3277,117 +3020,87 @@ function(_ep_prepare_update name genex_supported) Update to Mercurial >= 2.1.1. ") endif() + set(cmd ${HG_EXECUTABLE} pull + COMMAND ${HG_EXECUTABLE} update ${hg_tag} + ) + set(always 1) + endif() - set(hg_tag "${_EP_HG_TAG}") - if(NOT hg_tag) - set(hg_tag "tip") - endif() - - set(work_dir ${source_dir}) - _ep_write_hgupdate_script( - "${script_filename}" - "${HG_EXECUTABLE}" - "${hg_tag}" - "${work_dir}" - "${quiet}" - ) - set(script_does_something TRUE) - set(comment "Performing update step (hg pull) for '${name}'") - + get_property(log TARGET ${name} PROPERTY _EP_LOG_UPDATE) + if(log) + set(log LOG 1) else() - set(script_does_something FALSE) + set(log "") endif() - # Provide variables that can be used later to create a custom command or - # invoke the step directly - if(script_does_something) - set(always TRUE) - set(cmd ${CMAKE_COMMAND} -P ${script_filename}) - if(log) - _ep_write_log_script(${name} update "${genex_supported}" cmd) - endif() + get_property(uses_terminal TARGET ${name} PROPERTY + _EP_USES_TERMINAL_UPDATE) + if(uses_terminal) + set(uses_terminal USES_TERMINAL 1) else() - set(always FALSE) - set(cmd) + set(uses_terminal "") endif() - set(_EPcomment_UPDATE "${comment}" PARENT_SCOPE) - set(_EPcommand_UPDATE "${cmd}" PARENT_SCOPE) - set(_EPalways_UPDATE "${always}" PARENT_SCOPE) - set(_EPexcludefrommain_UPDATE "${update_disconnected}" PARENT_SCOPE) - set(_EPdepends_UPDATE "" PARENT_SCOPE) - set(_EPdependees_UPDATE download PARENT_SCOPE) + set(__cmdQuoted) + foreach(__item IN LISTS cmd) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + cmake_language(EVAL CODE " + ExternalProject_Add_Step(${name} update + INDEPENDENT TRUE + COMMENT \${comment} + COMMAND ${__cmdQuoted} + ALWAYS \${always} + EXCLUDE_FROM_MAIN \${update_disconnected} + WORKING_DIRECTORY \${work_dir} + DEPENDEES download + ${log} + ${uses_terminal} + )" + ) endfunction() -# This function is an implementation detail of ExternalProject_Add() and -# _ep_do_preconfigure_steps_now(). -# -# The function expects keyword arguments to have already been parsed into -# variables of the form _EP_. -# -# Variables will also be set in the calling scope to enable subsequently -# calling _ep_add_preconfigure_command() for the patch step. -# -function(_ep_prepare_patch name genex_supported) - set(tmp_dir "${_EP_TMP_DIR}") - set(source_dir "${_EP_SOURCE_DIR}") - set(quiet "${_EP_QUIET}") +function(_ep_add_patch_command name) + ExternalProject_Get_Property(${name} source_dir) - _ep_get_update_disconnected(update_disconnected) - if(update_disconnected) - set(patch_dep download) - else() - set(patch_dep update) + get_property(cmd_set TARGET ${name} PROPERTY _EP_PATCH_COMMAND SET) + get_property(cmd TARGET ${name} PROPERTY _EP_PATCH_COMMAND) + + set(work_dir) + + if(cmd_set) + set(work_dir ${source_dir}) endif() - # We handle the log setting directly here rather than deferring it to - # be handled by ExternalProject_Add_Step() - set(log "${_EP_LOG_PATCH}") + get_property(log TARGET ${name} PROPERTY _EP_LOG_PATCH) if(log) - set(script_filename ${tmp_dir}/${name}-patch-impl.cmake) - set(log TRUE) - set(quiet FALSE) # Already quiet as a result of log being enabled + set(log LOG 1) else() - set(script_filename ${tmp_dir}/${name}-patch.cmake) - set(log FALSE) + set(log "") endif() - if(DEFINED _EP_PATCH_COMMAND) - set(work_dir "${source_dir}") - _ep_write_command_script( - "${script_filename}" - "${_EP_PATCH_COMMAND}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) - if(script_does_something) - set(cmd ${CMAKE_COMMAND} -P ${script_filename}) - if(log) - _ep_write_log_script(${name} patch "${genex_supported}" cmd) - endif() - set(comment "Performing patch step (custom command) for '${name}'") - else() - set(cmd) - set(comment "Skipping patch step (empty custom command) for '${name}'") - endif() + _ep_get_update_disconnected(update_disconnected ${name}) + if(update_disconnected) + set(patch_dep download) else() - set(cmd) - set(comment "Skipping patch step (no custom command) for '${name}'") + set(patch_dep update) endif() - # Provide variables that can be used later to create a custom command or - # invoke the step directly - set(_EPcomment_PATCH "${comment}" PARENT_SCOPE) - set(_EPcommand_PATCH "${cmd}" PARENT_SCOPE) - set(_EPalways_PATCH FALSE PARENT_SCOPE) - set(_EPexcludefrommain_PATCH FALSE PARENT_SCOPE) - set(_EPdepends_PATCH "" PARENT_SCOPE) - set(_EPdependees_PATCH "${patch_dep}" PARENT_SCOPE) - + set(__cmdQuoted) + foreach(__item IN LISTS cmd) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + cmake_language(EVAL CODE " + ExternalProject_Add_Step(${name} patch + INDEPENDENT TRUE + COMMAND ${__cmdQuoted} + WORKING_DIRECTORY \${work_dir} + DEPENDEES \${patch_dep} + ${log} + )" + ) endfunction() function(_ep_get_file_deps var name) @@ -3497,11 +3210,7 @@ function(_ep_extract_configure_command var name) if(has_cmake_cache_default_args) _ep_command_line_to_initial_cache(script_initial_cache_default "${cmake_cache_default_args}" 0) endif() - _ep_write_initial_cache( - ${name} - "${_ep_cache_args_script}" - "${script_initial_cache_force}${script_initial_cache_default}" - ) + _ep_write_initial_cache(${name} "${_ep_cache_args_script}" "${script_initial_cache_force}${script_initial_cache_default}") list(APPEND cmd "-C${_ep_cache_args_script}") _ep_replace_location_tags(${name} _ep_cache_args_script) set(_ep_cache_args_script @@ -3533,11 +3242,10 @@ 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 # - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/cfgcmd.txt.in - ${tmp_dir}/${name}-cfgcmd.txt - @ONLY - ) + 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) list(APPEND file_deps ${tmp_dir}/${name}-cfgcmd.txt) list(APPEND file_deps ${_ep_cache_args_script}) @@ -3748,8 +3456,51 @@ function(_ep_add_test_command name) endif() endfunction() -macro(_ep_get_add_keywords out_var) - set(${out_var} + +function(ExternalProject_Add name) + cmake_policy(GET CMP0097 _EP_CMP0097 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + cmake_policy(GET CMP0114 cmp0114 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12 AND NOT cmp0114 STREQUAL "NEW") + message(AUTHOR_WARNING + "Policy CMP0114 is not set to NEW. " + "In order to support the Xcode \"new build system\", " + "this project must be updated to set policy CMP0114 to NEW." + "\n" + "Since CMake is generating for the Xcode \"new build system\", " + "ExternalProject_Add will use policy CMP0114's NEW behavior anyway, " + "but the generated build system may not match what the project intends." + ) + set(cmp0114 "NEW") + endif() + + _ep_get_configuration_subdir_suffix(cfgdir) + + # Add a custom target for the external project. + set(cmf_dir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles) + _ep_get_complete_stampfile(${name} complete_stamp_file) + + cmake_policy(PUSH) + if(cmp0114 STREQUAL "NEW") + # To implement CMP0114 NEW behavior with Makefile generators, + # we need CMP0113 NEW behavior. + cmake_policy(SET CMP0113 NEW) + endif() + # The "ALL" option to add_custom_target just tells it to not set the + # EXCLUDE_FROM_ALL target property. Later, if the EXCLUDE_FROM_ALL + # argument was passed, we explicitly set it for the target. + add_custom_target(${name} ALL DEPENDS ${complete_stamp_file}) + cmake_policy(POP) + set_property(TARGET ${name} PROPERTY _EP_IS_EXTERNAL_PROJECT 1) + set_property(TARGET ${name} PROPERTY LABELS ${name}) + set_property(TARGET ${name} PROPERTY FOLDER "ExternalProjectTargets/${name}") + + set_property(TARGET ${name} PROPERTY _EP_CMP0114 "${cmp0114}") + + set(keywords # # Directory options # @@ -3878,145 +3629,14 @@ macro(_ep_get_add_keywords out_var) # LIST_SEPARATOR ) -endmacro() - - -# Internal function called by FetchContent to populate immediately. -# It only executes steps up to and including "patch". It takes the same -# arguments as ExternalProject_Add() plus one additional argument: QUIET. -# -# Not to be used outside of CMake. -# -function(_ep_do_preconfigure_steps_now name) - - cmake_policy(GET CMP0097 _EP_CMP0097 - PARENT_SCOPE # undocumented, do not use outside of CMake - ) - - set(genex_supported FALSE) - - _ep_get_add_keywords(keywords) - _ep_parse_arguments_to_vars("${keywords};QUIET" ${name} _EP_ "${ARGN}") - - _ep_get_update_disconnected(update_disconnected) - - _ep_prepare_directories(${name}) - _ep_prepare_download(${name} ${genex_supported}) - _ep_prepare_update(${name} ${genex_supported}) - _ep_prepare_patch(${name} ${genex_supported}) - - set(stamp_dir "${_EP_STAMP_DIR}") - set(tmp_dir "${_EP_TMP_DIR}") - - # Once any step has to run, all later steps have to be run too - set(need_to_run FALSE) - foreach(step IN ITEMS download update patch) - if(update_disconnected AND "${step}" STREQUAL "update") - continue() - endif() - - string(TOUPPER "${step}" STEP) - if("${_EPcommand_${STEP}}" STREQUAL "") - continue() - endif() - - set(stamp_file "${stamp_dir}/${name}-${step}") - set(script_file ${tmp_dir}/${name}-${step}.cmake) - - if(NOT EXISTS ${stamp_file}) - set(need_to_run TRUE) - endif() - - if(NOT need_to_run) - foreach(dep_file ${script_file} ${_EPdepends_${STEP}}) - # IS_NEWER_THAN is also true if the timestamps are the same. On some - # file systems, we only have second resolution timestamps and the - # likelihood of having the same timestamp is high. Use the negative - # form to ensure we actually get a true "is newer than" test. - if(NOT EXISTS ${dep_file} OR - NOT ${stamp_file} IS_NEWER_THAN ${dep_file}) - set(need_to_run TRUE) - break() - endif() - endforeach() - endif() - - if(need_to_run) - include(${script_file}) - file(TOUCH ${stamp_file}) - endif() - endforeach() - - if("${_EP_DOWNLOAD_NO_EXTRACT}") - file(COPY "${_EP_DOWNLOADED_FILE}" DESTINATION "${_EP_SOURCE_DIR}") - endif() - -endfunction() - -function(ExternalProject_Add name) - cmake_policy(GET CMP0097 _EP_CMP0097 - PARENT_SCOPE # undocumented, do not use outside of CMake - ) - cmake_policy(GET CMP0114 cmp0114 - PARENT_SCOPE # undocumented, do not use outside of CMake - ) - if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12 AND NOT cmp0114 STREQUAL "NEW") - message(AUTHOR_WARNING - "Policy CMP0114 is not set to NEW. " - "In order to support the Xcode \"new build system\", " - "this project must be updated to set policy CMP0114 to NEW." - "\n" - "Since CMake is generating for the Xcode \"new build system\", " - "ExternalProject_Add will use policy CMP0114's NEW behavior anyway, " - "but the generated build system may not match what the project intends." - ) - set(cmp0114 "NEW") - endif() - - set(genex_supported TRUE) - - _ep_get_add_keywords(keywords) - _ep_parse_arguments_to_vars("${keywords}" ${name} _EP_ "${ARGN}") - _ep_prepare_directories(${name}) - _ep_prepare_download(${name} ${genex_supported}) - _ep_prepare_update(${name} ${genex_supported}) - _ep_prepare_patch(${name} ${genex_supported}) - - _ep_get_configuration_subdir_suffix(cfgdir) - - # Add a custom target for the external project. - set(cmf_dir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles) - _ep_get_complete_stampfile(${name} complete_stamp_file) - - cmake_policy(PUSH) - if(cmp0114 STREQUAL "NEW") - # To implement CMP0114 NEW behavior with Makefile generators, - # we need CMP0113 NEW behavior. - cmake_policy(SET CMP0113 NEW) - endif() - # The "ALL" option to add_custom_target just tells it to not set the - # EXCLUDE_FROM_ALL target property. Later, if the EXCLUDE_FROM_ALL - # argument was passed, we explicitly set it for the target. - add_custom_target(${name} ALL DEPENDS ${complete_stamp_file}) - cmake_policy(POP) - set_property(TARGET ${name} PROPERTY _EP_IS_EXTERNAL_PROJECT 1) - set_property(TARGET ${name} PROPERTY LABELS ${name}) - set_property(TARGET ${name} PROPERTY FOLDER "ExternalProjectTargets/${name}") - - set_property(TARGET ${name} PROPERTY _EP_CMP0114 "${cmp0114}") - - # Transfer the arguments to the target as target properties. These are - # read by the various steps, potentially from different scopes. - foreach(key IN LISTS keywords ITEMS DOWNLOADED_FILE) - if(DEFINED _EP_${key}) - set_property(TARGET ${name} PROPERTY _EP_${key} "${_EP_${key}}") - endif() - endforeach() - + _ep_parse_arguments(ExternalProject_Add "${keywords}" ${name} _EP_ "${ARGN}") + _ep_set_directories(${name}) _ep_get_step_stampfile(${name} "done" done_stamp_file) _ep_get_step_stampfile(${name} "install" install_stamp_file) - if(arg_EXCLUDE_FROM_ALL) + # Set the EXCLUDE_FROM_ALL target property if required. + get_property(exclude_from_all TARGET ${name} PROPERTY _EP_EXCLUDE_FROM_ALL) + if(exclude_from_all) set_property(TARGET ${name} PROPERTY EXCLUDE_FROM_ALL TRUE) endif() @@ -4057,10 +3677,10 @@ function(ExternalProject_Add name) # The target depends on the output of the final step. # (Already set up above in the DEPENDS of the add_custom_target command.) # - _ep_add_preconfigure_command(${name} mkdir) - _ep_add_preconfigure_command(${name} download) - _ep_add_preconfigure_command(${name} update) - _ep_add_preconfigure_command(${name} patch) + _ep_add_mkdir_command(${name}) + _ep_add_download_command(${name}) + _ep_add_update_command(${name}) + _ep_add_patch_command(${name}) _ep_add_configure_command(${name}) _ep_add_build_command(${name}) _ep_add_install_command(${name}) diff --git a/Modules/ExternalProject/RepositoryInfo.txt.in b/Modules/ExternalProject/RepositoryInfo.txt.in deleted file mode 100644 index d82f04c..0000000 --- a/Modules/ExternalProject/RepositoryInfo.txt.in +++ /dev/null @@ -1 +0,0 @@ -@repo_info_content@ diff --git a/Modules/ExternalProject/captured_process_setup.cmake b/Modules/ExternalProject/captured_process_setup.cmake deleted file mode 100644 index 9c8abb1..0000000 --- a/Modules/ExternalProject/captured_process_setup.cmake +++ /dev/null @@ -1,55 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -if(quiet) - set(capture_output - OUTPUT_VARIABLE out_var - ERROR_VARIABLE out_var - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_STRIP_TRAILING_WHITESPACE - ) - set(capture_error_only - ERROR_VARIABLE out_var - ERROR_STRIP_TRAILING_WHITESPACE - ) -else() - unset(capture_output) - unset(capture_error_only) -endif() - -set(out_var "") -set(accumulated_output "") - -macro(_ep_message_quiet_capture mode) - if("${mode}" STREQUAL "FATAL_ERROR") - string(JOIN "" detail "${ARGN}") - if(NOT detail STREQUAL "" AND NOT accumulated_output STREQUAL "") - string(PREPEND detail "\n") - endif() - message(FATAL_ERROR "${accumulated_output}${detail}") - endif() - - if(quiet) - if("${mode}" MATCHES "WARNING") - # We can't provide the full CMake backtrace, but we can at least record - # the warning message with a sensible prefix - string(APPEND accumulated_output "${mode}: ") - endif() - string(APPEND accumulated_output "${ARGN}\n") - else() - message(${mode} ${ARGN}) - endif() -endmacro() - -macro(_ep_accumulate_captured_output) - if(NOT "${out_var}" STREQUAL "") - string(APPEND accumulated_output "${out_var}\n") - endif() -endmacro() - -macro(_ep_command_check_result result) - _ep_accumulate_captured_output() - if(result) - _ep_message_quiet_capture(FATAL_ERROR ${ARGN}) - endif() -endmacro() diff --git a/Modules/ExternalProject/cfgcmd.txt.in b/Modules/ExternalProject/cfgcmd.txt.in deleted file mode 100644 index b3f09ef..0000000 --- a/Modules/ExternalProject/cfgcmd.txt.in +++ /dev/null @@ -1 +0,0 @@ -cmd='@cmd@' diff --git a/Modules/ExternalProject/copydir.cmake.in b/Modules/ExternalProject/copydir.cmake.in deleted file mode 100644 index 5dd3891..0000000 --- a/Modules/ExternalProject/copydir.cmake.in +++ /dev/null @@ -1,10 +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) - -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/customcommand.cmake.in b/Modules/ExternalProject/customcommand.cmake.in deleted file mode 100644 index d41f31b..0000000 --- a/Modules/ExternalProject/customcommand.cmake.in +++ /dev/null @@ -1,8 +0,0 @@ - -execute_process( - COMMAND @this_command@ - WORKING_DIRECTORY "@work_dir@" - RESULT_VARIABLE result - ${capture_output} -) -_ep_command_check_result(result) diff --git a/Modules/ExternalProject/customcommand_preamble.cmake.in b/Modules/ExternalProject/customcommand_preamble.cmake.in deleted file mode 100644 index ae4fec6..0000000 --- a/Modules/ExternalProject/customcommand_preamble.cmake.in +++ /dev/null @@ -1,8 +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) - -set(quiet "@quiet@") -set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") -include(${script_dir}/captured_process_setup.cmake) diff --git a/Modules/ExternalProject/download.cmake.in b/Modules/ExternalProject/download.cmake.in deleted file mode 100644 index c8d2f28..0000000 --- a/Modules/ExternalProject/download.cmake.in +++ /dev/null @@ -1,205 +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) - -set(quiet "@quiet@") -set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") -include(${script_dir}/captured_process_setup.cmake) - -function(check_file_hash has_hash hash_is_good) - if("${has_hash}" STREQUAL "") - _ep_message_quiet_capture(FATAL_ERROR "has_hash Can't be empty") - endif() - - if("${hash_is_good}" STREQUAL "") - _ep_message_quiet_capture(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) - - _ep_message_quiet_capture(STATUS "verifying file... - file='@LOCAL@'") - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - - file("@ALGO@" "@LOCAL@" actual_value) - - if(NOT "${actual_value}" STREQUAL "@EXPECT_VALUE@") - set("${hash_is_good}" FALSE PARENT_SCOPE) - _ep_message_quiet_capture(STATUS "@ALGO@ hash of - @LOCAL@ - does not match expected value - expected: '@EXPECT_VALUE@' - actual: '${actual_value}'") - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - 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) - _ep_message_quiet_capture(STATUS "Retrying...") - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - 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() - - _ep_message_quiet_capture(STATUS - "Retry after ${sleep_seconds} seconds (attempt #${attempt}) ..." - ) - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - - 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) - _ep_message_quiet_capture(STATUS -"File already exists and hash match (skip download): - file='@LOCAL@' - @ALGO@='@EXPECT_VALUE@'" - ) - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - return() - else() - _ep_message_quiet_capture(STATUS - "File already exists but hash mismatch. Removing..." - ) - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - file(REMOVE "@LOCAL@") - endif() - else() - _ep_message_quiet_capture(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) - - _ep_message_quiet_capture(STATUS "Downloading... - dst='@LOCAL@' - timeout='@TIMEOUT_MSG@' - inactivity timeout='@INACTIVITY_TIMEOUT_MSG@'" - ) - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - 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) - _ep_message_quiet_capture(STATUS "Using src='${url}'") - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - - @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) - _ep_message_quiet_capture(STATUS "Hash mismatch, removing...") - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - file(REMOVE "@LOCAL@") - else() - _ep_message_quiet_capture(STATUS "Downloading... done") - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - 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() - - _ep_message_quiet_capture(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 deleted file mode 100644 index d46de73..0000000 --- a/Modules/ExternalProject/extractfile.cmake.in +++ /dev/null @@ -1,73 +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) - -set(quiet "@quiet@") -set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") -include(${script_dir}/captured_process_setup.cmake) - -# Make file names absolute: -# -get_filename_component(filename "@filename@" ABSOLUTE) -get_filename_component(directory "@directory@" ABSOLUTE) - -_ep_message_quiet_capture(STATUS "extracting... - src='${filename}' - dst='${directory}'" -) - -if(NOT EXISTS "${filename}") - _ep_message_quiet_capture(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: -# -_ep_message_quiet_capture(STATUS "extracting... [tar @args@]") -execute_process(COMMAND ${CMAKE_COMMAND} -E tar @args@ ${filename} - WORKING_DIRECTORY ${ut_dir} - RESULT_VARIABLE rv - ${capture_output} -) -_ep_accumulate_captured_output() - -if(NOT rv EQUAL 0) - _ep_message_quiet_capture(STATUS "extracting... [error clean up]") - file(REMOVE_RECURSE "${ut_dir}") - _ep_message_quiet_capture(FATAL_ERROR "Extract of '${filename}' failed") -endif() - -# Analyze what came out of the tar file: -# -_ep_message_quiet_capture(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: -# -_ep_message_quiet_capture(STATUS "extracting... [rename]") -file(REMOVE_RECURSE ${directory}) -get_filename_component(contents ${contents} ABSOLUTE) -file(RENAME ${contents} ${directory}) - -# Clean up: -# -_ep_message_quiet_capture(STATUS "extracting... [clean up]") -file(REMOVE_RECURSE "${ut_dir}") - -_ep_message_quiet_capture(STATUS "extracting... done") diff --git a/Modules/ExternalProject/gitclone.cmake.in b/Modules/ExternalProject/gitclone.cmake.in deleted file mode 100644 index edbdd72..0000000 --- a/Modules/ExternalProject/gitclone.cmake.in +++ /dev/null @@ -1,93 +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) - -set(quiet "@quiet@") -set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") -include(${script_dir}/captured_process_setup.cmake) - -if(EXISTS "@gitclone_stampfile@" AND EXISTS "@gitclone_infofile@" AND - "@gitclone_stampfile@" IS_NEWER_THAN "@gitclone_infofile@") - if(NOT quiet) - message(STATUS - "Avoiding repeated git clone, stamp file is up to date: " - "'@gitclone_stampfile@'" - ) - endif() - return() -endif() - -execute_process( - COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@" - RESULT_VARIABLE error_code - ${capture_output} -) -_ep_command_check_result( - error_code "Failed to remove directory: '@source_dir@'" -) - -# 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) - # If you are seeing the following call hang and you have QUIET enabled, try - # turning QUIET off to show any output immediately. The command may be - # blocking while waiting for user input (e.g. a password to a SSH key). - execute_process( - COMMAND "@git_EXECUTABLE@" @git_options@ - clone @git_clone_options@ "@git_repository@" "@src_name@" - WORKING_DIRECTORY "@work_dir@" - RESULT_VARIABLE error_code - ${capture_output} - ) - if(NOT "${out_var}" STREQUAL "") - string(APPEND accumulated_output "${out_var}\n") - endif() - math(EXPR number_of_tries "${number_of_tries} + 1") -endwhile() -if(number_of_tries GREATER 1) - set(msg "Had to git clone more than once: ${number_of_tries} times.") - if(quiet) - string(APPEND accumulated_output "${msg}\n") - else() - message(STATUS "${msg}") - endif() -endif() -_ep_command_check_result( - error_code "Failed to clone repository: '@git_repository@'" -) - -execute_process( - COMMAND "@git_EXECUTABLE@" @git_options@ - checkout "@git_tag@" @git_checkout_explicit--@ - WORKING_DIRECTORY "@work_dir@/@src_name@" - RESULT_VARIABLE error_code - ${capture_output} -) -_ep_command_check_result(error_code "Failed to checkout tag: '@git_tag@'") - -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 - ${capture_output} - ) - _ep_command_check_result( - error_code "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 - ${capture_output} -) -_ep_command_check_result( - error_code "Failed to copy script-last-run stamp file: '@gitclone_stampfile@'" -) diff --git a/Modules/ExternalProject/gitupdate.cmake.in b/Modules/ExternalProject/gitupdate.cmake.in deleted file mode 100644 index fc2a6ab..0000000 --- a/Modules/ExternalProject/gitupdate.cmake.in +++ /dev/null @@ -1,315 +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) - -set(quiet "@quiet@") -set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") -include(${script_dir}/captured_process_setup.cmake) - -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) - _ep_message_quiet_capture(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 - _ep_message_quiet_capture(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 "") - _ep_message_quiet_capture(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 "") - _ep_message_quiet_capture(WARNING "${error_msg}") - endif() - - endif() -endif() - -if(fetch_required) - _ep_message_quiet_capture(VERBOSE "Fetching latest from the remote @git_remote_name@") - execute_process( - COMMAND "@git_EXECUTABLE@" fetch --tags --force "@git_remote_name@" - WORKING_DIRECTORY "@work_dir@" - RESULT_VARIABLE error_code - ${capture_output} - ) - _ep_command_check_result( - error_code "Failed to fetch from the remote @git_remote_name@'" - ) -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@" - RESULT_VARIABLE error_code # There is no error if no upstream is set - OUTPUT_VARIABLE upstream_branch - OUTPUT_STRIP_TRAILING_WHITESPACE - ${capture_error_only} - ) - _ep_command_check_result(error_code) - 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") - _ep_message_quiet_capture(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 - ${capture_error_only} -) -_ep_command_check_result(error_code "Failed to get the status") -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@" - RESULT_VARIABLE error_code - ${capture_output} - ) - _ep_command_check_result(error_code) -endif() - -if(git_update_strategy STREQUAL "CHECKOUT") - execute_process( - COMMAND "@git_EXECUTABLE@" checkout "${checkout_name}" - WORKING_DIRECTORY "@work_dir@" - RESULT_VARIABLE error_code - ${capture_output} - ) - _ep_command_check_result(error_code) -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() - _ep_message_quiet_capture(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}") - _ep_message_quiet_capture(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@" - RESULT_VARIABLE error_code - ${capture_output} - ) - _ep_command_check_result(error_code) - - execute_process( - COMMAND "@git_EXECUTABLE@" checkout "${checkout_name}" - WORKING_DIRECTORY "@work_dir@" - RESULT_VARIABLE error_code - ${capture_output} - ) - _ep_command_check_result(error_code) - 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 - ${capture_output} - ) - _ep_accumulate_captured_output() - if(error_code) - # Stash pop --index failed: Try again dropping the index - execute_process( - COMMAND "@git_EXECUTABLE@" reset --hard --quiet - WORKING_DIRECTORY "@work_dir@" - ${capture_output} - ) - _ep_accumulate_captured_output() - execute_process( - COMMAND "@git_EXECUTABLE@" stash pop --quiet - WORKING_DIRECTORY "@work_dir@" - RESULT_VARIABLE error_code - ${capture_output} - ) - _ep_accumulate_captured_output() - if(error_code) - # Stash pop failed: Restore previous state. - execute_process( - COMMAND "@git_EXECUTABLE@" reset --hard --quiet ${head_sha} - WORKING_DIRECTORY "@work_dir@" - ${capture_output} - ) - _ep_accumulate_captured_output() - execute_process( - COMMAND "@git_EXECUTABLE@" stash pop --index --quiet - WORKING_DIRECTORY "@work_dir@" - ${capture_output} - ) - _ep_accumulate_captured_output() - _ep_message_quiet_capture(FATAL_ERROR - "Failed to unstash changes in: '@work_dir@'.\n" - "You 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@" - RESULT_VARIABLE error_code - ${capture_output} - ) - _ep_command_check_result(error_code) -endif() diff --git a/Modules/ExternalProject/hgclone.cmake.in b/Modules/ExternalProject/hgclone.cmake.in deleted file mode 100644 index 9a574d2..0000000 --- a/Modules/ExternalProject/hgclone.cmake.in +++ /dev/null @@ -1,59 +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) - -set(quiet "@quiet@") -set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") -include(${script_dir}/captured_process_setup.cmake) - -if(EXISTS "@hgclone_stampfile@" AND EXISTS "@hgclone_infofile@" AND - "@hgclone_stampfile@" IS_NEWER_THAN "@hgclone_infofile@") - if(NOT quiet) - message(STATUS - "Avoiding repeated hg clone, stamp file is up to date: " - "'@hgclone_stampfile@'" - ) - endif() - return() -endif() - -execute_process( - COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@" - RESULT_VARIABLE error_code - ${capture_output} -) -_ep_command_check_result( - error_code "Failed to remove directory: '@source_dir@'" -) - -execute_process( - COMMAND "@hg_EXECUTABLE@" clone -U "@hg_repository@" "@src_name@" - WORKING_DIRECTORY "@work_dir@" - RESULT_VARIABLE error_code - ${capture_output} -) -_ep_command_check_result( - error_code "Failed to clone repository: '@hg_repository@'" -) - -execute_process( - COMMAND "@hg_EXECUTABLE@" update @hg_tag@ - WORKING_DIRECTORY "@work_dir@/@src_name@" - RESULT_VARIABLE error_code - ${capture_output} -) -_ep_command_check_result( - error_code "Failed to checkout tag: '@hg_tag@'" -) - -# Complete success, update the script-last-run stamp file: -# -execute_process( - COMMAND ${CMAKE_COMMAND} -E copy "@hgclone_infofile@" "@hgclone_stampfile@" - RESULT_VARIABLE error_code - ${capture_output} -) -_ep_command_check_result( - error_code "Failed to copy script-last-run stamp file: '@hgclone_stampfile@'" -) diff --git a/Modules/ExternalProject/hgupdate.cmake.in b/Modules/ExternalProject/hgupdate.cmake.in deleted file mode 100644 index a82a819..0000000 --- a/Modules/ExternalProject/hgupdate.cmake.in +++ /dev/null @@ -1,24 +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.19) - -set(quiet "@quiet@") -set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") -include(${script_dir}/captured_process_setup.cmake) - -execute_process( - COMMAND "@hg_EXECUTABLE@" pull - WORKING_DIRECTORY "@work_dir@" - RESULT_VARIABLE error_code - ${capture_output} -) -_ep_command_check_result(error_code) - -execute_process( - COMMAND "@hg_EXECUTABLE@" update @hg_tag@ - WORKING_DIRECTORY "@work_dir@" - RESULT_VARIABLE error_code - ${capture_output} -) -_ep_command_check_result(error_code) diff --git a/Modules/ExternalProject/mkdirs.cmake.in b/Modules/ExternalProject/mkdirs.cmake.in deleted file mode 100644 index 73e80fa..0000000 --- a/Modules/ExternalProject/mkdirs.cmake.in +++ /dev/null @@ -1,19 +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) - -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 deleted file mode 100644 index cd34ba9..0000000 --- a/Modules/ExternalProject/verify.cmake.in +++ /dev/null @@ -1,58 +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) - -set(quiet "@quiet@") -set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") -include(${script_dir}/captured_process_setup.cmake) - -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 "") - _ep_message_quiet_capture(WARNING - "File will not be verified since no URL_HASH specified" - ) - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - return() - endif() - - if("@EXPECT_VALUE@" STREQUAL "") - _ep_message_quiet_capture(FATAL_ERROR "EXPECT_VALUE can't be empty") - endif() - - _ep_message_quiet_capture(STATUS -"verifying file... - file='@LOCAL@'" - ) - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) - - file("@ALGO@" "@LOCAL@" actual_value) - - if(NOT "${actual_value}" STREQUAL "@EXPECT_VALUE@") - _ep_message_quiet_capture(FATAL_ERROR -"error: @ALGO@ hash of - @LOCAL@ -does not match expected value - expected: '@EXPECT_VALUE@' - actual: '${actual_value}' -") - endif() - - _ep_message_quiet_capture(STATUS "verifying file... done") - set(accumulated_output "${accumulated_output}" PARENT_SCOPE) -endfunction() - -do_verify() - -set(extract_script "@extract_script_filename@") -if(NOT "${extract_script}" STREQUAL "") - include("${extract_script}") -endif() diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake index 6a4cf38..8adef47 100644 --- a/Modules/FetchContent.cmake +++ b/Modules/FetchContent.cmake @@ -849,6 +849,8 @@ function(__FetchContent_directPopulate contentName) SUBBUILD_DIR SOURCE_DIR BINARY_DIR + # We need special processing if DOWNLOAD_NO_EXTRACT is true + DOWNLOAD_NO_EXTRACT # Prevent the following from being passed through CONFIGURE_COMMAND BUILD_COMMAND @@ -892,28 +894,123 @@ function(__FetchContent_directPopulate contentName) set(${contentName}_SOURCE_DIR "${ARG_SOURCE_DIR}" PARENT_SCOPE) set(${contentName}_BINARY_DIR "${ARG_BINARY_DIR}" PARENT_SCOPE) - if(ARG_QUIET) - set(quiet TRUE) + # The unparsed arguments may contain spaces, so build up ARG_EXTRA + # in such a way that it correctly substitutes into the generated + # CMakeLists.txt file with each argument quoted. + unset(ARG_EXTRA) + foreach(arg IN LISTS ARG_UNPARSED_ARGUMENTS) + set(ARG_EXTRA "${ARG_EXTRA} \"${arg}\"") + endforeach() + + if(ARG_DOWNLOAD_NO_EXTRACT) + set(ARG_EXTRA "${ARG_EXTRA} DOWNLOAD_NO_EXTRACT YES") + set(__FETCHCONTENT_COPY_FILE +" +ExternalProject_Get_Property(${contentName}-populate DOWNLOADED_FILE) +get_filename_component(dlFileName \"\${DOWNLOADED_FILE}\" NAME) + +ExternalProject_Add_Step(${contentName}-populate copyfile + COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different + \"\" \"${ARG_SOURCE_DIR}\" + DEPENDEES patch + DEPENDERS configure + BYPRODUCTS \"${ARG_SOURCE_DIR}/\${dlFileName}\" + COMMENT \"Copying file to SOURCE_DIR\" +) +") else() - set(quiet FALSE) + unset(__FETCHCONTENT_COPY_FILE) + endif() + + # Hide output if requested, but save it to a variable in case there's an + # error so we can show the output upon failure. When not quiet, don't + # capture the output to a variable because the user may want to see the + # output as it happens (e.g. progress during long downloads). Combine both + # stdout and stderr in the one capture variable so the output stays in order. + if (ARG_QUIET) + set(outputOptions + OUTPUT_VARIABLE capturedOutput + ERROR_VARIABLE capturedOutput + ) + else() + set(capturedOutput) + set(outputOptions) message(STATUS "Populating ${contentName}") endif() - include(ExternalProject) - set(argsQuoted) - foreach(__item IN LISTS ARG_UNPARSED_ARGUMENTS) - string(APPEND argsQuoted " [==[${__item}]==]") - endforeach() - cmake_language(EVAL CODE " - _ep_do_preconfigure_steps_now(${contentName} - ${argsQuoted} - QUIET ${quiet} - SOURCE_DIR [==[${ARG_SOURCE_DIR}]==] - BINARY_DIR [==[${ARG_BINARY_DIR}]==] - USES_TERMINAL_DOWNLOAD YES - USES_TERMINAL_UPDATE YES - )" + if(CMAKE_GENERATOR) + set(subCMakeOpts "-G${CMAKE_GENERATOR}") + if(CMAKE_GENERATOR_PLATFORM) + list(APPEND subCMakeOpts "-A${CMAKE_GENERATOR_PLATFORM}") + endif() + if(CMAKE_GENERATOR_TOOLSET) + list(APPEND subCMakeOpts "-T${CMAKE_GENERATOR_TOOLSET}") + endif() + + if(CMAKE_MAKE_PROGRAM) + list(APPEND subCMakeOpts "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}") + endif() + + else() + # Likely we've been invoked via CMake's script mode where no + # generator is set (and hence CMAKE_MAKE_PROGRAM could not be + # trusted even if provided). We will have to rely on being + # able to find the default generator and build tool. + unset(subCMakeOpts) + endif() + + if(DEFINED CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY) + list(APPEND subCMakeOpts + "-DCMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY=${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}") + endif() + + # Avoid using if(... IN_LIST ...) so we don't have to alter policy settings + set(__FETCHCONTENT_CACHED_INFO "") + list(FIND ARG_UNPARSED_ARGUMENTS GIT_REPOSITORY indexResult) + if(indexResult GREATER_EQUAL 0) + find_package(Git QUIET) + set(__FETCHCONTENT_CACHED_INFO +"# Pass through things we've already detected in the main project to avoid +# paying the cost of redetecting them again in ExternalProject_Add() +set(GIT_EXECUTABLE [==[${GIT_EXECUTABLE}]==]) +set(GIT_VERSION_STRING [==[${GIT_VERSION_STRING}]==]) +set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION + [==[${GIT_EXECUTABLE};${GIT_VERSION_STRING}]==] +) +") + endif() + + # Create and build a separate CMake project to carry out the population. + # If we've already previously done these steps, they will not cause + # anything to be updated, so extra rebuilds of the project won't occur. + # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project + # has this set to something not findable on the PATH. + configure_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/FetchContent/CMakeLists.cmake.in" + "${ARG_SUBBUILD_DIR}/CMakeLists.txt") + execute_process( + COMMAND ${CMAKE_COMMAND} ${subCMakeOpts} . + RESULT_VARIABLE result + ${outputOptions} + WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" + ) + if(result) + if(capturedOutput) + message("${capturedOutput}") + endif() + message(FATAL_ERROR "CMake step for ${contentName} failed: ${result}") + endif() + execute_process( + COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + ${outputOptions} + WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" ) + if(result) + if(capturedOutput) + message("${capturedOutput}") + endif() + message(FATAL_ERROR "Build step for ${contentName} failed: ${result}") + endif() endfunction() diff --git a/Modules/FetchContent/CMakeLists.cmake.in b/Modules/FetchContent/CMakeLists.cmake.in new file mode 100644 index 0000000..5ebb12f --- /dev/null +++ b/Modules/FetchContent/CMakeLists.cmake.in @@ -0,0 +1,27 @@ +# 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 ${CMAKE_VERSION}) + +# We name the project and the target for the ExternalProject_Add() call +# to something that will highlight to the user what we are working on if +# something goes wrong and an error message is produced. + +project(${contentName}-populate NONE) + +@__FETCHCONTENT_CACHED_INFO@ + +include(ExternalProject) +ExternalProject_Add(${contentName}-populate + ${ARG_EXTRA} + SOURCE_DIR "${ARG_SOURCE_DIR}" + BINARY_DIR "${ARG_BINARY_DIR}" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + USES_TERMINAL_DOWNLOAD YES + USES_TERMINAL_UPDATE YES +) + +@__FETCHCONTENT_COPY_FILE@ diff --git a/Modules/RepositoryInfo.txt.in b/Modules/RepositoryInfo.txt.in new file mode 100644 index 0000000..df8e322 --- /dev/null +++ b/Modules/RepositoryInfo.txt.in @@ -0,0 +1,3 @@ +repository='@repository@' +module='@module@' +tag='@tag@' diff --git a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-stderr.txt b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-stderr.txt index 22d7ac0..5a5ba89 100644 --- a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-stderr.txt +++ b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-stderr.txt @@ -10,7 +10,7 @@ Call Stack \(most recent call first\): [^ ]*/Modules/ExternalProject.cmake:[0-9]+ \(ExternalProject_Add_Step\) [^ -]*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_preconfigure_command\) +]*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_mkdir_command\) NO_DEPENDS-CMP0114-Common.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-NEW.cmake:[0-9]+ \(include\) CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-WARN-stderr.txt b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-WARN-stderr.txt index 0172e3f..bbf7178 100644 --- a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-WARN-stderr.txt +++ b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-WARN-stderr.txt @@ -13,7 +13,7 @@ Call Stack \(most recent call first\): [^ ]*/Modules/ExternalProject.cmake:[0-9]+ \(ExternalProject_Add_Step\) [^ -]*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_preconfigure_command\) +]*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_mkdir_command\) NO_DEPENDS-CMP0114-Common.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-WARN.cmake:[0-9]+ \(include\) CMakeLists.txt:[0-9]+ \(include\) @@ -68,7 +68,7 @@ Call Stack \(most recent call first\): [^ ]*/Modules/ExternalProject.cmake:[0-9]+ \(ExternalProject_Add_Step\) [^ -]*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_preconfigure_command\) +]*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_mkdir_command\) NO_DEPENDS-CMP0114-Common.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-WARN.cmake:[0-9]+ \(include\) CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt b/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt index 9576ae1..2fc7d29 100644 --- a/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt +++ b/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt @@ -13,6 +13,6 @@ \* HG_REPOSITORY \* CVS_REPOSITORY and CVS_MODULE Call Stack \(most recent call first\): - .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_prepare_download\) + .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_download_command\) NoOptions.cmake:[0-9]+ \(ExternalProject_Add\) CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt b/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt index 648f28b..07c6e87 100644 --- a/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt +++ b/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt @@ -13,6 +13,6 @@ \* HG_REPOSITORY \* CVS_REPOSITORY and CVS_MODULE Call Stack \(most recent call first\): - .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_prepare_download\) + .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_download_command\) SourceEmpty.cmake:[0-9]+ \(ExternalProject_Add\) CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt b/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt index e061cf6..373f6e3 100644 --- a/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt +++ b/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt @@ -13,6 +13,6 @@ \* HG_REPOSITORY \* CVS_REPOSITORY and CVS_MODULE Call Stack \(most recent call first\): - .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_prepare_download\) + .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_download_command\) SourceMissing.cmake:[0-9]+ \(ExternalProject_Add\) CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake b/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake index 2850bed..201d822 100644 --- a/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake +++ b/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake @@ -19,7 +19,7 @@ cmake_minimum_required(VERSION 3.3) # console pool. macro(CheckNinjaStep _target _step _require) if("${_build}" MATCHES -" DESC = Performing ${_step} step (\\([a-zA-Z0-9 ]*\\) )?for '${_target}' +" DESC = Performing ${_step} step for '${_target}' pool = console" ) if(NOT ${_require}) diff --git a/Tests/RunCMake/FetchContent/MultiCommand-stdout.txt b/Tests/RunCMake/FetchContent/MultiCommand-stdout.txt deleted file mode 100644 index 7f2c0f8..0000000 --- a/Tests/RunCMake/FetchContent/MultiCommand-stdout.txt +++ /dev/null @@ -1,6 +0,0 @@ -.* *download 1 -.* *download 2 -.* *update 1 -.* *update 2 -.* *patch 1 -.* *patch 2 diff --git a/Tests/RunCMake/FetchContent/MultiCommand.cmake b/Tests/RunCMake/FetchContent/MultiCommand.cmake deleted file mode 100644 index 92e08e4..0000000 --- a/Tests/RunCMake/FetchContent/MultiCommand.cmake +++ /dev/null @@ -1,18 +0,0 @@ -include(FetchContent) - -# Verify COMMAND keyword is recognised after various *_COMMAND options -FetchContent_Declare(multiCommand - DOWNLOAD_COMMAND "${CMAKE_COMMAND}" -E echo "download 1" - COMMAND "${CMAKE_COMMAND}" -E echo "download 2" - UPDATE_COMMAND "${CMAKE_COMMAND}" -E echo "update 1" - COMMAND "${CMAKE_COMMAND}" -E echo "update 2" - PATCH_COMMAND "${CMAKE_COMMAND}" -E echo "patch 1" - COMMAND "${CMAKE_COMMAND}" -E echo "patch 2" -) - -# Force all steps to be re-run by removing timestamps, scripts, etc. from any -# previous run -file(REMOVE_RECURSE "${FETCHCONTENT_BASE_DIR}/multiCommand-subbuild") - -set(FETCHCONTENT_QUIET FALSE) -FetchContent_MakeAvailable(multiCommand) diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake index d7fd009..9baeab7 100644 --- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake +++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake @@ -2,12 +2,12 @@ include(RunCMake) unset(RunCMake_TEST_NO_CLEAN) -run_cmake(MultiCommand) run_cmake(MissingDetails) run_cmake(DirectIgnoresDetails) run_cmake(FirstDetailsWin) run_cmake(DownloadTwice) run_cmake(DownloadFile) +run_cmake(SameGenerator) run_cmake(VarDefinitions) run_cmake(GetProperties) run_cmake(UsesTerminalOverride) @@ -27,36 +27,6 @@ run_cmake_with_options(ManualSourceDirectoryRelative -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT:STRING=WithProject" ) -function(run_FetchContent_TimeStamps) - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TimeStamps) - file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") - file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") - - # First run should execute the commands - run_cmake(TimeStamps) - - # Ensure that the file checks we use in the TimeStampsRerun-check.cmake script - # will not be defeated by file systems with only one second resolution. - # The IS_NEWER_THAN check returns TRUE if the timestamps of the two files are - # the same, which has been observed where filesystems only have one second - # resolution. - set(cmpTimeStamp ${RunCMake_TEST_BINARY_DIR}/cmpTimeStamp.txt) - set(checkTimeStamp ${RunCMake_TEST_BINARY_DIR}/cmpTimeStampCheck.txt) - file(TOUCH ${cmpTimeStamp}) - file(TOUCH ${checkTimeStamp}) - if("${cmpTimeStamp}" IS_NEWER_THAN "${checkTimeStamp}") - execute_process( - COMMAND ${CMAKE_COMMAND} -E sleep 1.125 - COMMAND_ERROR_IS_FATAL LAST - ) - endif() - - # Run again with no changes, no commands should re-execute - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake(TimeStampsRerun) -endfunction() -run_FetchContent_TimeStamps() - function(run_FetchContent_DirOverrides) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DirOverrides-build) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") diff --git a/Tests/RunCMake/FetchContent/SameGenerator.cmake b/Tests/RunCMake/FetchContent/SameGenerator.cmake new file mode 100644 index 0000000..58204ef --- /dev/null +++ b/Tests/RunCMake/FetchContent/SameGenerator.cmake @@ -0,0 +1,17 @@ +include(FetchContent) + +FetchContent_Declare( + t1 + DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command executed" +) + +FetchContent_Populate(t1) + +file(STRINGS "${FETCHCONTENT_BASE_DIR}/t1-subbuild/CMakeCache.txt" + matchLine REGEX "^CMAKE_GENERATOR:.*=" + LIMIT_COUNT 1 +) +if(NOT matchLine MATCHES "${CMAKE_GENERATOR}") + message(FATAL_ERROR "Generator line mismatch: ${matchLine}\n" + " Expected type: ${CMAKE_GENERATOR}") +endif() diff --git a/Tests/RunCMake/FetchContent/TimeStamps-stdout.txt b/Tests/RunCMake/FetchContent/TimeStamps-stdout.txt deleted file mode 100644 index 2ba1ff4..0000000 --- a/Tests/RunCMake/FetchContent/TimeStamps-stdout.txt +++ /dev/null @@ -1,2 +0,0 @@ -.* *download executed -.* *patch executed diff --git a/Tests/RunCMake/FetchContent/TimeStamps.cmake b/Tests/RunCMake/FetchContent/TimeStamps.cmake deleted file mode 100644 index 33874f9..0000000 --- a/Tests/RunCMake/FetchContent/TimeStamps.cmake +++ /dev/null @@ -1,14 +0,0 @@ -include(FetchContent) - -# Do nothing for an update because it would result in always re-running the -# patch step. We want to test that a patch step that only depends on the -# download step is not re-run unnecessarily. -FetchContent_Declare(customCommands - PREFIX ${CMAKE_CURRENT_BINARY_DIR} - DOWNLOAD_COMMAND "${CMAKE_COMMAND}" -E echo "download executed" - UPDATE_COMMAND "" - PATCH_COMMAND "${CMAKE_COMMAND}" -E echo "patch executed" -) - -set(FETCHCONTENT_QUIET FALSE) -FetchContent_MakeAvailable(customCommands) diff --git a/Tests/RunCMake/FetchContent/TimeStampsRerun-check.cmake b/Tests/RunCMake/FetchContent/TimeStampsRerun-check.cmake deleted file mode 100644 index c12a5f4..0000000 --- a/Tests/RunCMake/FetchContent/TimeStampsRerun-check.cmake +++ /dev/null @@ -1,38 +0,0 @@ -set(cmpFile ${RunCMake_TEST_BINARY_DIR}/cmpTimeStamp.txt) -set(scriptDir ${RunCMake_TEST_BINARY_DIR}/tmp) -set(stampDir ${RunCMake_TEST_BINARY_DIR}/src/customcommands-stamp) - -set(errorMessages) -if(NOT EXISTS "${cmpFile}") - list(APPEND errorMessages " ${cmpFile} is missing") -else() - foreach(script IN ITEMS mkdirs download patch) - set(scriptFile "${scriptDir}/customcommands-${script}.cmake") - if(NOT EXISTS "${scriptFile}") - list(APPEND errorMessages " ${scriptFile} is missing") - elseif(NOT "${cmpFile}" IS_NEWER_THAN "${scriptFile}") - list(APPEND errorMessages " ${scriptFile} was unexectedly updated") - endif() - endforeach() - - # special case, not a script, has different extension - set(repoInfoFile "${scriptDir}/customcommands-download-repoinfo.txt") - if(NOT EXISTS "${repoInfoFile}") - list(APPEND errorMessages " ${repoInfoFile} is missing") - elseif(NOT "${cmpFile}" IS_NEWER_THAN "${repoInfoFile}") - list(APPEND errorMessages " ${repoInfoFile} was unexectedly updated") - endif() - - foreach(step IN ITEMS download patch) - set(stampFile "${stampDir}/customcommands-${step}") - if(NOT EXISTS "${stampFile}") - list(APPEND errorMessages " ${stampFile} is missing") - elseif(NOT "${cmpFile}" IS_NEWER_THAN "${stampFile}") - list(APPEND errorMessages " ${stampFile} was unexectedly updated") - endif() - endforeach() -endif() - -if(errorMessages) - list(JOIN errorMessages "\n" RunCMake_TEST_FAILED) -endif() diff --git a/Tests/RunCMake/FetchContent/TimeStampsRerun.cmake b/Tests/RunCMake/FetchContent/TimeStampsRerun.cmake deleted file mode 100644 index e13667a..0000000 --- a/Tests/RunCMake/FetchContent/TimeStampsRerun.cmake +++ /dev/null @@ -1 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/TimeStamps.cmake) -- cgit v0.12