diff options
21 files changed, 1299 insertions, 784 deletions
diff --git a/Help/release/dev/fetchcontent-performance.rst b/Help/release/dev/fetchcontent-performance.rst new file mode 100644 index 0000000..361c2b4 --- /dev/null +++ b/Help/release/dev/fetchcontent-performance.rst @@ -0,0 +1,7 @@ +fetchcontent-performance +------------------------ + +* 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. diff --git a/Modules/ExternalProject-verify.cmake.in b/Modules/ExternalProject-verify.cmake.in deleted file mode 100644 index c06da4e..0000000 --- a/Modules/ExternalProject-verify.cmake.in +++ /dev/null @@ -1,37 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -cmake_minimum_required(VERSION 3.5) - -if("@LOCAL@" STREQUAL "") - message(FATAL_ERROR "LOCAL can't be empty") -endif() - -if(NOT EXISTS "@LOCAL@") - message(FATAL_ERROR "File not found: @LOCAL@") -endif() - -if("@ALGO@" STREQUAL "") - message(WARNING "File will not be verified since no URL_HASH specified") - return() -endif() - -if("@EXPECT_VALUE@" STREQUAL "") - message(FATAL_ERROR "EXPECT_VALUE can't be empty") -endif() - -message(STATUS "verifying file... - file='@LOCAL@'") - -file("@ALGO@" "@LOCAL@" actual_value) - -if(NOT "${actual_value}" STREQUAL "@EXPECT_VALUE@") - message(FATAL_ERROR "error: @ALGO@ hash of - @LOCAL@ -does not match expected value - expected: '@EXPECT_VALUE@' - actual: '${actual_value}' -") -endif() - -message(STATUS "verifying file... done") diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 56525080..5f00c87 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -407,7 +407,7 @@ External Project Definition ``CVS_TAG <tag>`` Tag to checkout from the CVS repository. - **Update/Patch Step Options:** + **Update 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,6 +442,7 @@ 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 <cmd>...`` 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 @@ -717,6 +718,11 @@ External Project Definition ``USES_TERMINAL_UPDATE <bool>`` Give the update step access to the terminal. + ``USES_TERMINAL_PATCH <bool>`` + .. versionadded:: 3.20 + + Give the patch step access to the terminal. + ``USES_TERMINAL_CONFIGURE <bool>`` Give the configure step access to the terminal. @@ -1134,16 +1140,17 @@ macro(_ep_get_hash_regex out_var) set(${out_var} "^(${${out_var}})=([0-9A-Fa-f]+)$") endmacro() -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. - # +function(_ep_parse_arguments_to_vars keywords name ns args) + # Transfer the arguments into variables in the calling scope. # Because some keywords can be repeated, we can't use cmake_parse_arguments(). - # Instead, we loop through ARGN and consider the namespace starting with an - # upper-case letter followed by at least two more upper-case letters, + # 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, # numbers or underscores to be keywords. + foreach(key IN LISTS keywords) + unset(${ns}${key}) + endforeach() + set(key) foreach(arg IN LISTS args) @@ -1160,25 +1167,37 @@ function(_ep_parse_arguments f keywords name ns args) if(is_value) if(key) # Value - 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() + list(APPEND ${ns}${key} "${arg}") else() # Missing Keyword - message(AUTHOR_WARNING "value '${arg}' with no previous keyword in ${f}") + message(AUTHOR_WARNING "value '${arg}' with no previous keyword") 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() @@ -1221,7 +1240,26 @@ define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED "ExternalProject module." ) -function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git_repository git_tag git_remote_name init_submodules git_submodules_recurse git_submodules git_shallow git_progress git_config src_name work_dir gitclone_infofile gitclone_stampfile tls_verify) + +function(_ep_write_gitclone_script + script_filename + source_dir + git_EXECUTABLE + git_repository + git_tag + git_remote_name + init_submodules + git_submodules_recurse + git_submodules + git_shallow + git_progress + git_config + src_name + work_dir + gitclone_infofile + gitclone_stampfile + tls_verify) + if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5) # Use `git checkout <tree-ish> --` to avoid ambiguity with a local path. set(git_checkout_explicit-- "--") @@ -1267,134 +1305,50 @@ function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git endif() string (REPLACE ";" " " git_options "${git_options}") - 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() - -# try the clone 3 times in case there is an odd git clone issue -set(error_code 1) -set(number_of_tries 0) -while(error_code AND number_of_tries LESS 3) - execute_process( - COMMAND \"${git_EXECUTABLE}\" ${git_options} clone ${git_clone_options} \"${git_repository}\" \"${src_name}\" - WORKING_DIRECTORY \"${work_dir}\" - RESULT_VARIABLE error_code - ) - math(EXPR number_of_tries \"\${number_of_tries} + 1\") -endwhile() -if(number_of_tries GREATER 1) - message(STATUS \"Had to git clone more than once: - \${number_of_tries} times.\") -endif() -if(error_code) - message(FATAL_ERROR \"Failed to clone repository: '${git_repository}'\") -endif() - -execute_process( - COMMAND \"${git_EXECUTABLE}\" ${git_options} checkout ${git_tag} ${git_checkout_explicit--} - WORKING_DIRECTORY \"${work_dir}/${src_name}\" - RESULT_VARIABLE error_code - ) -if(error_code) - message(FATAL_ERROR \"Failed to checkout tag: '${git_tag}'\") -endif() - -set(init_submodules ${init_submodules}) -if(init_submodules) - execute_process( - COMMAND \"${git_EXECUTABLE}\" ${git_options} submodule update ${git_submodules_recurse} --init ${git_submodules} - WORKING_DIRECTORY \"${work_dir}/${src_name}\" - RESULT_VARIABLE error_code - ) -endif() -if(error_code) - message(FATAL_ERROR \"Failed to update submodules in: '${work_dir}/${src_name}'\") -endif() - -# Complete success, update the script-last-run stamp file: -# -execute_process( - COMMAND \${CMAKE_COMMAND} -E copy - \"${gitclone_infofile}\" - \"${gitclone_stampfile}\" - RESULT_VARIABLE error_code + configure_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitclone.cmake.in + ${script_filename} + @ONLY ) -if(error_code) - message(FATAL_ERROR \"Failed to copy script-last-run stamp file: '${gitclone_stampfile}'\") -endif() - -" -) endfunction() -function(_ep_write_hgclone_script script_filename source_dir hg_EXECUTABLE hg_repository hg_tag src_name work_dir hgclone_infofile hgclone_stampfile) +function(_ep_write_hgclone_script + script_filename + source_dir + hg_EXECUTABLE + hg_repository + hg_tag + src_name + work_dir + hgclone_infofile + hgclone_stampfile) + if("${hg_tag}" STREQUAL "") message(FATAL_ERROR "Tag for hg checkout should not be empty.") endif() - file(WRITE ${script_filename} -" -if(NOT \"${hgclone_infofile}\" IS_NEWER_THAN \"${hgclone_stampfile}\") - message(STATUS \"Avoiding repeated hg clone, stamp file is up to date: '${hgclone_stampfile}'\") - return() -endif() -execute_process( - COMMAND \${CMAKE_COMMAND} -E rm -rf \"${source_dir}\" - RESULT_VARIABLE error_code - ) -if(error_code) - message(FATAL_ERROR \"Failed to remove directory: '${source_dir}'\") -endif() - -execute_process( - COMMAND \"${hg_EXECUTABLE}\" clone -U \"${hg_repository}\" \"${src_name}\" - WORKING_DIRECTORY \"${work_dir}\" - RESULT_VARIABLE error_code - ) -if(error_code) - message(FATAL_ERROR \"Failed to clone repository: '${hg_repository}'\") -endif() - -execute_process( - COMMAND \"${hg_EXECUTABLE}\" update ${hg_tag} - WORKING_DIRECTORY \"${work_dir}/${src_name}\" - RESULT_VARIABLE error_code - ) -if(error_code) - message(FATAL_ERROR \"Failed to checkout tag: '${hg_tag}'\") -endif() - -# Complete success, update the script-last-run stamp file: -# -execute_process( - COMMAND \${CMAKE_COMMAND} -E copy - \"${hgclone_infofile}\" - \"${hgclone_stampfile}\" - RESULT_VARIABLE error_code + configure_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/hgclone.cmake.in + ${script_filename} + @ONLY ) -if(error_code) - message(FATAL_ERROR \"Failed to copy script-last-run stamp file: '${hgclone_stampfile}'\") -endif() - -" -) endfunction() -function(_ep_write_gitupdate_script script_filename git_EXECUTABLE git_tag git_remote_name init_submodules git_submodules_recurse git_submodules git_repository work_dir git_update_strategy) +function(_ep_write_gitupdate_script + script_filename + git_EXECUTABLE + git_tag + git_remote_name + init_submodules + git_submodules_recurse + git_submodules + git_repository + work_dir + git_update_strategy) + if("${git_tag}" STREQUAL "") message(FATAL_ERROR "Tag for git checkout should not be empty.") endif() @@ -1408,13 +1362,54 @@ function(_ep_write_gitupdate_script script_filename git_EXECUTABLE git_tag git_r endif() configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-gitupdate.cmake.in" - "${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) +function(_ep_write_hgupdate_script + script_filename + hg_EXECUTABLE + hg_tag + work_dir) + + 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 + ) +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) + if(timeout) set(TIMEOUT_ARGS TIMEOUT ${timeout}) set(TIMEOUT_MSG "${timeout} seconds") @@ -1518,13 +1513,18 @@ function(_ep_write_downloadfile_script script_filename REMOTE LOCAL timeout inac # * USERPWD_ARGS # * HTTP_HEADERS_ARGS configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-download.cmake.in" - "${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) +function(_ep_write_verifyfile_script + script_filename + LOCAL + hash + extract_script_filename) + _ep_get_hash_regex(_ep_hash_regex) if("${hash}" MATCHES "${_ep_hash_regex}") set(ALGO "${CMAKE_MATCH_1}") @@ -1538,15 +1538,21 @@ function(_ep_write_verifyfile_script script_filename LOCAL hash) # * 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) +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)$") @@ -1558,77 +1564,33 @@ function(_ep_write_extractfile_script script_filename name filename directory) endif() if(args STREQUAL "") - 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() + 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") endif() - file(WRITE ${script_filename} -"# Make file names absolute: -# -get_filename_component(filename \"${filename}\" ABSOLUTE) -get_filename_component(directory \"${directory}\" ABSOLUTE) + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/extractfile.cmake.in" + "${script_filename}" + @ONLY + ) -message(STATUS \"extracting... - src='\${filename}' - dst='\${directory}'\") +endfunction() -if(NOT EXISTS \"\${filename}\") - message(FATAL_ERROR \"error: file to extract does not exist: '\${filename}'\") -endif() -# Prepare a space for extracting: +# This function is an implementation detail of ExternalProject_Add(). # -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: +# The function expects keyword arguments to have already been parsed into +# variables of the form _EP_<keyword>. It will create the various directories +# before returning and it will populate variables of the form +# _EP_<location>_DIR in the calling scope. # -message(STATUS \"extracting... [tar ${args}]\") -execute_process(COMMAND \${CMAKE_COMMAND} -E tar ${args} \${filename} - WORKING_DIRECTORY \${ut_dir} - RESULT_VARIABLE rv) - -if(NOT rv EQUAL 0) - message(STATUS \"extracting... [error clean up]\") - file(REMOVE_RECURSE \"\${ut_dir}\") - message(FATAL_ERROR \"error: extract of '\${filename}' failed\") -endif() - -# Analyze what came out of the tar file: +# Variables will also be set in the calling scope to enable subsequently +# calling _ep_add_preconfigure_command() for the mkdir step. # -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() +function(_ep_prepare_directories name) -# Move \"the one\" directory to the final directory: -# -message(STATUS \"extracting... [rename]\") -file(REMOVE_RECURSE \${directory}) -get_filename_component(contents \${contents} ABSOLUTE) -file(RENAME \${contents} \${directory}) - -# Clean up: -# -message(STATUS \"extracting... [clean up]\") -file(REMOVE_RECURSE \"\${ut_dir}\") - -message(STATUS \"extracting... done\") -" -) - -endfunction() - - -function(_ep_set_directories name) - get_property(prefix TARGET ${name} PROPERTY _EP_PREFIX) + set(prefix ${_EP_PREFIX}) if(NOT prefix) get_property(prefix DIRECTORY PROPERTY EP_PREFIX) if(NOT prefix) @@ -1639,6 +1601,7 @@ function(_ep_set_directories name) endif() endif() if(prefix) + file(TO_CMAKE_PATH "${prefix}" prefix) set(tmp_default "${prefix}/tmp") set(download_default "${prefix}/src") set(source_default "${prefix}/src/${name}") @@ -1646,6 +1609,7 @@ function(_ep_set_directories name) set(stamp_default "${prefix}/src/${name}-stamp") set(install_default "${prefix}") else() + file(TO_CMAKE_PATH "${base}" base) set(tmp_default "${base}/tmp/${name}") set(download_default "${base}/Download/${name}") set(source_default "${base}/Source/${name}") @@ -1653,10 +1617,10 @@ function(_ep_set_directories name) set(stamp_default "${base}/Stamp/${name}") set(install_default "${base}/Install/${name}") endif() - get_property(build_in_source TARGET ${name} PROPERTY _EP_BUILD_IN_SOURCE) + + set(build_in_source "${_EP_BUILD_IN_SOURCE}") if(build_in_source) - get_property(have_binary_dir TARGET ${name} PROPERTY _EP_BINARY_DIR SET) - if(have_binary_dir) + if(DEFINED _EP_BINARY_DIR) message(FATAL_ERROR "External project ${name} has both BINARY_DIR and BUILD_IN_SOURCE!") endif() @@ -1667,64 +1631,77 @@ function(_ep_set_directories name) set(places stamp download source binary install tmp) foreach(var ${places}) string(TOUPPER "${var}" VAR) - get_property(${var}_dir TARGET ${name} PROPERTY _EP_${VAR}_DIR) + set(${var}_dir "${_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() - set_property(TARGET ${name} PROPERTY _EP_${VAR}_DIR "${${var}_dir}") + file(TO_CMAKE_PATH "${${var}_dir}" ${var}_dir) endforeach() # Special case for default log directory based on stamp directory. - get_property(log_dir TARGET ${name} PROPERTY _EP_LOG_DIR) + set(log_dir "${_EP_LOG_DIR}") if(NOT log_dir) - get_property(log_dir TARGET ${name} PROPERTY _EP_STAMP_DIR) - endif() - if(NOT IS_ABSOLUTE "${log_dir}") - get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE) + set(log_dir "${stamp_dir}") + else() + if(NOT IS_ABSOLUTE "${log_dir}") + get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE) + endif() endif() - set_property(TARGET ${name} PROPERTY _EP_LOG_DIR "${log_dir}") + file(TO_CMAKE_PATH "${log_dir}" log_dir) + list(APPEND places log) - 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() + 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}") # Prefix with a slash so that when appended to the source directory, it # behaves as expected. - set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "/${source_subdir}") + string(PREPEND source_subdir "/") endif() + if(build_in_source) - 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() + set(binary_dir "${source_dir}${source_subdir}") endif() - # Make the directories at CMake configure time *and* add a custom command - # to make them at build time. They need to exist at makefile generation - # time for Borland make and wmake so that CMake may generate makefiles - # with "cd C:\short\paths\with\no\spaces" commands in them. - # - # Additionally, the add_custom_command is still used in case somebody - # removes one of the necessary directories and tries to rebuild without - # re-running cmake. + # 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) + foreach(var ${places}) string(TOUPPER "${var}" VAR) - get_property(dir TARGET ${name} PROPERTY _EP_${VAR}_DIR) - file(MAKE_DIRECTORY "${dir}") - if(NOT EXISTS "${dir}") - message(FATAL_ERROR "dir '${dir}' does not exist after file(MAKE_DIRECTORY)") - endif() + set(_EP_${VAR}_DIR "${${var}_dir}" PARENT_SCOPE) endforeach() -endfunction() + 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} @@ -1741,6 +1718,17 @@ 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 "") @@ -1923,17 +1911,24 @@ function(_ep_get_build_command name step cmd_var) set(${cmd_var} "${cmd}" PARENT_SCOPE) endfunction() -function(_ep_write_log_script name step cmd_var) - ExternalProject_Get_Property(${name} log_dir) - ExternalProject_Get_Property(${name} stamp_dir) +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}-$<CONFIG>) + else() + set(script_base ${tmp_dir}/${name}-${step}) + endif() 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) @@ -1955,8 +1950,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() @@ -1990,15 +1985,22 @@ endif() endif() endforeach() string(APPEND code "set(command \"${cmd}\")${code_execute_process}") - file(GENERATE OUTPUT "${stamp_dir}/${name}-${step}-$<CONFIG>-impl.cmake" CONTENT "${code}") - set(command ${CMAKE_COMMAND} "-Dmake=\${make}" "-Dconfig=\${config}" -P ${stamp_dir}/${name}-${step}-$<CONFIG>-impl.cmake) + 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 + ) endif() # Wrap the command in a script to log output to files. - set(script ${stamp_dir}/${name}-${step}-$<CONFIG>.cmake) set(logbase ${log_dir}/${name}-${step}) - 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) + set(log_merged "${_EP_LOG_MERGED_STDOUTERR}") + set(log_output_on_failure "${_EP_LOG_OUTPUT_ON_FAILURE}") if (log_merged) set(stdout_log "${logbase}.log") set(stderr_log "${logbase}.log") @@ -2063,8 +2065,13 @@ else() endif() endif() ") - file(GENERATE OUTPUT "${script}" CONTENT "${code}") - set(command ${CMAKE_COMMAND} ${make} ${config} -P ${script}) + 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}) set(${cmd_var} "${command}" PARENT_SCOPE) endfunction() @@ -2242,8 +2249,7 @@ function(ExternalProject_Add_Step name step) LOG USES_TERMINAL ) - _ep_parse_arguments(ExternalProject_Add_Step "${keywords}" - ${name} _EP_${step}_ "${ARGN}") + _ep_parse_arguments("${keywords}" ${name} _EP_${step}_ "${ARGN}") get_property(independent TARGET ${name} PROPERTY _EP_${step}_INDEPENDENT) if(independent STREQUAL "") @@ -2354,7 +2360,8 @@ function(ExternalProject_Add_Step name step) # Wrap with log script? get_property(log TARGET ${name} PROPERTY _EP_${step}_LOG) if(command AND log) - _ep_write_log_script(${name} ${step} command) + set(genex_supported TRUE) + _ep_write_log_script(${name} ${step} ${genex_supported} command) endif() if("${command}" STREQUAL "") @@ -2486,27 +2493,6 @@ 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 "") @@ -2517,114 +2503,258 @@ function(_ep_is_dir_empty dir empty_var) endfunction() function(_ep_get_git_submodules_recurse git_submodules_recurse) - # 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") + + 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) else() - get_property(git_submodules_recurse_value TARGET ${name} PROPERTY _EP_GIT_SUBMODULES_RECURSE) - if(git_submodules_recurse_value) - set(recurseFlag "--recursive") + set(${git_submodules_recurse} "" PARENT_SCOPE) + endif() + +endfunction() + +function(_ep_write_command_script + script_filename + commands + work_dir + genex_supported + 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) + + 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(APPEND script_content " +execute_process( + COMMAND ${this_command} + COMMAND_ERROR_IS_FATAL LAST + WORKING_DIRECTORY [==[${work_dir}]==] +) +") + set(this_command) else() - set(recurseFlag "") + # Ensure we quote every token so we preserve empty items, quotes, etc + string(APPEND this_command " [==[${token}]==]") endif() + endforeach() + + if(NOT "${this_command}" STREQUAL "") + string(APPEND script_content " +execute_process( + COMMAND ${this_command} + COMMAND_ERROR_IS_FATAL LAST + WORKING_DIRECTORY [==[${work_dir}]==] +) +") endif() - set(${git_submodules_recurse} "${recurseFlag}" PARENT_SCOPE) - # 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}'") + if(script_content STREQUAL "") + set(${have_commands_var} FALSE PARENT_SCOPE) + else() + set(${have_commands_var} TRUE PARENT_SCOPE) + string(PREPEND script_content "cmake_minimum_required(VERSION 3.19)\n") endif() + + if(genex_supported) + # Only written at generation phase + file(GENERATE OUTPUT "${script_filename}" CONTENT "${script_content}") + else() + # Written immediately, needed if script has to be invoked in configure phase + file(WRITE "${script_filename}" "${script_content}") + endif() + endfunction() +function(_ep_add_preconfigure_command name step) -function(_ep_add_download_command name) - ExternalProject_Get_Property(${name} source_dir stamp_dir download_dir tmp_dir) + 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() - 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) +# This function is an implementation detail of ExternalProject_Add(). +# +# The function expects keyword arguments to have already been parsed into +# variables of the form _EP_<keyword>. 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) - # TODO: Perhaps file:// should be copied to download dir before extraction. - string(REGEX REPLACE "file://" "" url "${url}") + 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(depends) set(comment) - set(work_dir) - if(cmd_set) + # 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) + 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) set(work_dir ${download_dir}) - elseif(cvs_repository) + 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}" + script_does_something + ) + set(comment "Performing download step (custom command) for '${name}'") + + elseif(DEFINED _EP_CVS_REPOSITORY) find_package(CVS QUIET) if(NOT CVS_EXECUTABLE) message(FATAL_ERROR "error: could not find cvs for checkout of ${name}") endif() - get_target_property(cvs_module ${name} _EP_CVS_MODULE) - if(NOT cvs_module) + if("${_EP_CVS_MODULE}" STREQUAL "") message(FATAL_ERROR "error: no CVS_MODULE") endif() - get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG) - - set(repository ${cvs_repository}) - set(module ${cvs_module}) - set(tag ${cvs_tag}) + set(repo_info_content +"method=cvs +repository=${_EP_CVS_REPOSITORY} +module=${_EP_CVS_MODULE} +tag=${_EP_CVS_TAG} +source_dir=${source_dir} +") configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" - "${stamp_dir}/${name}-cvsinfo.txt" + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in + ${repo_info_file} @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}" + script_does_something + ) set(comment "Performing download step (CVS checkout) for '${name}'") - 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) + + elseif(DEFINED _EP_SVN_REPOSITORY) find_package(Subversion QUIET) if(NOT Subversion_SVN_EXECUTABLE) message(FATAL_ERROR "error: could not find svn for checkout of ${name}") 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(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(repository "${svn_repository} user=${svn_username} password=${svn_password}") - set(module) - set(tag ${svn_revision}) + 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() + + set(repo_info_content +"method=svn +repository=${svn_repository} +user=${svn_username} +password=${svn_password} +revision=${svn_revision} +source_dir=${source_dir} +") configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" - "${stamp_dir}/${name}-svninfo.txt" + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in + ${repo_info_file} @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}" + script_does_something + ) set(comment "Performing download step (SVN checkout) for '${name}'") - 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) + + elseif(DEFINED _EP_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 @@ -2634,111 +2764,131 @@ function(_ep_add_download_command name) endif() endif() - _ep_get_git_submodules_recurse(git_submodules_recurse) - - get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) + set(git_tag "${_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) - 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") + if(DEFINED _EP_GIT_SUBMODULES) + set(git_submodules "${_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) - 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) + set(tls_verify "${_EP_TLS_VERIFY}") if("x${tls_verify}" STREQUAL "x" AND DEFINED CMAKE_TLS_VERIFY) set(tls_verify "${CMAKE_TLS_VERIFY}") endif() - 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) + set(git_shallow "${_EP_GIT_SHALLOW}") + set(git_progress "${_EP_GIT_PROGRESS}") + set(git_config "${_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 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 + # 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 # changes, avoid running the clone script again. Let the 'always' running # update step checkout the new tag. - # - set(repository ${git_repository}) - set(module) - set(tag ${git_remote_name}) + set(repo_info_content +"method=git +repository=${_EP_GIT_REPOSITORY} +remote=${git_remote_name} +source_dir=${source_dir} +") configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" - "${stamp_dir}/${name}-gitinfo.txt" + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in + ${repo_info_file} @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, - # 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}" - ) + # 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}" + ) set(comment "Performing download step (git clone) for '${name}'") - set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitclone.cmake) - list(APPEND depends ${stamp_dir}/${name}-gitinfo.txt) - elseif(hg_repository) + + elseif(DEFINED _EP_HG_REPOSITORY) find_package(Hg QUIET) if(NOT HG_EXECUTABLE) message(FATAL_ERROR "error: could not find hg for clone of ${name}") endif() - get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG) + set(hg_tag "${_EP_HG_TAG}") if(NOT hg_tag) set(hg_tag "tip") endif() - # 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) + # 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} +") configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" - "${stamp_dir}/${name}-hginfo.txt" + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in + ${repo_info_file} @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, - # 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 - ) + # 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}" + ) set(comment "Performing download step (hg clone) for '${name}'") - 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) + + 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}") _ep_get_hash_regex(_ep_hash_regex) if(hash AND NOT "${hash}" MATCHES "${_ep_hash_regex}") _ep_get_hash_algos(_ep_hash_algos) @@ -2747,22 +2897,27 @@ function(_ep_add_download_command name) "but must be ALGO=value where ALGO is\n ${_ep_hash_algos}\n" "and value is a hex string.") endif() - get_property(md5 TARGET ${name} PROPERTY _EP_URL_MD5) + set(md5 "${_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(repository "external project URL") - set(module "${url}") - set(tag "${hash}") + + set(repo_info_content +"method=url +url=${url} +hash=${hash} +source_dir=${source_dir} +") configure_file( - "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" - "${stamp_dir}/${name}-urlinfo.txt" + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in + ${repo_info_file} @ONLY - ) - list(APPEND depends ${stamp_dir}/${name}-urlinfo.txt) + ) + + set(fname "${_EP_DOWNLOAD_NAME}") list(LENGTH url url_list_length) if(NOT "${url_list_length}" STREQUAL "1") @@ -2777,12 +2932,21 @@ function(_ep_add_download_command name) endif() if(IS_DIRECTORY "${url}") - 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}) + get_filename_component(from_dir "${url}" ABSOLUTE) + _ep_write_copydir_script( + ${script_filename} + ${from_dir} + ${source_dir} + ) + set(steps "DIR copy") else() - get_property(no_extract TARGET "${name}" PROPERTY _EP_DOWNLOAD_NO_EXTRACT) + set(no_extract "${_EP_DOWNLOAD_NO_EXTRACT}") + if(no_extract) + set(extract_script) + else() + set(extract_script "${tmp_dir}/extract-${name}.cmake") + endif() + if("${url}" MATCHES "^[a-z]+://") # TODO: Should download and extraction be different steps? if("x${fname}" STREQUAL "x") @@ -2796,55 +2960,67 @@ function(_ep_add_download_command name) # 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 during the build which is an easier - # place for users to diagnose than an error here anyway. + # 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. set(fname "archive.tar") endif() string(REPLACE ";" "-" fname "${fname}") set(file ${download_dir}/${fname}) - 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) + _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}" + ) + if(no_extract) set(steps "download and verify") - else () + else() set(steps "download, verify and extract") - endif () - set(comment "Performing download step (${steps}) for '${name}'") - file(WRITE "${stamp_dir}/verify-${name}.cmake" "") # already verified by 'download_script' + endif() else() set(file "${url}") - if (no_extract) + _ep_write_verifyfile_script( + "${script_filename}" + "${file}" + "${hash}" + "${extract_script}" + ) + if(no_extract) set(steps "verify") - else () + else() set(steps "verify and extract") - endif () - set(comment "Performing download step (${steps}) for '${name}'") - _ep_write_verifyfile_script("${stamp_dir}/verify-${name}.cmake" "${file}" "${hash}") + 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}" + ) 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(SEND_ERROR + message(FATAL_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" @@ -2857,105 +3033,143 @@ function(_ep_add_download_command name) " * CVS_REPOSITORY and CVS_MODULE" ) endif() - 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 + ) - get_property(log TARGET ${name} PROPERTY _EP_LOG_DOWNLOAD) - if(log) - set(log LOG 1) - else() - set(log "") + set(comment "Skipping download step (SOURCE_DIR given) for '${name}'") + set(script_does_something FALSE) endif() - get_property(uses_terminal TARGET ${name} PROPERTY - _EP_USES_TERMINAL_DOWNLOAD) - if(uses_terminal) - set(uses_terminal USES_TERMINAL 1) + # 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}) else() - set(uses_terminal "") + set(cmd) + set(depends) + string(REPLACE "Performing" "Skipping" comment "${comment}") 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} - )" - ) + 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) + endfunction() -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) +function(_ep_get_update_disconnected var) + if(DEFINED _EP_UPDATE_DISCONNECTED) + set(update_disconnected "${_EP_UPDATE_DISCONNECTED}") else() get_property(update_disconnected DIRECTORY PROPERTY EP_UPDATE_DISCONNECTED) endif() set(${var} "${update_disconnected}" PARENT_SCOPE) endfunction() -function(_ep_add_update_command name) - ExternalProject_Get_Property(${name} source_dir tmp_dir) - - 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 ) +# This function is an implementation detail of ExternalProject_Add(). +# +# The function expects keyword arguments to have already been parsed into +# variables of the form _EP_<keyword>. +# +# 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) - _ep_get_update_disconnected(update_disconnected ${name}) + set(tmp_dir "${_EP_TMP_DIR}") + set(source_dir "${_EP_SOURCE_DIR}") - set(work_dir) set(comment) - set(always) - if(cmd_set) + _ep_get_update_disconnected(update_disconnected) + + # 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) + else() + set(script_filename ${tmp_dir}/${name}-update.cmake) + set(log FALSE) + endif() + + if(DEFINED _EP_UPDATE_COMMAND) set(work_dir ${source_dir}) - if(NOT "x${cmd}" STREQUAL "x") - set(always 1) - endif() - elseif(cvs_repository) + _ep_write_command_script( + "${script_filename}" + "${_EP_UPDATE_COMMAND}" + "${work_dir}" + "${genex_supported}" + script_does_something + ) + set(comment "Performing update step (custom command) for '${name}'") + + elseif(DEFINED _EP_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}" + script_does_something + ) set(comment "Performing update step (CVS update) for '${name}'") - 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) + + elseif(DEFINED _EP_SVN_REPOSITORY) if(NOT Subversion_SVN_EXECUTABLE) message(FATAL_ERROR "error: could not find svn for update of ${name}") endif() - 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}") + + 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 svn_password) - set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") + if(DEFINED _EP_SVN_PASSWORD) + list(APPEND svn_options "--password=${svn_password}") endif() if(svn_trust_cert) - set(svn_trust_cert_args --trust-server-cert) + list(APPEND svn_options --trust-server-cert) endif() - set(cmd ${Subversion_SVN_EXECUTABLE} up ${svn_revision} - --non-interactive ${svn_trust_cert_args} ${svn_user_pw_args}) - set(always 1) - elseif(git_repository) + + 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}" + script_does_something + ) + set(comment "Performing update step (SVN update) for '${name}'") + + elseif(DEFINED _EP_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 @@ -2964,27 +3178,27 @@ function(_ep_add_update_command name) message(FATAL_ERROR "error: could not find git for fetch of ${name}") endif() endif() - set(work_dir ${source_dir}) - set(comment "Performing update step for '${name}'") - get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) + + set(git_tag "${_EP_GIT_TAG}") if(NOT git_tag) set(git_tag "master") endif() - get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME) + + set(git_remote_name "${_EP_GIT_REMOTE_NAME}") if(NOT git_remote_name) set(git_remote_name "origin") endif() set(git_init_submodules TRUE) - 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(DEFINED _EP_GIT_SUBMODULES) + set(git_submodules "${_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) - get_property(git_update_strategy TARGET ${name} PROPERTY _EP_GIT_REMOTE_UPDATE_STRATEGY) + set(git_update_strategy "${_EP_GIT_REMOTE_UPDATE_STRATEGY}") if(NOT git_update_strategy) set(git_update_strategy "${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}") endif() @@ -2996,23 +3210,27 @@ function(_ep_add_update_command name) message(FATAL_ERROR "'${git_update_strategy}' is not one of the supported strategies: ${strategies}") endif() - _ep_get_git_submodules_recurse(git_submodules_recurse) + 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}" + ) + set(script_does_something TRUE) + set(comment "Performing update step (git update) for '${name}'") - _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) + elseif(DEFINED _EP_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 @@ -3020,87 +3238,112 @@ function(_ep_add_update_command name) Update to Mercurial >= 2.1.1. ") endif() - set(cmd ${HG_EXECUTABLE} pull - COMMAND ${HG_EXECUTABLE} update ${hg_tag} - ) - set(always 1) - endif() - get_property(log TARGET ${name} PROPERTY _EP_LOG_UPDATE) - if(log) - set(log LOG 1) + 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}" + ) + set(script_does_something TRUE) + set(comment "Performing update step (hg pull) for '${name}'") + else() - set(log "") + set(script_does_something FALSE) endif() - get_property(uses_terminal TARGET ${name} PROPERTY - _EP_USES_TERMINAL_UPDATE) - if(uses_terminal) - set(uses_terminal USES_TERMINAL 1) + # 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() else() - set(uses_terminal "") + set(always FALSE) + set(cmd) endif() - 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} - )" - ) + 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) endfunction() +# This function is an implementation detail of ExternalProject_Add(). +# +# The function expects keyword arguments to have already been parsed into +# variables of the form _EP_<keyword>. +# +# 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) -function(_ep_add_patch_command name) - ExternalProject_Get_Property(${name} source_dir) - - get_property(cmd_set TARGET ${name} PROPERTY _EP_PATCH_COMMAND SET) - get_property(cmd TARGET ${name} PROPERTY _EP_PATCH_COMMAND) - - set(work_dir) + set(tmp_dir "${_EP_TMP_DIR}") + set(source_dir "${_EP_SOURCE_DIR}") - if(cmd_set) - set(work_dir ${source_dir}) + _ep_get_update_disconnected(update_disconnected) + if(update_disconnected) + set(patch_dep download) + else() + set(patch_dep update) endif() - get_property(log TARGET ${name} PROPERTY _EP_LOG_PATCH) + # We handle the log setting directly here rather than deferring it to + # be handled by ExternalProject_Add_Step() + set(log "${_EP_LOG_PATCH}") if(log) - set(log LOG 1) + set(script_filename ${tmp_dir}/${name}-patch-impl.cmake) + set(log TRUE) else() - set(log "") + set(script_filename ${tmp_dir}/${name}-patch.cmake) + set(log FALSE) endif() - _ep_get_update_disconnected(update_disconnected ${name}) - if(update_disconnected) - set(patch_dep download) + if(DEFINED _EP_PATCH_COMMAND) + set(work_dir "${source_dir}") + _ep_write_command_script( + "${script_filename}" + "${_EP_PATCH_COMMAND}" + "${work_dir}" + "${genex_supported}" + 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() else() - set(patch_dep update) + set(cmd) + set(comment "Skipping patch step (no custom command) for '${name}'") endif() - 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} - )" - ) + # 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) + endfunction() function(_ep_get_file_deps var name) @@ -3210,7 +3453,11 @@ 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 @@ -3242,10 +3489,11 @@ function(_ep_add_configure_command name) # used, cmake args or cmake generator) then re-run the configure step. # Fixes issue https://gitlab.kitware.com/cmake/cmake/-/issues/10258 # - if(NOT EXISTS ${tmp_dir}/${name}-cfgcmd.txt.in) - file(WRITE ${tmp_dir}/${name}-cfgcmd.txt.in "cmd='\@cmd\@'\n") - endif() - configure_file(${tmp_dir}/${name}-cfgcmd.txt.in ${tmp_dir}/${name}-cfgcmd.txt) + configure_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/cfgcmd.txt.in + ${tmp_dir}/${name}-cfgcmd.txt + @ONLY + ) list(APPEND file_deps ${tmp_dir}/${name}-cfgcmd.txt) list(APPEND file_deps ${_ep_cache_args_script}) @@ -3456,51 +3704,8 @@ function(_ep_add_test_command name) 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() - - _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 +macro(_ep_get_add_keywords out_var) + set(${out_var} # # Directory options # @@ -3629,14 +3834,73 @@ function(ExternalProject_Add name) # LIST_SEPARATOR ) - _ep_parse_arguments(ExternalProject_Add "${keywords}" ${name} _EP_ "${ARGN}") - _ep_set_directories(${name}) +endmacro() + + +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_get_step_stampfile(${name} "done" done_stamp_file) _ep_get_step_stampfile(${name} "install" install_stamp_file) - # 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) + if(arg_EXCLUDE_FROM_ALL) set_property(TARGET ${name} PROPERTY EXCLUDE_FROM_ALL TRUE) endif() @@ -3677,10 +3941,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_mkdir_command(${name}) - _ep_add_download_command(${name}) - _ep_add_update_command(${name}) - _ep_add_patch_command(${name}) + _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_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 new file mode 100644 index 0000000..d82f04c --- /dev/null +++ b/Modules/ExternalProject/RepositoryInfo.txt.in @@ -0,0 +1 @@ +@repo_info_content@ diff --git a/Modules/ExternalProject/cfgcmd.txt.in b/Modules/ExternalProject/cfgcmd.txt.in new file mode 100644 index 0000000..b3f09ef --- /dev/null +++ b/Modules/ExternalProject/cfgcmd.txt.in @@ -0,0 +1 @@ +cmd='@cmd@' diff --git a/Modules/ExternalProject/copydir.cmake.in b/Modules/ExternalProject/copydir.cmake.in new file mode 100644 index 0000000..5dd3891 --- /dev/null +++ b/Modules/ExternalProject/copydir.cmake.in @@ -0,0 +1,10 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.5) + +file(REMOVE_RECURSE "@to_dir@") + +# Copy the _contents_ of the source dir into the destination dir, hence the +# trailing slash on the from_dir +file(COPY "@from_dir@/" DESTINATION "@to_dir@") diff --git a/Modules/ExternalProject-download.cmake.in b/Modules/ExternalProject/download.cmake.in index ff8c659..6ef4eb1 100644 --- a/Modules/ExternalProject-download.cmake.in +++ b/Modules/ExternalProject/download.cmake.in @@ -79,77 +79,81 @@ 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): +function(download_and_verify) + if(EXISTS "@LOCAL@") + check_file_hash(has_hash hash_is_good) + if(has_hash) + if(hash_is_good) + message(STATUS +"File already exists and hash match (skip download): file='@LOCAL@' @ALGO@='@EXPECT_VALUE@'" - ) - return() + ) + return() + else() + message(STATUS "File already exists but hash mismatch. Removing...") + file(REMOVE "@LOCAL@") + endif() 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): + 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@") + ) + file(REMOVE "@LOCAL@") + endif() endif() -endif() -set(retry_number 5) + set(retry_number 5) -message(STATUS "Downloading... + 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@") + ) + 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() - message(STATUS "Downloading... done") - return() - endif() - else() - string(APPEND logFailedURLs "error: downloading '${url}' failed + string(APPEND logFailedURLs +"error: downloading '${url}' failed status_code: ${status_code} status_string: ${status_string} log: @@ -157,17 +161,27 @@ foreach(i RANGE ${retry_number}) ${log} --- LOG END --- " - ) - if(NOT status_code IN_LIST download_retry_codes) - list(APPEND skip_url_list "${url}") - break() + ) + if(NOT status_code IN_LIST download_retry_codes) + list(APPEND skip_url_list "${url}") + break() + endif() endif() endif() - endif() + endforeach() endforeach() -endforeach() -message(FATAL_ERROR "Each download failed! + message(FATAL_ERROR +"Each download failed! ${logFailedURLs} " -) + ) + +endfunction() + +download_and_verify() + +set(extract_script @extract_script_filename@) +if(NOT "${extract_script}" STREQUAL "") + include(${extract_script}) +endif() diff --git a/Modules/ExternalProject/extractfile.cmake.in b/Modules/ExternalProject/extractfile.cmake.in new file mode 100644 index 0000000..d9e07f1 --- /dev/null +++ b/Modules/ExternalProject/extractfile.cmake.in @@ -0,0 +1,63 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.5) + +# Make file names absolute: +# +get_filename_component(filename "@filename@" ABSOLUTE) +get_filename_component(directory "@directory@" ABSOLUTE) + +message(STATUS "extracting... + src='${filename}' + dst='${directory}'") + +if(NOT EXISTS "${filename}") + message(FATAL_ERROR "File to extract does not exist: '${filename}'") +endif() + +# Prepare a space for extracting: +# +set(i 1234) +while(EXISTS "${directory}/../ex-@name@${i}") + math(EXPR i "${i} + 1") +endwhile() +set(ut_dir "${directory}/../ex-@name@${i}") +file(MAKE_DIRECTORY "${ut_dir}") + +# Extract it: +# +message(STATUS "extracting... [tar @args@]") +execute_process(COMMAND ${CMAKE_COMMAND} -E tar @args@ ${filename} + WORKING_DIRECTORY ${ut_dir} + RESULT_VARIABLE rv) + +if(NOT rv EQUAL 0) + message(STATUS "extracting... [error clean up]") + file(REMOVE_RECURSE "${ut_dir}") + message(FATAL_ERROR "Extract of '${filename}' failed") +endif() + +# Analyze what came out of the tar file: +# +message(STATUS "extracting... [analysis]") +file(GLOB contents "${ut_dir}/*") +list(REMOVE_ITEM contents "${ut_dir}/.DS_Store") +list(LENGTH contents n) +if(NOT n EQUAL 1 OR NOT IS_DIRECTORY "${contents}") + set(contents "${ut_dir}") +endif() + +# Move "the one" directory to the final directory: +# +message(STATUS "extracting... [rename]") +file(REMOVE_RECURSE ${directory}) +get_filename_component(contents ${contents} ABSOLUTE) +file(RENAME ${contents} ${directory}) + +# Clean up: +# +message(STATUS "extracting... [clean up]") +file(REMOVE_RECURSE "${ut_dir}") + +message(STATUS "extracting... done") diff --git a/Modules/ExternalProject/gitclone.cmake.in b/Modules/ExternalProject/gitclone.cmake.in new file mode 100644 index 0000000..5e5c415 --- /dev/null +++ b/Modules/ExternalProject/gitclone.cmake.in @@ -0,0 +1,67 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.5) + +if(NOT "@gitclone_infofile@" IS_NEWER_THAN "@gitclone_stampfile@") + message(STATUS "Avoiding repeated git clone, stamp file is up to date: '@gitclone_stampfile@'") + return() +endif() + +execute_process( + COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR "Failed to remove directory: '@source_dir@'") +endif() + +# try the clone 3 times in case there is an odd git clone issue +set(error_code 1) +set(number_of_tries 0) +while(error_code AND number_of_tries LESS 3) + execute_process( + COMMAND "@git_EXECUTABLE@" @git_options@ clone @git_clone_options@ "@git_repository@" "@src_name@" + WORKING_DIRECTORY "@work_dir@" + RESULT_VARIABLE error_code + ) + math(EXPR number_of_tries "${number_of_tries} + 1") +endwhile() +if(number_of_tries GREATER 1) + message(STATUS "Had to git clone more than once: + ${number_of_tries} times.") +endif() +if(error_code) + message(FATAL_ERROR "Failed to clone repository: '@git_repository@'") +endif() + +execute_process( + COMMAND "@git_EXECUTABLE@" @git_options@ checkout "@git_tag@" @git_checkout_explicit--@ + WORKING_DIRECTORY "@work_dir@/@src_name@" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR "Failed to checkout tag: '@git_tag@'") +endif() + +set(init_submodules @init_submodules@) +if(init_submodules) + execute_process( + COMMAND "@git_EXECUTABLE@" @git_options@ submodule update @git_submodules_recurse@ --init @git_submodules@ + WORKING_DIRECTORY "@work_dir@/@src_name@" + RESULT_VARIABLE error_code + ) +endif() +if(error_code) + message(FATAL_ERROR "Failed to update submodules in: '@work_dir@/@src_name@'") +endif() + +# Complete success, update the script-last-run stamp file: +# +execute_process( + COMMAND ${CMAKE_COMMAND} -E copy "@gitclone_infofile@" "@gitclone_stampfile@" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR "Failed to copy script-last-run stamp file: '@gitclone_stampfile@'") +endif() diff --git a/Modules/ExternalProject-gitupdate.cmake.in b/Modules/ExternalProject/gitupdate.cmake.in index 7033918..7033918 100644 --- a/Modules/ExternalProject-gitupdate.cmake.in +++ b/Modules/ExternalProject/gitupdate.cmake.in diff --git a/Modules/ExternalProject/hgclone.cmake.in b/Modules/ExternalProject/hgclone.cmake.in new file mode 100644 index 0000000..09395cc --- /dev/null +++ b/Modules/ExternalProject/hgclone.cmake.in @@ -0,0 +1,45 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.5) + +if(NOT "@hgclone_infofile@" IS_NEWER_THAN "@hgclone_stampfile@") + message(STATUS "Avoiding repeated hg clone, stamp file is up to date: '@hgclone_stampfile@'") + return() +endif() + +execute_process( + COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR "Failed to remove directory: '@source_dir@'") +endif() + +execute_process( + COMMAND "@hg_EXECUTABLE@" clone -U "@hg_repository@" "@src_name@" + WORKING_DIRECTORY "@work_dir@" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR "Failed to clone repository: '@hg_repository@'") +endif() + +execute_process( + COMMAND "@hg_EXECUTABLE@" update @hg_tag@ + WORKING_DIRECTORY "@work_dir@/@src_name@" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR "Failed to checkout tag: '@hg_tag@'") +endif() + +# Complete success, update the script-last-run stamp file: +# +execute_process( + COMMAND ${CMAKE_COMMAND} -E copy "@hgclone_infofile@" "@hgclone_stampfile@" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR "Failed to copy script-last-run stamp file: '@hgclone_stampfile@'") +endif() diff --git a/Modules/ExternalProject/hgupdate.cmake.in b/Modules/ExternalProject/hgupdate.cmake.in new file mode 100644 index 0000000..f88e1ee --- /dev/null +++ b/Modules/ExternalProject/hgupdate.cmake.in @@ -0,0 +1,16 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.19) + +execute_process( + COMMAND "@hg_EXECUTABLE@" pull + COMMAND_ERROR_IS_FATAL ANY + WORKING_DIRECTORY "@work_dir@" +) + +execute_process( + COMMAND "@hg_EXECUTABLE@" update @hg_tag@ + COMMAND_ERROR_IS_FATAL ANY + WORKING_DIRECTORY "@work_dir@" +) diff --git a/Modules/ExternalProject/mkdirs.cmake.in b/Modules/ExternalProject/mkdirs.cmake.in new file mode 100644 index 0000000..73e80fa --- /dev/null +++ b/Modules/ExternalProject/mkdirs.cmake.in @@ -0,0 +1,19 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.5) + +file(MAKE_DIRECTORY + "@source_dir@" + "@binary_dir@" + "@install_dir@" + "@tmp_dir@" + "@stamp_dir@" + "@download_dir@" + "@log_dir@" +) + +set(configSubDirs @CMAKE_CONFIGURATION_TYPES@) +foreach(subDir IN LISTS configSubDirs) + file(MAKE_DIRECTORY "@stamp_dir@/${subDir}") +endforeach() diff --git a/Modules/ExternalProject/verify.cmake.in b/Modules/ExternalProject/verify.cmake.in new file mode 100644 index 0000000..f37059b --- /dev/null +++ b/Modules/ExternalProject/verify.cmake.in @@ -0,0 +1,48 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.5) + +if("@LOCAL@" STREQUAL "") + message(FATAL_ERROR "LOCAL can't be empty") +endif() + +if(NOT EXISTS "@LOCAL@") + message(FATAL_ERROR "File not found: @LOCAL@") +endif() + +function(do_verify) + if("@ALGO@" STREQUAL "") + message(WARNING "File will not be verified since no URL_HASH specified") + return() + endif() + + if("@EXPECT_VALUE@" STREQUAL "") + message(FATAL_ERROR "EXPECT_VALUE can't be empty") + endif() + + message(STATUS +"verifying file... + file='@LOCAL@'") + + file("@ALGO@" "@LOCAL@" actual_value) + + if(NOT "${actual_value}" STREQUAL "@EXPECT_VALUE@") + message(FATAL_ERROR +"error: @ALGO@ hash of + @LOCAL@ +does not match expected value + expected: '@EXPECT_VALUE@' + actual: '${actual_value}' +") + endif() + + message(STATUS "verifying file... done") +endfunction() + +do_verify() + +set(extract_script "@extract_script_filename@") +if(NOT "${extract_script}" STREQUAL "") + include("${extract_script}") +endif() diff --git a/Modules/RepositoryInfo.txt.in b/Modules/RepositoryInfo.txt.in deleted file mode 100644 index df8e322..0000000 --- a/Modules/RepositoryInfo.txt.in +++ /dev/null @@ -1,3 +0,0 @@ -repository='@repository@' -module='@module@' -tag='@tag@' diff --git a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-stderr.txt b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-stderr.txt index 5a5ba89..22d7ac0 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_mkdir_command\) +]*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_preconfigure_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 bbf7178..0172e3f 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_mkdir_command\) +]*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_preconfigure_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_mkdir_command\) +]*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_add_preconfigure_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 2fc7d29..9576ae1 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_add_download_command\) + .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_prepare_download\) 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 07c6e87..648f28b 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_add_download_command\) + .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_prepare_download\) 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 373f6e3..e061cf6 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_add_download_command\) + .*/Modules/ExternalProject.cmake:[0-9]+ \(_ep_prepare_download\) 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 201d822..2850bed 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 for '${_target}' +" DESC = Performing ${_step} step (\\([a-zA-Z0-9 ]*\\) )?for '${_target}' pool = console" ) if(NOT ${_require}) |