summaryrefslogtreecommitdiff
path: root/Modules/ExternalProject.cmake
diff options
context:
space:
mode:
authorCraig Scott <craig.scott@crascit.com>2021-02-05 07:32:58 +1100
committerCraig Scott <craig.scott@crascit.com>2021-02-05 07:33:38 +1100
commit4f3d1abbb4dca3d1e6b019471fa5d8be296492e3 (patch)
treef3614845c0e2c7c904cb0be116462f8d80f1af47 /Modules/ExternalProject.cmake
parent23aab9ecce264b7ea198fc9d36f382a4bb4fbe28 (diff)
downloadcmake-4f3d1abbb4dca3d1e6b019471fa5d8be296492e3.tar.gz
ExternalProject: Refactor pre-configure steps to support no-target uses
The mkdir, download, update and patch steps are used by FetchContent during the configure phase of the main build. Because these steps need a target, this has so far required a sub-build to be set up. The changes here factor out the preparation of the scripts from the creation of the targets, allowing future work to leverage these steps without a sub-build (see #21703). As part of the refactoring, some rationalisation of the stamp files, repository info files and script names was done to make things more consistent between download methods and step implementations. Every download method now records its own specific repository info in a file and that file is a dependency of the download step. The source directory is also written to that file, so if the SOURCE_DIR changes, the download will be retriggered (the existing implementation fails in this scenario). Each download method now also has just one driver script that implements the whole step (it may pull in other scripts to do its task though). The patch step gained support for USES_TERMINAL as a result of generalising the implementation for custom commands. Fixes: #21748
Diffstat (limited to 'Modules/ExternalProject.cmake')
-rw-r--r--Modules/ExternalProject.cmake1604
1 files changed, 934 insertions, 670 deletions
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 56525080cc..5f00c87447 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})