Browse Source

Merge topic 'only-git-fetch-when-necessary'

a41d3a4 ExternalProjectUpdateTest: Only support Git 1.6.5 and greater.
de760c1 ExternalProject: Verify when a fetch occurs during update test.
0a34433 ExternalProject: Make sure the ExternalProjectUpdate setup is available.
9b66c8f ExternalProject: Always do a git fetch for a remote ref.
2619f4d ExternalProject: Add tests for UPDATE_COMMAND.
378aa12 ExternalProject: Do smoke tests for Git Tutorial builds.
d075829 ExternalProject: Only run 'git fetch' when required.
Brad King 13 years ago
parent
commit
bd52ff3ac9

+ 78 - 5
Modules/ExternalProject.cmake

@@ -402,6 +402,79 @@ endif()
 endfunction()
 
 
+function(_ep_write_gitupdate_script script_filename git_EXECUTABLE git_tag git_repository work_dir)
+  file(WRITE ${script_filename}
+"if(\"${git_tag}\" STREQUAL \"\")
+  message(FATAL_ERROR \"Tag for git checkout should not be empty.\")
+endif()
+
+execute_process(
+  COMMAND \"${git_EXECUTABLE}\" rev-list --max-count=1 HEAD
+  WORKING_DIRECTORY \"${work_dir}\"
+  RESULT_VARIABLE error_code
+  OUTPUT_VARIABLE head_sha
+  )
+if(error_code)
+  message(FATAL_ERROR \"Failed to get the hash for HEAD\")
+endif()
+
+execute_process(
+  COMMAND \"${git_EXECUTABLE}\" show-ref ${git_tag}
+  WORKING_DIRECTORY \"${work_dir}\"
+  OUTPUT_VARIABLE show_ref_output
+  )
+# If a remote ref is asked for, which can possibly move around,
+# we must always do a fetch and checkout.
+if(\"\${show_ref_output}\" MATCHES \"remotes\")
+  set(is_remote_ref 1)
+else()
+  set(is_remote_ref 0)
+endif()
+
+# This will fail if the tag does not exist (it probably has not been fetched
+# yet).
+execute_process(
+  COMMAND \"${git_EXECUTABLE}\" rev-list --max-count=1 ${git_tag}
+  WORKING_DIRECTORY \"${work_dir}\"
+  RESULT_VARIABLE error_code
+  OUTPUT_VARIABLE tag_sha
+  )
+
+# Is the hash checkout out that we want?
+if(error_code OR is_remote_ref OR NOT (\"\${tag_sha}\" STREQUAL \"\${head_sha}\"))
+  execute_process(
+    COMMAND \"${git_EXECUTABLE}\" fetch
+    WORKING_DIRECTORY \"${work_dir}\"
+    RESULT_VARIABLE error_code
+    )
+  if(error_code)
+    message(FATAL_ERROR \"Failed to fetch repository '${git_repository}'\")
+  endif()
+
+  execute_process(
+    COMMAND \"${git_EXECUTABLE}\" checkout ${git_tag}
+    WORKING_DIRECTORY \"${work_dir}\"
+    RESULT_VARIABLE error_code
+    )
+  if(error_code)
+    message(FATAL_ERROR \"Failed to checkout tag: '${git_tag}'\")
+  endif()
+
+  execute_process(
+    COMMAND \"${git_EXECUTABLE}\" submodule update --recursive
+    WORKING_DIRECTORY \"${work_dir}/${src_name}\"
+    RESULT_VARIABLE error_code
+    )
+  if(error_code)
+    message(FATAL_ERROR \"Failed to update submodules in: '${work_dir}/${src_name}'\")
+  endif()
+endif()
+
+"
+)
+
+endfunction(_ep_write_gitupdate_script)
+
 function(_ep_write_downloadfile_script script_filename remote local timeout hash tls_verify tls_cainfo)
   if(timeout)
     set(timeout_args TIMEOUT ${timeout})
@@ -1354,7 +1427,7 @@ endfunction()
 
 
 function(_ep_add_update_command name)
-  ExternalProject_Get_Property(${name} source_dir)
+  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)
@@ -1406,15 +1479,15 @@ function(_ep_add_update_command name)
       message(FATAL_ERROR "error: could not find git for fetch of ${name}")
     endif()
     set(work_dir ${source_dir})
-    set(comment "Performing update step (git fetch) for '${name}'")
+    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(cmd ${GIT_EXECUTABLE} fetch
-      COMMAND ${GIT_EXECUTABLE} checkout ${git_tag}
-      COMMAND ${GIT_EXECUTABLE} submodule update --recursive
+    _ep_write_gitupdate_script(${tmp_dir}/${name}-gitupdate.cmake
+      ${GIT_EXECUTABLE} ${git_tag} ${git_repository} ${work_dir}
       )
+    set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitupdate.cmake)
     set(always 1)
   elseif(hg_repository)
     if(NOT HG_EXECUTABLE)

+ 30 - 0
Tests/CMakeLists.txt

@@ -983,6 +983,36 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
   set_tests_properties(ExternalProject PROPERTIES
     TIMEOUT ${CMAKE_LONG_TEST_TIMEOUT})
 
+  add_test(ExternalProjectUpdateSetup ${CMAKE_CTEST_COMMAND}
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/ExternalProjectUpdate"
+    "${CMake_BINARY_DIR}/Tests/ExternalProjectUpdate"
+    --build-generator ${CMAKE_TEST_GENERATOR}
+    --build-project ExternalProjectUpdateTest
+    --build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
+    --build-exe-dir "${CMake_BINARY_DIR}/Tests/ExternalProjectUpdate"
+    --force-new-ctest-process
+    --test-command ${CMAKE_CTEST_COMMAND} -V
+    )
+  list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ExternalProjectUpdate")
+  set_tests_properties(ExternalProjectUpdateSetup PROPERTIES
+    TIMEOUT ${CMAKE_LONG_TEST_TIMEOUT})
+
+  add_test(NAME ExternalProjectUpdate
+    COMMAND ${CMAKE_CMAKE_COMMAND}
+    -DExternalProjectUpdate_SOURCE_DIR:PATH=${CMake_SOURCE_DIR}/Tests/ExternalProjectUpdate
+    -DExternalProjectUpdate_BINARY_DIR:PATH=${CMake_BINARY_DIR}/Tests/ExternalProjectUpdate
+    -DCMAKE_TEST_GENERATOR=${CMAKE_TEST_GENERATOR}
+    -DCMAKE_TEST_MAKEPROGRAM=${CMAKE_TEST_MAKEPROGRAM}
+    -DCMAKE_CTEST_COMMAND=${CMAKE_CTEST_COMMAND}
+    -P ${CMake_SOURCE_DIR}/Tests/ExternalProjectUpdate/ExternalProjectUpdateTest.cmake
+    )
+  list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ExternalProjectUpdate")
+  set_tests_properties(ExternalProjectUpdate PROPERTIES
+    TIMEOUT ${CMAKE_LONG_TEST_TIMEOUT}
+    WORKING_DIRECTORY ${CMake_SOURCE_DIR}/Tests/ExternalProjectUpdate
+    DEPENDS ExternalProjectUpdateSetup )
+
   # do each of the tutorial steps
   foreach(STP RANGE 1 7)
     add_test(TutorialStep${STP} ${CMAKE_CTEST_COMMAND}

+ 11 - 0
Tests/ExternalProject/CMakeLists.txt

@@ -638,6 +638,17 @@ if(do_svn_tests)
     "${binary_base}/TutorialStep1-SVN-trunk/Tutorial" 98)
 endif()
 
+if(do_git_tests)
+  add_test(TutorialStep1-GIT-byhash
+    "${binary_base}/TutorialStep1-GIT-byhash/Tutorial" 100)
+
+  add_test(TutorialStep1-GIT-bytag
+    "${binary_base}/TutorialStep1-GIT-bytag/Tutorial" 99)
+
+  add_test(TutorialStep1-GIT-master
+    "${binary_base}/TutorialStep1-GIT-master/Tutorial" 98)
+endif()
+
 
 # InstallTree tests:
 #

+ 94 - 0
Tests/ExternalProjectUpdate/CMakeLists.txt

@@ -0,0 +1,94 @@
+cmake_minimum_required(VERSION 2.8)
+project(ExternalProjectUpdateTest NONE)
+
+include(ExternalProject)
+
+find_package(Git)
+
+option(ExternalProjectUpdateTest_USE_FOLDERS "Enable folder grouping in IDEs." ON)
+if(ExternalProjectUpdateTest_USE_FOLDERS)
+  set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+else()
+  set_property(GLOBAL PROPERTY USE_FOLDERS OFF)
+endif()
+
+set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER
+  "CMakePredefinedTargets-in-ExternalProjectUpdateTest")
+
+set(base "${CMAKE_BINARY_DIR}/CMakeExternals")
+set(binary_base "${base}/Build")
+set_property(DIRECTORY PROPERTY EP_BASE ${base})
+set_property(DIRECTORY PROPERTY EP_STEP_TARGETS configure build test)
+
+set(do_git_tests 0)
+
+if(GIT_EXECUTABLE)
+  set(do_git_tests 1)
+
+  execute_process(
+    COMMAND "${GIT_EXECUTABLE}" --version
+    OUTPUT_VARIABLE ov
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+  string(REGEX REPLACE "^git version (.+)$" "\\1" git_version "${ov}")
+  message(STATUS "git_version='${git_version}'")
+
+  if(git_version VERSION_LESS 1.6.5)
+    message(STATUS "No ExternalProject git tests with git client less than version 1.6.5")
+    set(do_git_tests 0)
+  endif()
+endif()
+
+# This should be specified from the command line.
+if(NOT TEST_GIT_TAG)
+  set(TEST_GIT_TAG origin/master)
+endif()
+
+if(do_git_tests)
+  set(local_git_repo "../../LocalRepositories/GIT")
+
+  # Unzip/untar the git repository in our source folder so that other
+  # projects below may use it to test git args of ExternalProject_Add
+  #
+  set(proj SetupLocalGITRepository)
+  ExternalProject_Add(${proj}
+    SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/LocalRepositories/GIT
+    URL ${CMAKE_CURRENT_SOURCE_DIR}/gitrepo.tgz
+    BUILD_COMMAND ""
+    CONFIGURE_COMMAND "${GIT_EXECUTABLE}" --version
+    INSTALL_COMMAND ""
+  )
+  set_property(TARGET ${proj}
+    PROPERTY FOLDER "SetupRepos/Local/Deeply/Nested/For/Testing")
+
+  set(proj TutorialStep1-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_DIR>
+    INSTALL_COMMAND ""
+    DEPENDS "SetupLocalGITRepository"
+  )
+  set_property(TARGET ${proj} PROPERTY FOLDER "GIT")
+endif()
+
+
+# Test the testable built/installed products:
+#
+enable_testing()
+
+
+# Do at least a smoke test of a built executable from each
+# project's build directory...
+#
+# BuildTree tests:
+#
+
+if(do_git_tests)
+  add_test(TutorialStep1-GIT
+    "${binary_base}/TutorialStep1-GIT/Tutorial" 81)
+endif()
+
+message(STATUS "do_git_tests='${do_git_tests}'")
+message(STATUS "GIT_EXECUTABLE='${GIT_EXECUTABLE}'")

+ 94 - 0
Tests/ExternalProjectUpdate/ExternalProjectUpdateTest.cmake

@@ -0,0 +1,94 @@
+# Set the ExternalProject GIT_TAG to desired_tag, and make sure the
+# resulting checked out version is resulting_sha and rebuild.
+# This check's the correct behavior of the ExternalProject UPDATE_COMMAND.
+# Also verify that a fetch only occurs when fetch_expected is 1.
+macro(check_a_tag desired_tag resulting_sha fetch_expected)
+  message( STATUS "Checking ExternalProjectUpdate to tag: ${desired_tag}" )
+
+  # 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/TutorialStep1-GIT/.git/FETCH_HEAD )
+  file( REMOVE ${FETCH_HEAD_file} )
+
+  # Configure
+  execute_process(COMMAND ${CMAKE_COMMAND}
+    -G ${CMAKE_TEST_GENERATOR}
+    -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()
+
+  # Check the resulting SHA
+  execute_process(COMMAND ${GIT_EXECUTABLE}
+    rev-list --max-count=1 HEAD
+    WORKING_DIRECTORY ${ExternalProjectUpdate_BINARY_DIR}/CMakeExternals/Source/TutorialStep1-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)
+set(do_git_tests 0)
+if(GIT_EXECUTABLE)
+  set(do_git_tests 1)
+
+  execute_process(
+    COMMAND "${GIT_EXECUTABLE}" --version
+    OUTPUT_VARIABLE ov
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+  string(REGEX REPLACE "^git version (.+)$" "\\1" git_version "${ov}")
+  message(STATUS "git_version='${git_version}'")
+
+  if(git_version VERSION_LESS 1.6.5)
+    message(STATUS "No ExternalProject git tests with git client less than version 1.6.5")
+    set(do_git_tests 0)
+  endif()
+endif()
+
+if(do_git_tests)
+  check_a_tag(origin/master 5842b503ba4113976d9bb28d57b5aee1ad2736b7 1)
+  check_a_tag(tag1          d1970730310fe8bc07e73f15dc570071f9f9654a 1)
+  # With the Git UPDATE_COMMAND performance patch, this will not required a
+  # 'git fetch'
+  check_a_tag(tag1          d1970730310fe8bc07e73f15dc570071f9f9654a 0)
+  check_a_tag(tag2          5842b503ba4113976d9bb28d57b5aee1ad2736b7 1)
+  check_a_tag(d19707303     d1970730310fe8bc07e73f15dc570071f9f9654a 1)
+  check_a_tag(d19707303     d1970730310fe8bc07e73f15dc570071f9f9654a 0)
+  check_a_tag(origin/master 5842b503ba4113976d9bb28d57b5aee1ad2736b7 1)
+  # This is a remote symbolic ref, so it will always trigger a 'git fetch'
+  check_a_tag(origin/master 5842b503ba4113976d9bb28d57b5aee1ad2736b7 1)
+endif()

BIN
Tests/ExternalProjectUpdate/gitrepo.tgz