diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 0302d5c4b..b1140c806 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -16,6 +16,8 @@ # [CVS_TAG tag] # Tag to checkout from CVS repo # [SVN_REPOSITORY url] # URL of Subversion repo # [SVN_REVISION rev] # Revision to checkout from Subversion repo +# [GIT_REPOSITORY url] # URL of git repo +# [GIT_TAG tag] # Git branch name, commit id or tag # [URL /.../src.tgz] # Full path or URL of source # [TIMEOUT seconds] # Time allowed for file download operations # #--Update/Patch step---------- @@ -203,6 +205,62 @@ define_property(DIRECTORY PROPERTY "EP_PREFIX" INHERITED ) +function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git_repository git_tag src_name 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 \${CMAKE_COMMAND} -E remove_directory \"${source_dir}\" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR \"Failed to remove directory: '${source_dir}'\") +endif() + +execute_process( + COMMAND \"${git_EXECUTABLE}\" clone \"${git_repository}\" \"${src_name}\" + WORKING_DIRECTORY \"${work_dir}\" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR \"Failed to clone repository: '${git_repository}'\") +endif() + +execute_process( + COMMAND \"${git_EXECUTABLE}\" checkout ${git_tag} + WORKING_DIRECTORY \"${work_dir}/${src_name}\" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR \"Failed to checkout tag: '${git_tag}'\") +endif() + +execute_process( + COMMAND \"${git_EXECUTABLE}\" submodule init + WORKING_DIRECTORY \"${work_dir}/${src_name}\" + RESULT_VARIABLE error_code + ) +if(error_code) + message(FATAL_ERROR \"Failed to init submodules in: '${work_dir}/${src_name}'\") +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() + +" +) + +endfunction(_ep_write_gitclone_script) + + function(_ep_write_downloadfile_script script_filename remote local timeout) if(timeout) set(timeout_args TIMEOUT ${timeout}) @@ -609,6 +667,19 @@ function(_ep_add_mkdir_command name) endfunction(_ep_add_mkdir_command) +function(_ep_get_git_version git_EXECUTABLE git_version_var) + if(git_EXECUTABLE) + execute_process( + COMMAND "${git_EXECUTABLE}" --version + OUTPUT_VARIABLE ov + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REGEX REPLACE "^git version (.+)$" "\\1" version "${ov}") + set(${git_version_var} "${version}" PARENT_SCOPE) + endif() +endfunction() + + function(_ep_add_download_command name) ExternalProject_Get_Property(${name} source_dir stamp_dir download_dir tmp_dir) @@ -616,6 +687,7 @@ function(_ep_add_download_command name) 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(url TARGET ${name} PROPERTY _EP_URL) # TODO: Perhaps file:// should be copied to download dir before extraction. @@ -676,6 +748,47 @@ function(_ep_add_download_command name) set(comment "Performing download step (SVN checkout) for '${name}'") set(cmd ${Subversion_SVN_EXECUTABLE} co ${svn_repository} ${svn_revision} ${src_name}) list(APPEND depends ${stamp_dir}/${name}-svninfo.txt) + elseif(git_repository) + find_program(git_EXECUTABLE NAMES git.cmd git eg.cmd eg DOC "git command line client") + mark_as_advanced(git_EXECUTABLE) + if(NOT git_EXECUTABLE) + message(FATAL_ERROR "error: could not find git for clone of ${name}") + endif() + + # The git submodule update '--recursive' flag requires git >= v1.6.5 + # + _ep_get_git_version("${git_EXECUTABLE}" git_version) + if(git_version VERSION_LESS 1.6.5) + message(FATAL_ERROR "error: git version 1.6.5 or later required for 'git submodule update --recursive': git_version='${git_version}'") + endif() + + get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) + if(NOT git_tag) + set(git_tag "master") + endif() + + set(repository ${git_repository}) + set(module) + set(tag ${git_tag}) + configure_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, + # 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} ${src_name} ${work_dir} + ) + set(comment "Performing download step (git clone) for '${name}'") + set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitclone.cmake) + list(APPEND depends ${stamp_dir}/${name}-gitinfo.txt) elseif(url) get_filename_component(work_dir "${source_dir}" PATH) set(repository "external project URL") @@ -734,6 +847,7 @@ function(_ep_add_update_command name) 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) set(work_dir) set(comment) @@ -759,6 +873,21 @@ function(_ep_add_update_command name) get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION) set(cmd ${Subversion_SVN_EXECUTABLE} up ${svn_revision}) set(always 1) + elseif(git_repository) + if(NOT git_EXECUTABLE) + 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}'") + 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 + ) + set(always 1) endif() ExternalProject_Add_Step(${name} update diff --git a/Tests/ExternalProject/CMakeLists.txt b/Tests/ExternalProject/CMakeLists.txt index f02f2f76d..09b761919 100644 --- a/Tests/ExternalProject/CMakeLists.txt +++ b/Tests/ExternalProject/CMakeLists.txt @@ -5,6 +5,7 @@ include(ExternalProject) find_package(CVS) find_package(Subversion) +find_program(git_EXECUTABLE NAMES git.cmd git eg.cmd eg DOC "git command line client") set(base "${CMAKE_BINARY_DIR}/CMakeExternals") set(binary_base "${base}/Build") @@ -37,8 +38,6 @@ if(NOT DEFINED can_build_tutorial_step5) endif() endif() -message(STATUS "can_build_tutorial_step5='${can_build_tutorial_step5}'") - # Empty projects that test all the known ExternalProject_Add argument key words: # @@ -301,6 +300,67 @@ if(do_svn_tests) endif() +set(do_git_tests 0) + +if(git_EXECUTABLE) + set(do_git_tests 1) +endif() + + +if(do_git_tests) + set(local_git_repo "${CMAKE_CURRENT_BINARY_DIR}/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 ${local_git_repo} + URL ${CMAKE_CURRENT_SOURCE_DIR}/gitrepo.tgz + BUILD_COMMAND "" + CONFIGURE_COMMAND ${git_EXECUTABLE} --version + INSTALL_COMMAND "" + ) + + # git by commit id: + # + set(proj TutorialStep1-GIT-byhash) + ExternalProject_Add(${proj} + GIT_REPOSITORY "${local_git_repo}" + GIT_TAG d1970730310fe8bc07e73f15dc570071f9f9654a + UPDATE_COMMAND "" + CMAKE_GENERATOR "${CMAKE_GENERATOR}" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + INSTALL_COMMAND "" + DEPENDS "SetupLocalGITRepository" + ) + + # git by explicit branch/tag name: + # + set(proj TutorialStep1-GIT-bytag) + ExternalProject_Add(${proj} + GIT_REPOSITORY "${local_git_repo}" + GIT_TAG "origin/master" + UPDATE_COMMAND "" + CMAKE_GENERATOR "${CMAKE_GENERATOR}" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + INSTALL_COMMAND "" + DEPENDS "SetupLocalGITRepository" + ) + + # Live git / master (no GIT_TAG): + # + set(proj TutorialStep1-GIT-master) + ExternalProject_Add(${proj} + GIT_REPOSITORY "${local_git_repo}" + CMAKE_GENERATOR "${CMAKE_GENERATOR}" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + INSTALL_COMMAND "" + DEPENDS "SetupLocalGITRepository" + ) +endif() + + # Test the testable built/installed products: # enable_testing() @@ -365,3 +425,10 @@ if(can_build_tutorial_step5) set_property(TEST TutorialStep5-InstallTreeTest APPEND PROPERTY LABELS Step5 InstallTree) endif() + + +message(STATUS "can_build_tutorial_step5='${can_build_tutorial_step5}'") +message(STATUS "do_cvs_tests='${do_cvs_tests}'") +message(STATUS "do_svn_tests='${do_svn_tests}'") +message(STATUS "do_git_tests='${do_git_tests}'") +message(STATUS "git_EXECUTABLE='${git_EXECUTABLE}'") diff --git a/Tests/ExternalProject/gitrepo.tgz b/Tests/ExternalProject/gitrepo.tgz new file mode 100644 index 000000000..0a84bda7c Binary files /dev/null and b/Tests/ExternalProject/gitrepo.tgz differ