diff options
Diffstat (limited to 'Modules/ExternalProject.cmake')
-rw-r--r-- | Modules/ExternalProject.cmake | 1792 |
1 files changed, 706 insertions, 1086 deletions
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 987b69a..56525080 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -407,7 +407,7 @@ External Project Definition ``CVS_TAG <tag>`` Tag to checkout from the CVS repository. - **Update Step Options:** + **Update/Patch Step Options:** Whenever CMake is re-run, by default the external project's sources will be updated if the download method supports updates (e.g. a git repository would be checked if the ``GIT_TAG`` does not refer to a specific commit). @@ -442,7 +442,6 @@ External Project Definition This may cause a step target to be created automatically for the ``download`` step. See policy :policy:`CMP0114`. - **Patch Step Options:** ``PATCH_COMMAND <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 @@ -718,11 +717,6 @@ 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. @@ -1140,17 +1134,16 @@ macro(_ep_get_hash_regex out_var) set(${out_var} "^(${${out_var}})=([0-9A-Fa-f]+)$") endmacro() -function(_ep_parse_arguments_to_vars keywords name ns args) - # Transfer the arguments into variables in the calling scope. +function(_ep_parse_arguments f keywords name ns args) + # Transfer the arguments to this function into target properties for the + # new custom target we just added so that we can set up all the build steps + # correctly based on target properties. + # # Because some keywords can be repeated, we can't use cmake_parse_arguments(). - # Instead, we loop through the args and consider the namespace starting with - # an upper-case letter followed by at least two more upper-case letters, + # Instead, we loop through ARGN and consider the namespace starting with an + # upper-case letter followed by at least two more upper-case letters, # numbers or underscores to be keywords. - foreach(key IN LISTS keywords) - unset(${ns}${key}) - endforeach() - set(key) foreach(arg IN LISTS args) @@ -1167,100 +1160,68 @@ function(_ep_parse_arguments_to_vars keywords name ns args) if(is_value) if(key) # Value - list(APPEND ${ns}${key} "${arg}") + if(NOT arg STREQUAL "") + set_property(TARGET ${name} APPEND PROPERTY ${ns}${key} "${arg}") + else() + get_property(have_key TARGET ${name} PROPERTY ${ns}${key} SET) + if(have_key) + get_property(value TARGET ${name} PROPERTY ${ns}${key}) + set_property(TARGET ${name} PROPERTY ${ns}${key} "${value};${arg}") + else() + set_property(TARGET ${name} PROPERTY ${ns}${key} "${arg}") + endif() + endif() else() # Missing Keyword - message(AUTHOR_WARNING "value '${arg}' with no previous keyword") + message(AUTHOR_WARNING "value '${arg}' with no previous keyword in ${f}") endif() else() set(key "${arg}") endif() endforeach() - - foreach(key IN LISTS keywords) - if(DEFINED ${ns}${key}) - set(${ns}${key} "${${ns}${key}}" PARENT_SCOPE) - else() - unset(${ns}${key} PARENT_SCOPE) - endif() - endforeach() - -endfunction() - -function(_ep_parse_arguments keywords name ns args) - _ep_parse_arguments_to_vars("${keywords}" ${name} ${ns} "${args}") - - # Transfer the arguments to the target as target properties. These are - # read by the various steps, potentially from different scopes. - foreach(key IN LISTS keywords) - if(DEFINED ${ns}${key}) - set_property(TARGET ${name} PROPERTY ${ns}${key} "${${ns}${key}}") - endif() - endforeach() - endfunction() -if(NOT DEFINED CMAKE_SCRIPT_MODE_FILE) - define_property(DIRECTORY PROPERTY "EP_BASE" INHERITED - BRIEF_DOCS "Base directory for External Project storage." - FULL_DOCS - "See documentation of the ExternalProject_Add() function in the " - "ExternalProject module." - ) - define_property(DIRECTORY PROPERTY "EP_PREFIX" INHERITED - BRIEF_DOCS "Top prefix for External Project storage." - FULL_DOCS - "See documentation of the ExternalProject_Add() function in the " - "ExternalProject module." - ) +define_property(DIRECTORY PROPERTY "EP_BASE" INHERITED + BRIEF_DOCS "Base directory for External Project storage." + FULL_DOCS + "See documentation of the ExternalProject_Add() function in the " + "ExternalProject module." + ) - define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED - BRIEF_DOCS - "List of ExternalProject steps that automatically get corresponding targets" - FULL_DOCS - "These targets will be dependent on the main target dependencies. " - "See documentation of the ExternalProject_Add_StepTargets() function in the " - "ExternalProject module." - ) +define_property(DIRECTORY PROPERTY "EP_PREFIX" INHERITED + BRIEF_DOCS "Top prefix for External Project storage." + FULL_DOCS + "See documentation of the ExternalProject_Add() function in the " + "ExternalProject module." + ) - define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED - BRIEF_DOCS - "List of ExternalProject steps that automatically get corresponding targets" - FULL_DOCS - "These targets will not be dependent on the main target dependencies. " - "See documentation of the ExternalProject_Add_StepTargets() function in the " - "ExternalProject module." - ) +define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED + BRIEF_DOCS + "List of ExternalProject steps that automatically get corresponding targets" + FULL_DOCS + "These targets will be dependent on the main target dependencies. " + "See documentation of the ExternalProject_Add_StepTargets() function in the " + "ExternalProject module." + ) - define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED - BRIEF_DOCS "Never update automatically from the remote repo." - FULL_DOCS - "See documentation of the ExternalProject_Add() function in the " - "ExternalProject module." - ) -endif() +define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED + BRIEF_DOCS + "List of ExternalProject steps that automatically get corresponding targets" + FULL_DOCS + "These targets will not be dependent on the main target dependencies. " + "See documentation of the ExternalProject_Add_StepTargets() function in the " + "ExternalProject module." + ) -function(_ep_write_gitclone_script - script_filename - source_dir - git_EXECUTABLE - git_repository - git_tag - git_remote_name - init_submodules - git_submodules_recurse - git_submodules - git_shallow - git_progress - git_config - src_name - work_dir - gitclone_infofile - gitclone_stampfile - tls_verify - quiet) +define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED + BRIEF_DOCS "Never update automatically from the remote repo." + FULL_DOCS + "See documentation of the ExternalProject_Add() function in the " + "ExternalProject module." + ) +function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git_repository git_tag git_remote_name init_submodules git_submodules_recurse git_submodules git_shallow git_progress git_config src_name work_dir gitclone_infofile gitclone_stampfile tls_verify) if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5) # Use `git checkout <tree-ish> --` to avoid ambiguity with a local path. set(git_checkout_explicit-- "--") @@ -1306,52 +1267,134 @@ function(_ep_write_gitclone_script endif() string (REPLACE ";" " " git_options "${git_options}") - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitclone.cmake.in - ${script_filename} - @ONLY + file(WRITE ${script_filename} +" +if(NOT \"${gitclone_infofile}\" IS_NEWER_THAN \"${gitclone_stampfile}\") + message(STATUS \"Avoiding repeated git clone, stamp file is up to date: '${gitclone_stampfile}'\") + return() +endif() + +execute_process( + COMMAND \${CMAKE_COMMAND} -E rm -rf \"${source_dir}\" + RESULT_VARIABLE error_code ) +if(error_code) + message(FATAL_ERROR \"Failed to remove directory: '${source_dir}'\") +endif() -endfunction() +# try the clone 3 times in case there is an odd git clone issue +set(error_code 1) +set(number_of_tries 0) +while(error_code AND number_of_tries LESS 3) + execute_process( + COMMAND \"${git_EXECUTABLE}\" ${git_options} clone ${git_clone_options} \"${git_repository}\" \"${src_name}\" + WORKING_DIRECTORY \"${work_dir}\" + RESULT_VARIABLE error_code + ) + math(EXPR number_of_tries \"\${number_of_tries} + 1\") +endwhile() +if(number_of_tries GREATER 1) + message(STATUS \"Had to git clone more than once: + \${number_of_tries} times.\") +endif() +if(error_code) + message(FATAL_ERROR \"Failed to clone repository: '${git_repository}'\") +endif() + +execute_process( + COMMAND \"${git_EXECUTABLE}\" ${git_options} checkout ${git_tag} ${git_checkout_explicit--} + WORKING_DIRECTORY \"${work_dir}/${src_name}\" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR \"Failed to checkout tag: '${git_tag}'\") +endif() + +set(init_submodules ${init_submodules}) +if(init_submodules) + execute_process( + COMMAND \"${git_EXECUTABLE}\" ${git_options} submodule update ${git_submodules_recurse} --init ${git_submodules} + WORKING_DIRECTORY \"${work_dir}/${src_name}\" + RESULT_VARIABLE error_code + ) +endif() +if(error_code) + message(FATAL_ERROR \"Failed to update submodules in: '${work_dir}/${src_name}'\") +endif() + +# Complete success, update the script-last-run stamp file: +# +execute_process( + COMMAND \${CMAKE_COMMAND} -E copy + \"${gitclone_infofile}\" + \"${gitclone_stampfile}\" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR \"Failed to copy script-last-run stamp file: '${gitclone_stampfile}'\") +endif() -function(_ep_write_hgclone_script - script_filename - source_dir - hg_EXECUTABLE - hg_repository - hg_tag - src_name - work_dir - hgclone_infofile - hgclone_stampfile - quiet) +" +) +endfunction() + +function(_ep_write_hgclone_script script_filename source_dir hg_EXECUTABLE hg_repository hg_tag src_name work_dir hgclone_infofile hgclone_stampfile) if("${hg_tag}" STREQUAL "") message(FATAL_ERROR "Tag for hg checkout should not be empty.") endif() + file(WRITE ${script_filename} +" +if(NOT \"${hgclone_infofile}\" IS_NEWER_THAN \"${hgclone_stampfile}\") + message(STATUS \"Avoiding repeated hg clone, stamp file is up to date: '${hgclone_stampfile}'\") + return() +endif() - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/hgclone.cmake.in - ${script_filename} - @ONLY +execute_process( + COMMAND \${CMAKE_COMMAND} -E rm -rf \"${source_dir}\" + RESULT_VARIABLE error_code ) +if(error_code) + message(FATAL_ERROR \"Failed to remove directory: '${source_dir}'\") +endif() -endfunction() +execute_process( + COMMAND \"${hg_EXECUTABLE}\" clone -U \"${hg_repository}\" \"${src_name}\" + WORKING_DIRECTORY \"${work_dir}\" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR \"Failed to clone repository: '${hg_repository}'\") +endif() +execute_process( + COMMAND \"${hg_EXECUTABLE}\" update ${hg_tag} + WORKING_DIRECTORY \"${work_dir}/${src_name}\" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR \"Failed to checkout tag: '${hg_tag}'\") +endif() -function(_ep_write_gitupdate_script - script_filename - git_EXECUTABLE - git_tag - git_remote_name - init_submodules - git_submodules_recurse - git_submodules - git_repository - work_dir - git_update_strategy - quiet) +# 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() +" +) + +endfunction() + + +function(_ep_write_gitupdate_script script_filename git_EXECUTABLE git_tag git_remote_name init_submodules git_submodules_recurse git_submodules git_repository work_dir git_update_strategy) if("${git_tag}" STREQUAL "") message(FATAL_ERROR "Tag for git checkout should not be empty.") endif() @@ -1365,56 +1408,13 @@ function(_ep_write_gitupdate_script endif() configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitupdate.cmake.in" - "${script_filename}" - @ONLY - ) -endfunction() - -function(_ep_write_hgupdate_script - script_filename - hg_EXECUTABLE - hg_tag - work_dir - quiet) - - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/hgupdate.cmake.in - ${script_filename} - @ONLY - ) - -endfunction() - -function(_ep_write_copydir_script - script_filename - from_dir - to_dir) - - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/copydir.cmake.in" - "${script_filename}" - @ONLY + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-gitupdate.cmake.in" + "${script_filename}" + @ONLY ) endfunction() -function(_ep_write_downloadfile_script - script_filename - REMOTE - LOCAL - timeout - inactivity_timeout - no_progress - hash - tls_verify - tls_cainfo - userpwd - http_headers - netrc - netrc_file - extract_script_filename - quiet) - +function(_ep_write_downloadfile_script script_filename REMOTE LOCAL timeout inactivity_timeout no_progress hash tls_verify tls_cainfo userpwd http_headers netrc netrc_file) if(timeout) set(TIMEOUT_ARGS TIMEOUT ${timeout}) set(TIMEOUT_MSG "${timeout} seconds") @@ -1431,7 +1431,7 @@ function(_ep_write_downloadfile_script endif() - if(no_progress OR quiet) + if(no_progress) set(SHOW_PROGRESS "") else() set(SHOW_PROGRESS "SHOW_PROGRESS") @@ -1518,19 +1518,13 @@ function(_ep_write_downloadfile_script # * USERPWD_ARGS # * HTTP_HEADERS_ARGS configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/download.cmake.in" - "${script_filename}" - @ONLY + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-download.cmake.in" + "${script_filename}" + @ONLY ) endfunction() -function(_ep_write_verifyfile_script - script_filename - LOCAL - hash - extract_script_filename - quiet) - +function(_ep_write_verifyfile_script script_filename LOCAL hash) _ep_get_hash_regex(_ep_hash_regex) if("${hash}" MATCHES "${_ep_hash_regex}") set(ALGO "${CMAKE_MATCH_1}") @@ -1544,22 +1538,15 @@ function(_ep_write_verifyfile_script # * ALGO # * EXPECT_VALUE # * LOCAL - # * extract_script_filename configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/verify.cmake.in" - "${script_filename}" - @ONLY + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject-verify.cmake.in" + "${script_filename}" + @ONLY ) endfunction() -function(_ep_write_extractfile_script - script_filename - name - filename - directory - quiet) - +function(_ep_write_extractfile_script script_filename name filename directory) set(args "") if(filename MATCHES "(\\.|=)(7z|tar\\.bz2|tar\\.gz|tar\\.xz|tbz2|tgz|txz|zip)$") @@ -1571,34 +1558,77 @@ function(_ep_write_extractfile_script endif() if(args STREQUAL "") - message(FATAL_ERROR - "Do not know how to extract '${filename}' -- known types are: " - ".7z, .tar, .tar.bz2, .tar.gz, .tar.xz, .tbz2, .tgz, .txz and .zip") + message(SEND_ERROR "error: do not know how to extract '${filename}' -- known types are .7z, .tar, .tar.bz2, .tar.gz, .tar.xz, .tbz2, .tgz, .txz and .zip") + return() endif() - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/extractfile.cmake.in" - "${script_filename}" - @ONLY - ) + file(WRITE ${script_filename} +"# Make file names absolute: +# +get_filename_component(filename \"${filename}\" ABSOLUTE) +get_filename_component(directory \"${directory}\" ABSOLUTE) -endfunction() +message(STATUS \"extracting... + src='\${filename}' + dst='\${directory}'\") + +if(NOT EXISTS \"\${filename}\") + message(FATAL_ERROR \"error: file to extract does not exist: '\${filename}'\") +endif() +# Prepare a space for extracting: +# +set(i 1234) +while(EXISTS \"\${directory}/../ex-${name}\${i}\") + math(EXPR i \"\${i} + 1\") +endwhile() +set(ut_dir \"\${directory}/../ex-${name}\${i}\") +file(MAKE_DIRECTORY \"\${ut_dir}\") + +# Extract it: +# +message(STATUS \"extracting... [tar ${args}]\") +execute_process(COMMAND \${CMAKE_COMMAND} -E tar ${args} \${filename} + WORKING_DIRECTORY \${ut_dir} + RESULT_VARIABLE rv) + +if(NOT rv EQUAL 0) + message(STATUS \"extracting... [error clean up]\") + file(REMOVE_RECURSE \"\${ut_dir}\") + message(FATAL_ERROR \"error: extract of '\${filename}' failed\") +endif() -# This function is an implementation detail of ExternalProject_Add() and -# _ep_do_preconfigure_steps_now(). +# Analyze what came out of the tar file: # -# The function expects keyword arguments to have already been parsed into -# variables of the form _EP_<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... [analysis]\") +file(GLOB contents \"\${ut_dir}/*\") +list(REMOVE_ITEM contents \"\${ut_dir}/.DS_Store\") +list(LENGTH contents n) +if(NOT n EQUAL 1 OR NOT IS_DIRECTORY \"\${contents}\") + set(contents \"\${ut_dir}\") +endif() + +# Move \"the one\" directory to the final directory: # -# Variables will also be set in the calling scope to enable subsequently -# calling _ep_add_preconfigure_command() for the mkdir step. +message(STATUS \"extracting... [rename]\") +file(REMOVE_RECURSE \${directory}) +get_filename_component(contents \${contents} ABSOLUTE) +file(RENAME \${contents} \${directory}) + +# Clean up: # -function(_ep_prepare_directories name) +message(STATUS \"extracting... [clean up]\") +file(REMOVE_RECURSE \"\${ut_dir}\") - set(prefix ${_EP_PREFIX}) +message(STATUS \"extracting... done\") +" +) + +endfunction() + + +function(_ep_set_directories name) + get_property(prefix TARGET ${name} PROPERTY _EP_PREFIX) if(NOT prefix) get_property(prefix DIRECTORY PROPERTY EP_PREFIX) if(NOT prefix) @@ -1609,7 +1639,6 @@ function(_ep_prepare_directories name) endif() endif() if(prefix) - file(TO_CMAKE_PATH "${prefix}" prefix) set(tmp_default "${prefix}/tmp") set(download_default "${prefix}/src") set(source_default "${prefix}/src/${name}") @@ -1617,7 +1646,6 @@ function(_ep_prepare_directories name) set(stamp_default "${prefix}/src/${name}-stamp") set(install_default "${prefix}") else() - file(TO_CMAKE_PATH "${base}" base) set(tmp_default "${base}/tmp/${name}") set(download_default "${base}/Download/${name}") set(source_default "${base}/Source/${name}") @@ -1625,10 +1653,10 @@ function(_ep_prepare_directories name) set(stamp_default "${base}/Stamp/${name}") set(install_default "${base}/Install/${name}") endif() - - set(build_in_source "${_EP_BUILD_IN_SOURCE}") + get_property(build_in_source TARGET ${name} PROPERTY _EP_BUILD_IN_SOURCE) if(build_in_source) - if(DEFINED _EP_BINARY_DIR) + get_property(have_binary_dir TARGET ${name} PROPERTY _EP_BINARY_DIR SET) + if(have_binary_dir) message(FATAL_ERROR "External project ${name} has both BINARY_DIR and BUILD_IN_SOURCE!") endif() @@ -1639,78 +1667,65 @@ function(_ep_prepare_directories name) set(places stamp download source binary install tmp) foreach(var ${places}) string(TOUPPER "${var}" VAR) - set(${var}_dir "${_EP_${VAR}_DIR}") + get_property(${var}_dir TARGET ${name} PROPERTY _EP_${VAR}_DIR) if(NOT ${var}_dir) set(${var}_dir "${${var}_default}") endif() if(NOT IS_ABSOLUTE "${${var}_dir}") get_filename_component(${var}_dir "${top}/${${var}_dir}" ABSOLUTE) endif() - file(TO_CMAKE_PATH "${${var}_dir}" ${var}_dir) + set_property(TARGET ${name} PROPERTY _EP_${VAR}_DIR "${${var}_dir}") endforeach() # Special case for default log directory based on stamp directory. - set(log_dir "${_EP_LOG_DIR}") + get_property(log_dir TARGET ${name} PROPERTY _EP_LOG_DIR) if(NOT log_dir) - set(log_dir "${stamp_dir}") - else() - if(NOT IS_ABSOLUTE "${log_dir}") - get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE) - endif() + get_property(log_dir TARGET ${name} PROPERTY _EP_STAMP_DIR) + endif() + if(NOT IS_ABSOLUTE "${log_dir}") + get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE) endif() - file(TO_CMAKE_PATH "${log_dir}" log_dir) - list(APPEND places log) + set_property(TARGET ${name} PROPERTY _EP_LOG_DIR "${log_dir}") - set(source_subdir "${_EP_SOURCE_SUBDIR}") - if(source_subdir) - if(IS_ABSOLUTE "${source_subdir}") - message(FATAL_ERROR - "External project ${name} has non-relative SOURCE_SUBDIR!") - endif() - string(REPLACE "\\" "/" source_subdir "${source_subdir}") + get_property(source_subdir TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR) + if(NOT source_subdir) + set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "") + elseif(IS_ABSOLUTE "${source_subdir}") + message(FATAL_ERROR + "External project ${name} has non-relative SOURCE_SUBDIR!") + else() # Prefix with a slash so that when appended to the source directory, it # behaves as expected. - string(PREPEND source_subdir "/") + set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "/${source_subdir}") endif() - if(build_in_source) - set(binary_dir "${source_dir}${source_subdir}") + get_property(source_dir TARGET ${name} PROPERTY _EP_SOURCE_DIR) + if(source_subdir) + set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}/${source_subdir}") + else() + set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}") + endif() endif() - # This script will be used both here and by the mkdir step. We create the - # directories now at configure time and ensure they exists again at build - # time (since somebody might remove one of the required directories and try - # to rebuild without re-running cmake). They need to exist now at makefile - # generation time for Borland make and wmake so that CMake may generate - # makefiles with "cd C:\short\paths\with\no\spaces" commands in them. - set(script_filename "${tmp_dir}/${name}-mkdirs.cmake") - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/mkdirs.cmake.in - ${script_filename} - @ONLY - ) - include(${script_filename}) - - set(comment "Creating directories for '${name}'") - set(cmd ${CMAKE_COMMAND} -P ${script_filename}) - - # Provide variables that can be used later to create a custom command or - # invoke the step directly - set(_EPcomment_MKDIR "${comment}" PARENT_SCOPE) - set(_EPcommand_MKDIR "${cmd}" PARENT_SCOPE) - set(_EPalways_MKDIR FALSE PARENT_SCOPE) - set(_EPexcludefrommain_MKDIR FALSE PARENT_SCOPE) - set(_EPdepends_MKDIR "" PARENT_SCOPE) - set(_EPdependees_MKDIR "" PARENT_SCOPE) - + # Make the directories at CMake configure time *and* add a custom command + # to make them at build time. They need to exist at makefile generation + # time for Borland make and wmake so that CMake may generate makefiles + # with "cd C:\short\paths\with\no\spaces" commands in them. + # + # Additionally, the add_custom_command is still used in case somebody + # removes one of the necessary directories and tries to rebuild without + # re-running cmake. foreach(var ${places}) string(TOUPPER "${var}" VAR) - set(_EP_${VAR}_DIR "${${var}_dir}" PARENT_SCOPE) + get_property(dir TARGET ${name} PROPERTY _EP_${VAR}_DIR) + file(MAKE_DIRECTORY "${dir}") + if(NOT EXISTS "${dir}") + message(FATAL_ERROR "dir '${dir}' does not exist after file(MAKE_DIRECTORY)") + endif() endforeach() - set(_EP_SOURCE_SUBDIR "${source_subdir}" PARENT_SCOPE) - endfunction() + # IMPORTANT: this MUST be a macro and not a function because of the # in-place replacements that occur in each ${var} # @@ -1726,17 +1741,6 @@ macro(_ep_replace_location_tags target_name) endforeach() endmacro() -macro(_ep_replace_location_tags_from_vars) - set(vars ${ARGN}) - foreach(var ${vars}) - if(${var}) - foreach(dir SOURCE_DIR SOURCE_SUBDIR BINARY_DIR INSTALL_DIR TMP_DIR DOWNLOAD_DIR DOWNLOADED_FILE LOG_DIR) - string(REPLACE "<${dir}>" "${_EP_${dir}}" ${var} "${${var}}") - endforeach() - endif() - endforeach() -endmacro() - function(_ep_command_line_to_initial_cache var args force) set(script_initial_cache "") @@ -1919,24 +1923,17 @@ function(_ep_get_build_command name step cmd_var) set(${cmd_var} "${cmd}" PARENT_SCOPE) endfunction() -function(_ep_write_log_script name step genex_supported cmd_var) - - set(log_dir "${_EP_LOG_DIR}") - set(tmp_dir "${_EP_TMP_DIR}") - - if(genex_supported) - set(script_base ${tmp_dir}/${name}-${step}-$<CONFIG>) - else() - set(script_base ${tmp_dir}/${name}-${step}) - endif() +function(_ep_write_log_script name step cmd_var) + ExternalProject_Get_Property(${name} log_dir) + ExternalProject_Get_Property(${name} stamp_dir) set(command "${${cmd_var}}") set(make "") set(code_cygpath_make "") - if(command MATCHES [[^\$\(MAKE\)]]) + if(command MATCHES "^\\$\\(MAKE\\)") # GNU make recognizes the string "$(MAKE)" as recursive make, so # ensure that it appears directly in the makefile. - string(REGEX REPLACE [[^\$\(MAKE\)]] [[${make}]] command "${command}") + string(REGEX REPLACE "^\\$\\(MAKE\\)" "\${make}" command "${command}") set(make "-Dmake=$(MAKE)") if(WIN32 AND NOT CYGWIN) @@ -1958,8 +1955,8 @@ endif() endif() set(config "") - if("${CMAKE_CFG_INTDIR}" MATCHES [[^\$]]) - string(REPLACE "${CMAKE_CFG_INTDIR}" [[${config}]] command "${command}") + if("${CMAKE_CFG_INTDIR}" MATCHES "^\\$") + string(REPLACE "${CMAKE_CFG_INTDIR}" "\${config}" command "${command}") set(config "-Dconfig=${CMAKE_CFG_INTDIR}") endif() @@ -1993,22 +1990,15 @@ endif() endif() endforeach() string(APPEND code "set(command \"${cmd}\")${code_execute_process}") - if(genex_supported) - file(GENERATE OUTPUT "${script_base}-impl.cmake" CONTENT "${code}") - else() - file(WRITE "${script_base}-impl.cmake" "${code}") - endif() - set(command ${CMAKE_COMMAND} - -D "make=\${make}" - -D "config=\${config}" - -P ${script_base}-impl.cmake - ) + file(GENERATE OUTPUT "${stamp_dir}/${name}-${step}-$<CONFIG>-impl.cmake" CONTENT "${code}") + set(command ${CMAKE_COMMAND} "-Dmake=\${make}" "-Dconfig=\${config}" -P ${stamp_dir}/${name}-${step}-$<CONFIG>-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}) - set(log_merged "${_EP_LOG_MERGED_STDOUTERR}") - set(log_output_on_failure "${_EP_LOG_OUTPUT_ON_FAILURE}") + get_property(log_merged TARGET ${name} PROPERTY _EP_LOG_MERGED_STDOUTERR) + get_property(log_output_on_failure TARGET ${name} PROPERTY _EP_LOG_OUTPUT_ON_FAILURE) if (log_merged) set(stdout_log "${logbase}.log") set(stderr_log "${logbase}.log") @@ -2067,19 +2057,14 @@ if(result) message(FATAL_ERROR \"\${msg}\") endif() else() - if(NOT \"${CMAKE_GENERATOR}\" MATCHES \"Ninja\" AND NOT \"${_EP_QUIET}\") + if(NOT \"${CMAKE_GENERATOR}\" MATCHES \"Ninja\") set(msg \"${name} ${step} command succeeded. See also ${logbase}-*.log\") message(STATUS \"\${msg}\") endif() endif() ") - set(script_filename ${script_base}.cmake) - if(genex_supported) - file(GENERATE OUTPUT ${script_filename} CONTENT "${code}") - else() - file(WRITE ${script_filename} "${code}") - endif() - set(command ${CMAKE_COMMAND} ${make} ${config} -P ${script_filename}) + file(GENERATE OUTPUT "${script}" CONTENT "${code}") + set(command ${CMAKE_COMMAND} ${make} ${config} -P ${script}) set(${cmd_var} "${command}" PARENT_SCOPE) endfunction() @@ -2257,7 +2242,8 @@ function(ExternalProject_Add_Step name step) LOG USES_TERMINAL ) - _ep_parse_arguments("${keywords}" ${name} _EP_${step}_ "${ARGN}") + _ep_parse_arguments(ExternalProject_Add_Step "${keywords}" + ${name} _EP_${step}_ "${ARGN}") get_property(independent TARGET ${name} PROPERTY _EP_${step}_INDEPENDENT) if(independent STREQUAL "") @@ -2368,8 +2354,7 @@ function(ExternalProject_Add_Step name step) # Wrap with log script? get_property(log TARGET ${name} PROPERTY _EP_${step}_LOG) if(command AND log) - set(genex_supported TRUE) - _ep_write_log_script(${name} ${step} ${genex_supported} command) + _ep_write_log_script(${name} ${step} command) endif() if("${command}" STREQUAL "") @@ -2501,6 +2486,27 @@ function(ExternalProject_Add_StepDependencies name step) endfunction() + +function(_ep_add_mkdir_command name) + ExternalProject_Get_Property(${name} + source_dir binary_dir install_dir stamp_dir download_dir tmp_dir log_dir) + + _ep_get_configuration_subdir_suffix(cfgdir) + + ExternalProject_Add_Step(${name} mkdir + INDEPENDENT TRUE + COMMENT "Creating directories for '${name}'" + COMMAND ${CMAKE_COMMAND} -E make_directory ${source_dir} + COMMAND ${CMAKE_COMMAND} -E make_directory ${binary_dir} + COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir} + COMMAND ${CMAKE_COMMAND} -E make_directory ${tmp_dir} + COMMAND ${CMAKE_COMMAND} -E make_directory ${stamp_dir}${cfgdir} + COMMAND ${CMAKE_COMMAND} -E make_directory ${download_dir} + COMMAND ${CMAKE_COMMAND} -E make_directory ${log_dir} + ) +endfunction() + + function(_ep_is_dir_empty dir empty_var) file(GLOB gr "${dir}/*") if("${gr}" STREQUAL "") @@ -2511,277 +2517,114 @@ function(_ep_is_dir_empty dir empty_var) endfunction() function(_ep_get_git_submodules_recurse git_submodules_recurse) - - if(NOT DEFINED _EP_GIT_SUBMODULES_RECURSE OR _EP_GIT_SUBMODULES_RECURSE) - # The git submodule update '--recursive' flag requires git >= v1.6.5 - if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5) - message(FATAL_ERROR - "git version 1.6.5 or later required for --recursive flag with " - "'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'") - endif() - set(${git_submodules_recurse} "--recursive" PARENT_SCOPE) + # Checks for GIT_SUBMODULES_RECURSE property + # Default is ON, which sets git_submodules_recurse output variable to "--recursive" + # Otherwise, the output variable is set to an empty value "" + get_property(git_submodules_recurse_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES_RECURSE SET) + if(NOT git_submodules_recurse_set) + set(recurseFlag "--recursive") else() - set(${git_submodules_recurse} "" PARENT_SCOPE) - endif() - -endfunction() - -function(_ep_write_command_script - script_filename - commands - work_dir - genex_supported - quiet - have_commands_var) - - set(sep "${_EP_LIST_SEPARATOR}") - if(sep AND commands) - string(REPLACE "${sep}" "\\;" commands "${commands}") - endif() - _ep_replace_location_tags_from_vars(commands) - - file(READ - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/customcommand.cmake.in - exec_command_template - ) - set(script_content) - set(this_command) - foreach(token IN LISTS commands) - if(token STREQUAL "COMMAND") - if("${this_command}" STREQUAL "") - # Silently skip empty commands - continue() - endif() - string(CONFIGURE "${exec_command_template}" content @ONLY) - string(APPEND script_content "${content}") - set(this_command) + get_property(git_submodules_recurse_value TARGET ${name} PROPERTY _EP_GIT_SUBMODULES_RECURSE) + if(git_submodules_recurse_value) + set(recurseFlag "--recursive") else() - # Ensure we quote every token so we preserve empty items, quotes, etc - string(APPEND this_command " [==[${token}]==]") + set(recurseFlag "") endif() - endforeach() - - if(NOT "${this_command}" STREQUAL "") - string(CONFIGURE "${exec_command_template}" content @ONLY) - string(APPEND script_content "${content}") - endif() - - if(script_content STREQUAL "") - set(${have_commands_var} FALSE PARENT_SCOPE) - else() - set(${have_commands_var} TRUE PARENT_SCOPE) - file(READ - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/customcommand_preamble.cmake.in - exec_command_preamble - ) - string(CONFIGURE "${exec_command_preamble}" exec_command_preamble @ONLY) - string(PREPEND script_content "${exec_command_preamble}") endif() + set(${git_submodules_recurse} "${recurseFlag}" PARENT_SCOPE) - if(genex_supported) - # Only written at generation phase. This will only change the file's - # timestamp if the contents change. - file(GENERATE OUTPUT "${script_filename}" CONTENT "${script_content}") - else() - # Update the file immediately, needed if script has to be invoked in the - # configure phase (e.g. via FetchContent). We need to be careful to avoid - # updating the timestamp if the file contents don't change. The file(WRITE) - # command always updates the file, so avoid it if we don't need to call it. - set(doWrite TRUE) - if(EXISTS "${script_filename}") - file(READ "${script_filename}" existing_content) - if(existing_content STREQUAL script_content) - set(doWrite FALSE) - endif() - endif() - if(doWrite) - file(WRITE "${script_filename}" "${script_content}") - endif() + # The git submodule update '--recursive' flag requires git >= v1.6.5 + if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5) + message(FATAL_ERROR "error: git version 1.6.5 or later required for --recursive flag with 'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'") endif() - endfunction() -function(_ep_add_preconfigure_command name step) - string(TOUPPER "${step}" STEP) - set(uses_terminal "${_EP_USES_TERMINAL_${STEP}}") - if(uses_terminal) - set(uses_terminal TRUE) - else() - set(uses_terminal FALSE) - endif() - - # Pre-configure steps are expected to set their own work_dir - ExternalProject_Add_Step(${name} ${step} - INDEPENDENT TRUE - COMMENT "${_EPcomment_${STEP}}" - COMMAND ${_EPcommand_${STEP}} - ALWAYS ${_EPalways_${STEP}} - EXCLUDE_FROM_MAIN ${_EPexcludefrommain_${STEP}} - DEPENDS ${_EPdepends_${STEP}} - DEPENDEES ${_EPdependees_${STEP}} - USES_TERMINAL ${uses_terminal} - ) -endfunction() +function(_ep_add_download_command name) + ExternalProject_Get_Property(${name} source_dir stamp_dir download_dir tmp_dir) -# This function is an implementation detail of ExternalProject_Add() and -# _ep_do_preconfigure_steps_now(). -# -# The function expects keyword arguments to have already been parsed into -# variables of the form _EP_<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) + get_property(cmd_set TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND SET) + get_property(cmd TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND) + get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY) + get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY) + get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY) + get_property(hg_repository TARGET ${name} PROPERTY _EP_HG_REPOSITORY ) + get_property(url TARGET ${name} PROPERTY _EP_URL) + get_property(fname TARGET ${name} PROPERTY _EP_DOWNLOAD_NAME) - set(stamp_dir "${_EP_STAMP_DIR}") - set(tmp_dir "${_EP_TMP_DIR}") - set(source_dir "${_EP_SOURCE_DIR}") - set(download_dir "${_EP_DOWNLOAD_DIR}") - set(quiet "${_EP_QUIET}") + # TODO: Perhaps file:// should be copied to download dir before extraction. + string(REGEX REPLACE "file://" "" url "${url}") + set(depends) set(comment) + set(work_dir) - # We handle the log setting directly here rather than deferring it to - # be handled by ExternalProject_Add_Step() - set(log "${_EP_LOG_DOWNLOAD}") - if(log) - set(script_filename ${tmp_dir}/${name}-download-impl.cmake) - set(log TRUE) - set(quiet FALSE) # Already quiet as a result of log being enabled - else() - set(script_filename ${tmp_dir}/${name}-download.cmake) - set(log FALSE) - endif() - - set(repo_info_file ${tmp_dir}/${name}-download-repoinfo.txt) - set(last_run_file ${stamp_dir}/${name}-download-lastrun.txt) - set(script_does_something TRUE) - - # We use configure_file() to write the repo_info_file below so that the - # file's timestamp is not updated if we don't change the contents of an - # existing file. - - if(DEFINED _EP_DOWNLOAD_COMMAND) + if(cmd_set) set(work_dir ${download_dir}) - set(repo_info_content -"method=custom -command=${_EP_DOWNLOAD_COMMAND} -source_dir=${source_dir} -work_dir=${work_dir} -") - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} - @ONLY - ) - - _ep_write_command_script( - "${script_filename}" - "${_EP_DOWNLOAD_COMMAND}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) - set(comment "Performing download step (custom command) for '${name}'") - - elseif(DEFINED _EP_CVS_REPOSITORY) + elseif(cvs_repository) find_package(CVS QUIET) if(NOT CVS_EXECUTABLE) message(FATAL_ERROR "error: could not find cvs for checkout of ${name}") endif() - if("${_EP_CVS_MODULE}" STREQUAL "") + get_target_property(cvs_module ${name} _EP_CVS_MODULE) + if(NOT cvs_module) message(FATAL_ERROR "error: no CVS_MODULE") endif() - set(repo_info_content -"method=cvs -repository=${_EP_CVS_REPOSITORY} -module=${_EP_CVS_MODULE} -tag=${_EP_CVS_TAG} -source_dir=${source_dir} -") + get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG) + + set(repository ${cvs_repository}) + set(module ${cvs_module}) + set(tag ${cvs_tag}) configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-cvsinfo.txt" @ONLY - ) + ) get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) - - set(cmd "${CVS_EXECUTABLE}" -d "${_EP_CVS_REPOSITORY}" -q - co ${_EP_CVS_TAG} -d "${src_name}" "${_EP_CVS_MODULE}" - ) - _ep_write_command_script( - "${script_filename}" - "${cmd}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) set(comment "Performing download step (CVS checkout) for '${name}'") - - elseif(DEFINED _EP_SVN_REPOSITORY) + set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q co ${cvs_tag} -d ${src_name} ${cvs_module}) + list(APPEND depends ${stamp_dir}/${name}-cvsinfo.txt) + elseif(svn_repository) find_package(Subversion QUIET) if(NOT Subversion_SVN_EXECUTABLE) message(FATAL_ERROR "error: could not find svn for checkout of ${name}") endif() - set(svn_repository "${_EP_SVN_REPOSITORY}") - set(svn_revision "${_EP_SVN_REVISION}") - set(svn_username "${_EP_SVN_USERNAME}") - set(svn_password "${_EP_SVN_PASSWORD}") - set(svn_trust_cert "${_EP_SVN_TRUST_CERT}") + 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_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} -") + set(repository "${svn_repository} user=${svn_username} password=${svn_password}") + set(module) + set(tag ${svn_revision}) configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-svninfo.txt" @ONLY - ) + ) get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) - - set(cmd "${Subversion_SVN_EXECUTABLE}" co "${svn_repository}" - ${svn_revision} ${svn_options} "${src_name}" - ) - _ep_write_command_script( - "${script_filename}" - "${cmd}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) set(comment "Performing download step (SVN checkout) for '${name}'") - - elseif(DEFINED _EP_GIT_REPOSITORY) + set(svn_user_pw_args "") + if(DEFINED svn_username) + set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") + endif() + if(DEFINED svn_password) + set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") + endif() + if(svn_trust_cert) + set(svn_trust_cert_args --trust-server-cert) + endif() + set(cmd ${Subversion_SVN_EXECUTABLE} co ${svn_repository} ${svn_revision} + --non-interactive ${svn_trust_cert_args} ${svn_user_pw_args} ${src_name}) + list(APPEND depends ${stamp_dir}/${name}-svninfo.txt) + elseif(git_repository) # FetchContent gives us these directly, so don't try to recompute them if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) unset(CMAKE_MODULE_PATH) # Use CMake builtin find module @@ -2791,133 +2634,111 @@ source_dir=${source_dir} endif() endif() - set(git_tag "${_EP_GIT_TAG}") + _ep_get_git_submodules_recurse(git_submodules_recurse) + + get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) if(NOT git_tag) set(git_tag "master") endif() - set(git_remote_name "${_EP_GIT_REMOTE_NAME}") - if(NOT git_remote_name) - set(git_remote_name "origin") - endif() - set(git_init_submodules TRUE) - if(DEFINED _EP_GIT_SUBMODULES) - set(git_submodules "${_EP_GIT_SUBMODULES}") - if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") + get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET) + if(git_submodules_set) + get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES) + if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") set(git_init_submodules FALSE) endif() endif() - _ep_get_git_submodules_recurse(git_submodules_recurse) - set(tls_verify "${_EP_TLS_VERIFY}") + get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME) + if(NOT git_remote_name) + set(git_remote_name "origin") + endif() + + get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY) if("x${tls_verify}" STREQUAL "x" AND DEFINED CMAKE_TLS_VERIFY) set(tls_verify "${CMAKE_TLS_VERIFY}") endif() - set(git_shallow "${_EP_GIT_SHALLOW}") - set(git_progress "${_EP_GIT_PROGRESS}") - set(git_config "${_EP_GIT_CONFIG}") + get_property(git_shallow TARGET ${name} PROPERTY _EP_GIT_SHALLOW) + get_property(git_progress TARGET ${name} PROPERTY _EP_GIT_PROGRESS) + get_property(git_config TARGET ${name} PROPERTY _EP_GIT_CONFIG) # Make checkouts quiet when checking out a git hash (this avoids the # very noisy detached head message) list(PREPEND git_config advice.detachedHead=false) - # For the git clone operation, only the repository and remote should be - # recorded in a configured repository info file. If the repo or remote - # name changes, the clone script should be run again. But if only the tag + # For the download step, and the git clone operation, only the repository + # should be recorded in a configured RepositoryInfo file. If the repo + # changes, the clone script should be run again. But if only the tag # changes, avoid running the clone script again. Let the 'always' running # update step checkout the new tag. - set(repo_info_content -"method=git -repository=${_EP_GIT_REPOSITORY} -remote=${git_remote_name} -source_dir=${source_dir} -") + # + set(repository ${git_repository}) + set(module) + set(tag ${git_remote_name}) configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-gitinfo.txt" @ONLY - ) + ) get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) # Since git clone doesn't succeed if the non-empty source_dir exists, - # the script will delete the source directory and then call git clone. - _ep_write_gitclone_script( - "${script_filename}" - "${source_dir}" - "${GIT_EXECUTABLE}" - "${_EP_GIT_REPOSITORY}" - "${git_tag}" - "${git_remote_name}" - "${git_init_submodules}" - "${git_submodules_recurse}" - "${git_submodules}" - "${git_shallow}" - "${git_progress}" - "${git_config}" - "${src_name}" - "${work_dir}" - "${repo_info_file}" - "${last_run_file}" - "${tls_verify}" - "${quiet}" - ) + # create a cmake script to invoke as download command. + # The script will delete the source directory and then call git clone. + # + _ep_write_gitclone_script(${tmp_dir}/${name}-gitclone.cmake ${source_dir} + ${GIT_EXECUTABLE} ${git_repository} ${git_tag} ${git_remote_name} ${git_init_submodules} "${git_submodules_recurse}" "${git_submodules}" "${git_shallow}" "${git_progress}" "${git_config}" ${src_name} ${work_dir} + ${stamp_dir}/${name}-gitinfo.txt ${stamp_dir}/${name}-gitclone-lastrun.txt "${tls_verify}" + ) set(comment "Performing download step (git clone) for '${name}'") - - elseif(DEFINED _EP_HG_REPOSITORY) + set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitclone.cmake) + list(APPEND depends ${stamp_dir}/${name}-gitinfo.txt) + elseif(hg_repository) find_package(Hg QUIET) if(NOT HG_EXECUTABLE) message(FATAL_ERROR "error: could not find hg for clone of ${name}") endif() - set(hg_tag "${_EP_HG_TAG}") + get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG) if(NOT hg_tag) set(hg_tag "tip") endif() - # For the hg clone operation, only the repository should be recorded in a - # configured repository info file. If the repo changes, the clone script - # should be run again. But if only the tag changes, avoid running the - # clone script again. Let the 'always' running update step checkout the - # new tag. - set(repo_info_content -"method=hg -repository=${_EP_HG_REPOSITORY} -source_dir=${source_dir} -") + # For the download step, and the hg clone operation, only the repository + # should be recorded in a configured RepositoryInfo file. If the repo + # changes, the clone script should be run again. But if only the tag + # changes, avoid running the clone script again. Let the 'always' running + # update step checkout the new tag. + # + set(repository ${hg_repository}) + set(module) + set(tag) configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-hginfo.txt" @ONLY - ) + ) get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) # Since hg clone doesn't succeed if the non-empty source_dir exists, - # the script will delete the source directory and then call hg clone. - _ep_write_hgclone_script( - "${script_filename}" - "${source_dir}" - "${HG_EXECUTABLE}" - "${_EP_HG_REPOSITORY}" - "${hg_tag}" - "${src_name}" - "${work_dir}" - "${repo_info_file}" - "${last_run_file}" - "${quiet}" - ) + # create a cmake script to invoke as download command. + # The script will delete the source directory and then call hg clone. + # + _ep_write_hgclone_script(${tmp_dir}/${name}-hgclone.cmake ${source_dir} + ${HG_EXECUTABLE} ${hg_repository} ${hg_tag} ${src_name} ${work_dir} + ${stamp_dir}/${name}-hginfo.txt ${stamp_dir}/${name}-hgclone-lastrun.txt + ) set(comment "Performing download step (hg clone) for '${name}'") - - elseif(DEFINED _EP_URL) - set(url "${_EP_URL}") - # TODO: Perhaps file:// should be copied to download dir before extraction. - string(REGEX REPLACE "file://" "" url "${url}") - - set(hash "${_EP_URL_HASH}") + set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-hgclone.cmake) + list(APPEND depends ${stamp_dir}/${name}-hginfo.txt) + elseif(url) + get_filename_component(work_dir "${source_dir}" PATH) + get_property(hash TARGET ${name} PROPERTY _EP_URL_HASH) _ep_get_hash_regex(_ep_hash_regex) if(hash AND NOT "${hash}" MATCHES "${_ep_hash_regex}") _ep_get_hash_algos(_ep_hash_algos) @@ -2926,27 +2747,22 @@ source_dir=${source_dir} "but must be ALGO=value where ALGO is\n ${_ep_hash_algos}\n" "and value is a hex string.") endif() - set(md5 "${_EP_URL_MD5}") + get_property(md5 TARGET ${name} PROPERTY _EP_URL_MD5) if(md5 AND NOT "MD5=${md5}" MATCHES "${_ep_hash_regex}") message(FATAL_ERROR "URL_MD5 is set to\n ${md5}\nbut must be a hex string.") endif() if(md5 AND NOT hash) set(hash "MD5=${md5}") endif() - - set(repo_info_content -"method=url -url=${url} -hash=${hash} -source_dir=${source_dir} -") + set(repository "external project URL") + set(module "${url}") + set(tag "${hash}") configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} + "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in" + "${stamp_dir}/${name}-urlinfo.txt" @ONLY - ) - - set(fname "${_EP_DOWNLOAD_NAME}") + ) + list(APPEND depends ${stamp_dir}/${name}-urlinfo.txt) list(LENGTH url url_list_length) if(NOT "${url_list_length}" STREQUAL "1") @@ -2961,21 +2777,12 @@ source_dir=${source_dir} endif() if(IS_DIRECTORY "${url}") - get_filename_component(from_dir "${url}" ABSOLUTE) - _ep_write_copydir_script( - ${script_filename} - ${from_dir} - ${source_dir} - ) - set(steps "DIR copy") + get_filename_component(abs_dir "${url}" ABSOLUTE) + set(comment "Performing download step (DIR copy) for '${name}'") + set(cmd ${CMAKE_COMMAND} -E rm -rf ${source_dir} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir}) else() - set(no_extract "${_EP_DOWNLOAD_NO_EXTRACT}") - if(no_extract) - set(extract_script) - else() - set(extract_script "${tmp_dir}/extract-${name}.cmake") - endif() - + get_property(no_extract TARGET "${name}" PROPERTY _EP_DOWNLOAD_NO_EXTRACT) if("${url}" MATCHES "^[a-z]+://") # TODO: Should download and extraction be different steps? if("x${fname}" STREQUAL "x") @@ -2989,70 +2796,55 @@ source_dir=${source_dir} # Fall back to a default file name. The actual file name does not # matter because it is used only internally and our extraction tool # inspects the file content directly. If it turns out the wrong URL - # was given, that will be revealed when the download is attempted - # (during the build unless we are being invoked by FetchContent) - # which is an easier place for users to diagnose than an error here. + # was given that will be revealed during the build which is an easier + # place for users to diagnose than an error here anyway. set(fname "archive.tar") endif() string(REPLACE ";" "-" fname "${fname}") set(file ${download_dir}/${fname}) - _ep_write_downloadfile_script( - "${script_filename}" - "${url}" - "${file}" - "${_EP_TIMEOUT}" - "${_EP_INACTIVITY_TIMEOUT}" - "${_EP_DOWNLOAD_NO_PROGRESS}" - "${hash}" - "${_EP_TLS_VERIFY}" - "${_EP_TLS_CAINFO}" - "${_EP_HTTP_USERNAME}:${_EP_HTTP_PASSWORD}" - "${_EP_HTTP_HEADER}" - "${_EP_NETRC}" - "${_EP_NETRC_FILE}" - "${extract_script}" - "${quiet}" - ) - if(no_extract) + get_property(timeout TARGET ${name} PROPERTY _EP_TIMEOUT) + get_property(inactivity_timeout TARGET ${name} PROPERTY _EP_INACTIVITY_TIMEOUT) + get_property(no_progress TARGET ${name} PROPERTY _EP_DOWNLOAD_NO_PROGRESS) + get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY) + get_property(tls_cainfo TARGET ${name} PROPERTY _EP_TLS_CAINFO) + get_property(netrc TARGET ${name} PROPERTY _EP_NETRC) + get_property(netrc_file TARGET ${name} PROPERTY _EP_NETRC_FILE) + get_property(http_username TARGET ${name} PROPERTY _EP_HTTP_USERNAME) + get_property(http_password TARGET ${name} PROPERTY _EP_HTTP_PASSWORD) + get_property(http_headers TARGET ${name} PROPERTY _EP_HTTP_HEADER) + set(download_script "${stamp_dir}/download-${name}.cmake") + _ep_write_downloadfile_script("${download_script}" "${url}" "${file}" "${timeout}" "${inactivity_timeout}" "${no_progress}" "${hash}" "${tls_verify}" "${tls_cainfo}" "${http_username}:${http_password}" "${http_headers}" "${netrc}" "${netrc_file}") + set(cmd ${CMAKE_COMMAND} -P "${download_script}" + COMMAND) + if (no_extract) set(steps "download and verify") - else() + else () set(steps "download, verify and extract") - endif() + endif () + set(comment "Performing download step (${steps}) for '${name}'") + file(WRITE "${stamp_dir}/verify-${name}.cmake" "") # already verified by 'download_script' else() set(file "${url}") - _ep_write_verifyfile_script( - "${script_filename}" - "${file}" - "${hash}" - "${extract_script}" - "${quiet}" - ) - if(no_extract) + if (no_extract) set(steps "verify") - else() + else () set(steps "verify and extract") - endif() - endif() - - if(no_extract) - set(_EP_DOWNLOADED_FILE ${file} PARENT_SCOPE) - else() - # This will be pulled in by the download/verify script written above - _ep_write_extractfile_script( - "${extract_script}" - "${name}" - "${file}" - "${source_dir}" - "${quiet}" - ) + endif () + set(comment "Performing download step (${steps}) for '${name}'") + _ep_write_verifyfile_script("${stamp_dir}/verify-${name}.cmake" "${file}" "${hash}") endif() + list(APPEND cmd ${CMAKE_COMMAND} -P ${stamp_dir}/verify-${name}.cmake) + if (NOT no_extract) + _ep_write_extractfile_script("${stamp_dir}/extract-${name}.cmake" "${name}" "${file}" "${source_dir}") + list(APPEND cmd COMMAND ${CMAKE_COMMAND} -P ${stamp_dir}/extract-${name}.cmake) + else () + set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file}) + endif () endif() - set(comment "Performing download step (${steps}) for '${name}'") - else() _ep_is_dir_empty("${source_dir}" empty) if(${empty}) - message(FATAL_ERROR + message(SEND_ERROR "No download info given for '${name}' and its source directory:\n" " ${source_dir}\n" "is not an existing non-empty directory. Please specify one of:\n" @@ -3065,149 +2857,105 @@ source_dir=${source_dir} " * CVS_REPOSITORY and CVS_MODULE" ) endif() - set(repo_info_content -"method=source_dir -source_dir=${source_dir} -") - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in - ${repo_info_file} - @ONLY - ) - - set(comment "Skipping download step (SOURCE_DIR given) for '${name}'") - set(script_does_something FALSE) endif() - # Provide variables that can be used later to create a custom command or - # invoke the step directly - if(script_does_something) - set(cmd ${CMAKE_COMMAND} -P ${script_filename}) - if(log) - _ep_write_log_script(${name} download "${genex_supported}" cmd) - endif() - set(depends ${repo_info_file}) + get_property(log TARGET ${name} PROPERTY _EP_LOG_DOWNLOAD) + if(log) + set(log LOG 1) else() - set(cmd) - set(depends) - string(REPLACE "Performing" "Skipping" comment "${comment}") + set(log "") endif() - set(_EPcomment_DOWNLOAD "${comment}" PARENT_SCOPE) - set(_EPcommand_DOWNLOAD "${cmd}" PARENT_SCOPE) - set(_EPalways_DOWNLOAD FALSE PARENT_SCOPE) - set(_EPexcludefrommain_DOWNLOAD FALSE PARENT_SCOPE) - set(_EPdepends_DOWNLOAD "${depends}" PARENT_SCOPE) - set(_EPdependees_DOWNLOAD mkdir PARENT_SCOPE) + get_property(uses_terminal TARGET ${name} PROPERTY + _EP_USES_TERMINAL_DOWNLOAD) + if(uses_terminal) + set(uses_terminal USES_TERMINAL 1) + else() + set(uses_terminal "") + endif() + set(__cmdQuoted) + foreach(__item IN LISTS cmd) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + cmake_language(EVAL CODE " + ExternalProject_Add_Step(\${name} download + INDEPENDENT TRUE + COMMENT \${comment} + COMMAND ${__cmdQuoted} + WORKING_DIRECTORY \${work_dir} + DEPENDS \${depends} + DEPENDEES mkdir + ${log} + ${uses_terminal} + )" + ) endfunction() -function(_ep_get_update_disconnected var) - if(DEFINED _EP_UPDATE_DISCONNECTED) - set(update_disconnected "${_EP_UPDATE_DISCONNECTED}") +function(_ep_get_update_disconnected var name) + get_property(update_disconnected_set TARGET ${name} PROPERTY _EP_UPDATE_DISCONNECTED SET) + if(update_disconnected_set) + get_property(update_disconnected TARGET ${name} PROPERTY _EP_UPDATE_DISCONNECTED) else() get_property(update_disconnected DIRECTORY PROPERTY EP_UPDATE_DISCONNECTED) endif() set(${var} "${update_disconnected}" PARENT_SCOPE) endfunction() -# This function is an implementation detail of ExternalProject_Add() and -# _ep_do_preconfigure_steps_now(). -# -# The function expects keyword arguments to have already been parsed into -# variables of the form _EP_<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) - - set(tmp_dir "${_EP_TMP_DIR}") - set(source_dir "${_EP_SOURCE_DIR}") - set(quiet "${_EP_QUIET}") +function(_ep_add_update_command name) + ExternalProject_Get_Property(${name} source_dir tmp_dir) - set(comment) + get_property(cmd_set TARGET ${name} PROPERTY _EP_UPDATE_COMMAND SET) + get_property(cmd TARGET ${name} PROPERTY _EP_UPDATE_COMMAND) + get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY) + get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY) + get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY) + get_property(hg_repository TARGET ${name} PROPERTY _EP_HG_REPOSITORY ) - _ep_get_update_disconnected(update_disconnected) + _ep_get_update_disconnected(update_disconnected ${name}) - # We handle the log setting directly here rather than deferring it to - # be handled by ExternalProject_Add_Step() - set(log "${_EP_LOG_UPDATE}") - if(log) - set(script_filename ${tmp_dir}/${name}-update-impl.cmake) - set(log TRUE) - set(quiet FALSE) # Already quiet as a result of log being enabled - else() - set(script_filename ${tmp_dir}/${name}-update.cmake) - set(log FALSE) - endif() + set(work_dir) + set(comment) + set(always) - if(DEFINED _EP_UPDATE_COMMAND) + if(cmd_set) set(work_dir ${source_dir}) - _ep_write_command_script( - "${script_filename}" - "${_EP_UPDATE_COMMAND}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) - set(comment "Performing update step (custom command) for '${name}'") - - elseif(DEFINED _EP_CVS_REPOSITORY) + if(NOT "x${cmd}" STREQUAL "x") + set(always 1) + endif() + elseif(cvs_repository) if(NOT CVS_EXECUTABLE) message(FATAL_ERROR "error: could not find cvs for update of ${name}") endif() - set(work_dir ${source_dir}) - set(cmd "${CVS_EXECUTABLE}" -d "${_EP_CVS_REPOSITORY}" -q - up -dP ${_EP_CVS_TAG} - ) - _ep_write_command_script( - "${script_filename}" - "${cmd}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) set(comment "Performing update step (CVS update) for '${name}'") - - elseif(DEFINED _EP_SVN_REPOSITORY) + get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG) + set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag}) + set(always 1) + elseif(svn_repository) if(NOT Subversion_SVN_EXECUTABLE) message(FATAL_ERROR "error: could not find svn for update of ${name}") endif() - - set(svn_revision "${_EP_SVN_REVISION}") - set(svn_username "${_EP_SVN_USERNAME}") - set(svn_password "${_EP_SVN_PASSWORD}") - set(svn_trust_cert "${_EP_SVN_TRUST_CERT}") - - set(svn_options --non-interactive) - if(DEFINED _EP_SVN_USERNAME) - list(APPEND svn_options "--username=${svn_username}") + set(work_dir ${source_dir}) + set(comment "Performing update step (SVN update) for '${name}'") + get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION) + get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME) + get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD) + get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT) + set(svn_user_pw_args "") + if(DEFINED svn_username) + set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") endif() - if(DEFINED _EP_SVN_PASSWORD) - list(APPEND svn_options "--password=${svn_password}") + if(DEFINED svn_password) + set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") endif() if(svn_trust_cert) - list(APPEND svn_options --trust-server-cert) + set(svn_trust_cert_args --trust-server-cert) endif() - - set(work_dir ${source_dir}) - set(cmd "${Subversion_SVN_EXECUTABLE}" up ${svn_revision} ${svn_options}) - - _ep_write_command_script( - "${script_filename}" - "${cmd}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) - set(comment "Performing update step (SVN update) for '${name}'") - - elseif(DEFINED _EP_GIT_REPOSITORY) + set(cmd ${Subversion_SVN_EXECUTABLE} up ${svn_revision} + --non-interactive ${svn_trust_cert_args} ${svn_user_pw_args}) + set(always 1) + elseif(git_repository) # FetchContent gives us these directly, so don't try to recompute them if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) unset(CMAKE_MODULE_PATH) # Use CMake builtin find module @@ -3216,27 +2964,27 @@ function(_ep_prepare_update name genex_supported) message(FATAL_ERROR "error: could not find git for fetch of ${name}") endif() endif() - - set(git_tag "${_EP_GIT_TAG}") + set(work_dir ${source_dir}) + set(comment "Performing update step for '${name}'") + get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) if(NOT git_tag) set(git_tag "master") endif() - - set(git_remote_name "${_EP_GIT_REMOTE_NAME}") + get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME) if(NOT git_remote_name) set(git_remote_name "origin") endif() set(git_init_submodules TRUE) - if(DEFINED _EP_GIT_SUBMODULES) - set(git_submodules "${_EP_GIT_SUBMODULES}") + get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET) + if(git_submodules_set) + get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES) if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") set(git_init_submodules FALSE) endif() endif() - _ep_get_git_submodules_recurse(git_submodules_recurse) - set(git_update_strategy "${_EP_GIT_REMOTE_UPDATE_STRATEGY}") + get_property(git_update_strategy TARGET ${name} PROPERTY _EP_GIT_REMOTE_UPDATE_STRATEGY) if(NOT git_update_strategy) set(git_update_strategy "${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}") endif() @@ -3248,28 +2996,23 @@ function(_ep_prepare_update name genex_supported) message(FATAL_ERROR "'${git_update_strategy}' is not one of the supported strategies: ${strategies}") endif() - set(work_dir ${source_dir}) - _ep_write_gitupdate_script( - "${script_filename}" - "${GIT_EXECUTABLE}" - "${git_tag}" - "${git_remote_name}" - "${git_init_submodules}" - "${git_submodules_recurse}" - "${git_submodules}" - "${_EP_GIT_REPOSITORY}" - "${work_dir}" - "${git_update_strategy}" - "${quiet}" - ) - set(script_does_something TRUE) - set(comment "Performing update step (git update) for '${name}'") + _ep_get_git_submodules_recurse(git_submodules_recurse) - elseif(DEFINED _EP_HG_REPOSITORY) + _ep_write_gitupdate_script(${tmp_dir}/${name}-gitupdate.cmake + ${GIT_EXECUTABLE} ${git_tag} ${git_remote_name} ${git_init_submodules} "${git_submodules_recurse}" "${git_submodules}" ${git_repository} ${work_dir} ${git_update_strategy} + ) + set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitupdate.cmake) + set(always 1) + elseif(hg_repository) if(NOT HG_EXECUTABLE) message(FATAL_ERROR "error: could not find hg for pull of ${name}") endif() - + set(work_dir ${source_dir}) + set(comment "Performing update step (hg pull) for '${name}'") + get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG) + if(NOT hg_tag) + set(hg_tag "tip") + endif() if("${HG_VERSION_STRING}" STREQUAL "2.1") message(WARNING "Mercurial 2.1 does not distinguish an empty pull from a failed pull: http://mercurial.selenic.com/wiki/UpgradeNotes#A2.1.1:_revert_pull_return_code_change.2C_compile_issue_on_OS_X @@ -3277,117 +3020,87 @@ function(_ep_prepare_update name genex_supported) Update to Mercurial >= 2.1.1. ") endif() + set(cmd ${HG_EXECUTABLE} pull + COMMAND ${HG_EXECUTABLE} update ${hg_tag} + ) + set(always 1) + endif() - set(hg_tag "${_EP_HG_TAG}") - if(NOT hg_tag) - set(hg_tag "tip") - endif() - - set(work_dir ${source_dir}) - _ep_write_hgupdate_script( - "${script_filename}" - "${HG_EXECUTABLE}" - "${hg_tag}" - "${work_dir}" - "${quiet}" - ) - set(script_does_something TRUE) - set(comment "Performing update step (hg pull) for '${name}'") - + get_property(log TARGET ${name} PROPERTY _EP_LOG_UPDATE) + if(log) + set(log LOG 1) else() - set(script_does_something FALSE) + set(log "") endif() - # Provide variables that can be used later to create a custom command or - # invoke the step directly - if(script_does_something) - set(always TRUE) - set(cmd ${CMAKE_COMMAND} -P ${script_filename}) - if(log) - _ep_write_log_script(${name} update "${genex_supported}" cmd) - endif() + get_property(uses_terminal TARGET ${name} PROPERTY + _EP_USES_TERMINAL_UPDATE) + if(uses_terminal) + set(uses_terminal USES_TERMINAL 1) else() - set(always FALSE) - set(cmd) + set(uses_terminal "") endif() - set(_EPcomment_UPDATE "${comment}" PARENT_SCOPE) - set(_EPcommand_UPDATE "${cmd}" PARENT_SCOPE) - set(_EPalways_UPDATE "${always}" PARENT_SCOPE) - set(_EPexcludefrommain_UPDATE "${update_disconnected}" PARENT_SCOPE) - set(_EPdepends_UPDATE "" PARENT_SCOPE) - set(_EPdependees_UPDATE download PARENT_SCOPE) + set(__cmdQuoted) + foreach(__item IN LISTS cmd) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + cmake_language(EVAL CODE " + ExternalProject_Add_Step(${name} update + INDEPENDENT TRUE + COMMENT \${comment} + COMMAND ${__cmdQuoted} + ALWAYS \${always} + EXCLUDE_FROM_MAIN \${update_disconnected} + WORKING_DIRECTORY \${work_dir} + DEPENDEES download + ${log} + ${uses_terminal} + )" + ) endfunction() -# This function is an implementation detail of ExternalProject_Add() and -# _ep_do_preconfigure_steps_now(). -# -# The function expects keyword arguments to have already been parsed into -# variables of the form _EP_<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) - set(tmp_dir "${_EP_TMP_DIR}") - set(source_dir "${_EP_SOURCE_DIR}") - set(quiet "${_EP_QUIET}") +function(_ep_add_patch_command name) + ExternalProject_Get_Property(${name} source_dir) - _ep_get_update_disconnected(update_disconnected) - if(update_disconnected) - set(patch_dep download) - else() - set(patch_dep update) + get_property(cmd_set TARGET ${name} PROPERTY _EP_PATCH_COMMAND SET) + get_property(cmd TARGET ${name} PROPERTY _EP_PATCH_COMMAND) + + set(work_dir) + + if(cmd_set) + set(work_dir ${source_dir}) endif() - # We handle the log setting directly here rather than deferring it to - # be handled by ExternalProject_Add_Step() - set(log "${_EP_LOG_PATCH}") + get_property(log TARGET ${name} PROPERTY _EP_LOG_PATCH) if(log) - set(script_filename ${tmp_dir}/${name}-patch-impl.cmake) - set(log TRUE) - set(quiet FALSE) # Already quiet as a result of log being enabled + set(log LOG 1) else() - set(script_filename ${tmp_dir}/${name}-patch.cmake) - set(log FALSE) + set(log "") endif() - if(DEFINED _EP_PATCH_COMMAND) - set(work_dir "${source_dir}") - _ep_write_command_script( - "${script_filename}" - "${_EP_PATCH_COMMAND}" - "${work_dir}" - "${genex_supported}" - "${quiet}" - script_does_something - ) - if(script_does_something) - set(cmd ${CMAKE_COMMAND} -P ${script_filename}) - if(log) - _ep_write_log_script(${name} patch "${genex_supported}" cmd) - endif() - set(comment "Performing patch step (custom command) for '${name}'") - else() - set(cmd) - set(comment "Skipping patch step (empty custom command) for '${name}'") - endif() + _ep_get_update_disconnected(update_disconnected ${name}) + if(update_disconnected) + set(patch_dep download) else() - set(cmd) - set(comment "Skipping patch step (no custom command) for '${name}'") + set(patch_dep update) endif() - # Provide variables that can be used later to create a custom command or - # invoke the step directly - set(_EPcomment_PATCH "${comment}" PARENT_SCOPE) - set(_EPcommand_PATCH "${cmd}" PARENT_SCOPE) - set(_EPalways_PATCH FALSE PARENT_SCOPE) - set(_EPexcludefrommain_PATCH FALSE PARENT_SCOPE) - set(_EPdepends_PATCH "" PARENT_SCOPE) - set(_EPdependees_PATCH "${patch_dep}" PARENT_SCOPE) - + set(__cmdQuoted) + foreach(__item IN LISTS cmd) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + cmake_language(EVAL CODE " + ExternalProject_Add_Step(${name} patch + INDEPENDENT TRUE + COMMAND ${__cmdQuoted} + WORKING_DIRECTORY \${work_dir} + DEPENDEES \${patch_dep} + ${log} + )" + ) endfunction() function(_ep_get_file_deps var name) @@ -3497,11 +3210,7 @@ function(_ep_extract_configure_command var name) if(has_cmake_cache_default_args) _ep_command_line_to_initial_cache(script_initial_cache_default "${cmake_cache_default_args}" 0) endif() - _ep_write_initial_cache( - ${name} - "${_ep_cache_args_script}" - "${script_initial_cache_force}${script_initial_cache_default}" - ) + _ep_write_initial_cache(${name} "${_ep_cache_args_script}" "${script_initial_cache_force}${script_initial_cache_default}") list(APPEND cmd "-C${_ep_cache_args_script}") _ep_replace_location_tags(${name} _ep_cache_args_script) set(_ep_cache_args_script @@ -3533,11 +3242,10 @@ function(_ep_add_configure_command name) # used, cmake args or cmake generator) then re-run the configure step. # Fixes issue https://gitlab.kitware.com/cmake/cmake/-/issues/10258 # - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/cfgcmd.txt.in - ${tmp_dir}/${name}-cfgcmd.txt - @ONLY - ) + if(NOT EXISTS ${tmp_dir}/${name}-cfgcmd.txt.in) + file(WRITE ${tmp_dir}/${name}-cfgcmd.txt.in "cmd='\@cmd\@'\n") + endif() + configure_file(${tmp_dir}/${name}-cfgcmd.txt.in ${tmp_dir}/${name}-cfgcmd.txt) list(APPEND file_deps ${tmp_dir}/${name}-cfgcmd.txt) list(APPEND file_deps ${_ep_cache_args_script}) @@ -3748,8 +3456,51 @@ function(_ep_add_test_command name) endif() endfunction() -macro(_ep_get_add_keywords out_var) - set(${out_var} + +function(ExternalProject_Add name) + cmake_policy(GET CMP0097 _EP_CMP0097 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + cmake_policy(GET CMP0114 cmp0114 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12 AND NOT cmp0114 STREQUAL "NEW") + message(AUTHOR_WARNING + "Policy CMP0114 is not set to NEW. " + "In order to support the Xcode \"new build system\", " + "this project must be updated to set policy CMP0114 to NEW." + "\n" + "Since CMake is generating for the Xcode \"new build system\", " + "ExternalProject_Add will use policy CMP0114's NEW behavior anyway, " + "but the generated build system may not match what the project intends." + ) + set(cmp0114 "NEW") + endif() + + _ep_get_configuration_subdir_suffix(cfgdir) + + # Add a custom target for the external project. + set(cmf_dir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles) + _ep_get_complete_stampfile(${name} complete_stamp_file) + + cmake_policy(PUSH) + if(cmp0114 STREQUAL "NEW") + # To implement CMP0114 NEW behavior with Makefile generators, + # we need CMP0113 NEW behavior. + cmake_policy(SET CMP0113 NEW) + endif() + # The "ALL" option to add_custom_target just tells it to not set the + # EXCLUDE_FROM_ALL target property. Later, if the EXCLUDE_FROM_ALL + # argument was passed, we explicitly set it for the target. + add_custom_target(${name} ALL DEPENDS ${complete_stamp_file}) + cmake_policy(POP) + set_property(TARGET ${name} PROPERTY _EP_IS_EXTERNAL_PROJECT 1) + set_property(TARGET ${name} PROPERTY LABELS ${name}) + set_property(TARGET ${name} PROPERTY FOLDER "ExternalProjectTargets/${name}") + + set_property(TARGET ${name} PROPERTY _EP_CMP0114 "${cmp0114}") + + set(keywords # # Directory options # @@ -3878,145 +3629,14 @@ macro(_ep_get_add_keywords out_var) # LIST_SEPARATOR ) -endmacro() - - -# Internal function called by FetchContent to populate immediately. -# It only executes steps up to and including "patch". It takes the same -# arguments as ExternalProject_Add() plus one additional argument: QUIET. -# -# Not to be used outside of CMake. -# -function(_ep_do_preconfigure_steps_now name) - - cmake_policy(GET CMP0097 _EP_CMP0097 - PARENT_SCOPE # undocumented, do not use outside of CMake - ) - - set(genex_supported FALSE) - - _ep_get_add_keywords(keywords) - _ep_parse_arguments_to_vars("${keywords};QUIET" ${name} _EP_ "${ARGN}") - - _ep_get_update_disconnected(update_disconnected) - - _ep_prepare_directories(${name}) - _ep_prepare_download(${name} ${genex_supported}) - _ep_prepare_update(${name} ${genex_supported}) - _ep_prepare_patch(${name} ${genex_supported}) - - set(stamp_dir "${_EP_STAMP_DIR}") - set(tmp_dir "${_EP_TMP_DIR}") - - # Once any step has to run, all later steps have to be run too - set(need_to_run FALSE) - foreach(step IN ITEMS download update patch) - if(update_disconnected AND "${step}" STREQUAL "update") - continue() - endif() - - string(TOUPPER "${step}" STEP) - if("${_EPcommand_${STEP}}" STREQUAL "") - continue() - endif() - - set(stamp_file "${stamp_dir}/${name}-${step}") - set(script_file ${tmp_dir}/${name}-${step}.cmake) - - if(NOT EXISTS ${stamp_file}) - set(need_to_run TRUE) - endif() - - if(NOT need_to_run) - foreach(dep_file ${script_file} ${_EPdepends_${STEP}}) - # IS_NEWER_THAN is also true if the timestamps are the same. On some - # file systems, we only have second resolution timestamps and the - # likelihood of having the same timestamp is high. Use the negative - # form to ensure we actually get a true "is newer than" test. - if(NOT EXISTS ${dep_file} OR - NOT ${stamp_file} IS_NEWER_THAN ${dep_file}) - set(need_to_run TRUE) - break() - endif() - endforeach() - endif() - - if(need_to_run) - include(${script_file}) - file(TOUCH ${stamp_file}) - endif() - endforeach() - - if("${_EP_DOWNLOAD_NO_EXTRACT}") - file(COPY "${_EP_DOWNLOADED_FILE}" DESTINATION "${_EP_SOURCE_DIR}") - endif() - -endfunction() - -function(ExternalProject_Add name) - cmake_policy(GET CMP0097 _EP_CMP0097 - PARENT_SCOPE # undocumented, do not use outside of CMake - ) - cmake_policy(GET CMP0114 cmp0114 - PARENT_SCOPE # undocumented, do not use outside of CMake - ) - if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12 AND NOT cmp0114 STREQUAL "NEW") - message(AUTHOR_WARNING - "Policy CMP0114 is not set to NEW. " - "In order to support the Xcode \"new build system\", " - "this project must be updated to set policy CMP0114 to NEW." - "\n" - "Since CMake is generating for the Xcode \"new build system\", " - "ExternalProject_Add will use policy CMP0114's NEW behavior anyway, " - "but the generated build system may not match what the project intends." - ) - set(cmp0114 "NEW") - endif() - - set(genex_supported TRUE) - - _ep_get_add_keywords(keywords) - _ep_parse_arguments_to_vars("${keywords}" ${name} _EP_ "${ARGN}") - _ep_prepare_directories(${name}) - _ep_prepare_download(${name} ${genex_supported}) - _ep_prepare_update(${name} ${genex_supported}) - _ep_prepare_patch(${name} ${genex_supported}) - - _ep_get_configuration_subdir_suffix(cfgdir) - - # Add a custom target for the external project. - set(cmf_dir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles) - _ep_get_complete_stampfile(${name} complete_stamp_file) - - cmake_policy(PUSH) - if(cmp0114 STREQUAL "NEW") - # To implement CMP0114 NEW behavior with Makefile generators, - # we need CMP0113 NEW behavior. - cmake_policy(SET CMP0113 NEW) - endif() - # The "ALL" option to add_custom_target just tells it to not set the - # EXCLUDE_FROM_ALL target property. Later, if the EXCLUDE_FROM_ALL - # argument was passed, we explicitly set it for the target. - add_custom_target(${name} ALL DEPENDS ${complete_stamp_file}) - cmake_policy(POP) - set_property(TARGET ${name} PROPERTY _EP_IS_EXTERNAL_PROJECT 1) - set_property(TARGET ${name} PROPERTY LABELS ${name}) - set_property(TARGET ${name} PROPERTY FOLDER "ExternalProjectTargets/${name}") - - set_property(TARGET ${name} PROPERTY _EP_CMP0114 "${cmp0114}") - - # Transfer the arguments to the target as target properties. These are - # read by the various steps, potentially from different scopes. - foreach(key IN LISTS keywords ITEMS DOWNLOADED_FILE) - if(DEFINED _EP_${key}) - set_property(TARGET ${name} PROPERTY _EP_${key} "${_EP_${key}}") - endif() - endforeach() - + _ep_parse_arguments(ExternalProject_Add "${keywords}" ${name} _EP_ "${ARGN}") + _ep_set_directories(${name}) _ep_get_step_stampfile(${name} "done" done_stamp_file) _ep_get_step_stampfile(${name} "install" install_stamp_file) - if(arg_EXCLUDE_FROM_ALL) + # Set the EXCLUDE_FROM_ALL target property if required. + get_property(exclude_from_all TARGET ${name} PROPERTY _EP_EXCLUDE_FROM_ALL) + if(exclude_from_all) set_property(TARGET ${name} PROPERTY EXCLUDE_FROM_ALL TRUE) endif() @@ -4057,10 +3677,10 @@ function(ExternalProject_Add name) # The target depends on the output of the final step. # (Already set up above in the DEPENDS of the add_custom_target command.) # - _ep_add_preconfigure_command(${name} mkdir) - _ep_add_preconfigure_command(${name} download) - _ep_add_preconfigure_command(${name} update) - _ep_add_preconfigure_command(${name} patch) + _ep_add_mkdir_command(${name}) + _ep_add_download_command(${name}) + _ep_add_update_command(${name}) + _ep_add_patch_command(${name}) _ep_add_configure_command(${name}) _ep_add_build_command(${name}) _ep_add_install_command(${name}) |