diff --git a/Help/release/dev/ExternalProject_UPDATE_DISCONNECTED.rst b/Help/release/dev/ExternalProject_UPDATE_DISCONNECTED.rst new file mode 100644 index 000000000..bed4a8944 --- /dev/null +++ b/Help/release/dev/ExternalProject_UPDATE_DISCONNECTED.rst @@ -0,0 +1,6 @@ +ExternalProject_UPDATE_DISCONNECTED +----------------------------------- + +* The :module:`ExternalProject` module ``ExternalProject_Add`` command + learned an ``UPDATE_DISCONNECTED`` option to avoid automatically + updating the source tree checkout from version control. diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index f6e88965b..32f6d2ce8 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -80,6 +80,8 @@ Create custom targets to build projects in external trees ``UPDATE_COMMAND ...`` Source work-tree update command + ``UPDATE_DISCONNECTED 1`` + Never update automatically from the remote repository ``PATCH_COMMAND ...`` Command to patch downloaded source @@ -203,6 +205,16 @@ Create custom targets to build projects in external trees options. The ``URL`` option may refer locally to a directory or source tarball, or refer to a remote tarball (e.g. ``http://.../src.tgz``). + If ``UPDATE_DISCONNECTED`` is set, the update step is not executed + automatically when building the main target. The update step can still + be added as a step target and called manually. This is useful if you + want to allow to build the project when you are disconnected from the + network (you might still need the network for the download step). + This is disabled by default. + The directory property ``EP_UPDATE_DISCONNECTED`` can be used to change + the default value for all the external projects in the current + directory and its subdirectories. + .. command:: ExternalProject_Add_Step The ``ExternalProject_Add_Step`` function adds a custom step to an @@ -439,6 +451,13 @@ define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED "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." + ) + function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git_repository git_tag git_submodules src_name work_dir gitclone_infofile gitclone_stampfile) file(WRITE ${script_filename} "if(\"${git_tag}\" STREQUAL \"\") @@ -1436,6 +1455,15 @@ function(ExternalProject_Add_Step name step) _ep_write_log_script(${name} ${step} command) endif() + if("${command}" STREQUAL "") + # Some generators (i.e. Xcode) will not generate a file level target + # if no command is set, and therefore the dependencies on this + # target will be broken. + # The empty command is replaced by an echo command here in order to + # avoid this issue. + set(command ${CMAKE_COMMAND} -E echo_append) + endif() + add_custom_command( OUTPUT ${stamp_file} COMMENT ${comment} @@ -1829,6 +1857,12 @@ function(_ep_add_update_command name) 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(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(work_dir) set(comment) @@ -1918,10 +1952,26 @@ Update to Mercurial >= 2.1.1. COMMENT ${comment} COMMAND ${cmd} ALWAYS ${always} + EXCLUDE_FROM_MAIN ${update_disconnected} WORKING_DIRECTORY ${work_dir} DEPENDEES download ${log} ) + + if(always AND update_disconnected) + _ep_get_step_stampfile(${name} skip-update skip-update_stamp_file) + string(REPLACE "Performing" "Skipping" comment "${comment}") + ExternalProject_Add_Step(${name} skip-update + COMMENT ${comment} + ALWAYS 1 + EXCLUDE_FROM_MAIN 1 + WORKING_DIRECTORY ${work_dir} + DEPENDEES download + ${log} + ) + set_property(SOURCE ${skip-update_stamp_file} PROPERTY SYMBOLIC 1) + endif() + endfunction() @@ -2043,10 +2093,22 @@ function(_ep_add_configure_command name) set(log "") endif() + 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() + if(update_disconnected) + set(update_dep skip-update) + else() + set(update_dep update) + endif() + ExternalProject_Add_Step(${name} configure COMMAND ${cmd} WORKING_DIRECTORY ${binary_dir} - DEPENDEES update patch + DEPENDEES ${update_dep} patch DEPENDS ${file_deps} ${log} ) diff --git a/Tests/ExternalProjectUpdate/CMakeLists.txt b/Tests/ExternalProjectUpdate/CMakeLists.txt index 582b0a87d..fbb338869 100644 --- a/Tests/ExternalProjectUpdate/CMakeLists.txt +++ b/Tests/ExternalProjectUpdate/CMakeLists.txt @@ -72,6 +72,18 @@ if(do_git_tests) ) ExternalProject_Add_StepDependencies(${proj} download SetupLocalGITRepository) set_property(TARGET ${proj} PROPERTY FOLDER "GIT") + + set(proj TutorialStep2-GIT) + ExternalProject_Add(${proj} + GIT_REPOSITORY "${local_git_repo}" + GIT_TAG ${TEST_GIT_TAG} + CMAKE_GENERATOR "${CMAKE_GENERATOR}" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + INSTALL_COMMAND "" + UPDATE_DISCONNECTED 1 + ) + ExternalProject_Add_StepDependencies(${proj} download SetupLocalGITRepository) + set_property(TARGET ${proj} PROPERTY FOLDER "GIT") endif() diff --git a/Tests/ExternalProjectUpdate/ExternalProjectUpdateTest.cmake b/Tests/ExternalProjectUpdate/ExternalProjectUpdateTest.cmake index 6c7bcfe40..7065f36c1 100644 --- a/Tests/ExternalProjectUpdate/ExternalProjectUpdateTest.cmake +++ b/Tests/ExternalProjectUpdate/ExternalProjectUpdateTest.cmake @@ -59,6 +59,102 @@ was expected." if( EXISTS ${FETCH_HEAD_file} AND NOT ${fetch_expected}) message( FATAL_ERROR "Fetch DID occur when it was not expected.") endif() + + message( STATUS "Checking ExternalProjectUpdate to tag: ${desired_tag} (disconnected)" ) + + # Remove the FETCH_HEAD file, so we can check if it gets replaced with a 'git + # fetch'. + set( FETCH_HEAD_file ${ExternalProjectUpdate_BINARY_DIR}/CMakeExternals/Source/TutorialStep2-GIT/.git/FETCH_HEAD ) + file( REMOVE ${FETCH_HEAD_file} ) + + # Check initial SHA + execute_process(COMMAND ${GIT_EXECUTABLE} + rev-list --max-count=1 HEAD + WORKING_DIRECTORY ${ExternalProjectUpdate_BINARY_DIR}/CMakeExternals/Source/TutorialStep2-GIT + RESULT_VARIABLE error_code + OUTPUT_VARIABLE initial_sha + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # Configure + execute_process(COMMAND ${CMAKE_COMMAND} + -G ${CMAKE_GENERATOR} -T "${CMAKE_GENERATOR_TOOLSET}" + -A "${CMAKE_GENERATOR_PLATFORM}" + -DTEST_GIT_TAG:STRING=${desired_tag} + ${ExternalProjectUpdate_SOURCE_DIR} + WORKING_DIRECTORY ${ExternalProjectUpdate_BINARY_DIR} + RESULT_VARIABLE error_code + ) + if(error_code) + message(FATAL_ERROR "Could not configure the project.") + endif() + + # Build + execute_process(COMMAND ${CMAKE_COMMAND} + --build ${ExternalProjectUpdate_BINARY_DIR} + RESULT_VARIABLE error_code + ) + if(error_code) + message(FATAL_ERROR "Could not build the project.") + endif() + + if( EXISTS ${FETCH_HEAD_file} ) + message( FATAL_ERROR "Fetch occured when it was not expected.") + endif() + + # Check the resulting SHA + execute_process(COMMAND ${GIT_EXECUTABLE} + rev-list --max-count=1 HEAD + WORKING_DIRECTORY ${ExternalProjectUpdate_BINARY_DIR}/CMakeExternals/Source/TutorialStep2-GIT + RESULT_VARIABLE error_code + OUTPUT_VARIABLE tag_sha + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(error_code) + message(FATAL_ERROR "Could not check the sha.") + endif() + + if(NOT (${tag_sha} STREQUAL ${initial_sha})) + message(FATAL_ERROR "Update occurred when it was not expected.") + endif() + + # Update + execute_process(COMMAND ${CMAKE_COMMAND} + --build ${ExternalProjectUpdate_BINARY_DIR} + --target TutorialStep2-GIT-update + RESULT_VARIABLE error_code + ) + if(error_code) + message(FATAL_ERROR "Could not build the project.") + endif() + + # Check the resulting SHA + execute_process(COMMAND ${GIT_EXECUTABLE} + rev-list --max-count=1 HEAD + WORKING_DIRECTORY ${ExternalProjectUpdate_BINARY_DIR}/CMakeExternals/Source/TutorialStep2-GIT + RESULT_VARIABLE error_code + OUTPUT_VARIABLE tag_sha + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(error_code) + message(FATAL_ERROR "Could not check the sha.") + endif() + + if(NOT (${tag_sha} STREQUAL ${resulting_sha})) + message(FATAL_ERROR "UPDATE_COMMAND produced + ${tag_sha} +when + ${resulting_sha} +was expected." + ) + endif() + + if( NOT EXISTS ${FETCH_HEAD_file} AND ${fetch_expected}) + message( FATAL_ERROR "Fetch did NOT occur when it was expected.") + endif() + if( EXISTS ${FETCH_HEAD_file} AND NOT ${fetch_expected}) + message( FATAL_ERROR "Fetch DID occur when it was not expected.") + endif() endmacro() find_package(Git)