summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Scott <craig.scott@crascit.com>2021-01-20 10:15:45 +1100
committerCraig Scott <craig.scott@crascit.com>2021-01-20 10:16:21 +1100
commit315a200f0c45415e8ab0da058aab9bbfa39c3c05 (patch)
tree6feb6f39b8dacadc4a6509c0404f48a2f7d1c4cc
parentddd9545895326e45bee798ae0b5a5d2bf8dae239 (diff)
downloadcmake-315a200f0c45415e8ab0da058aab9bbfa39c3c05.tar.gz
FindGit: Cache the GIT_EXECUTABLE version for the current run
The git version should not change while CMake is running. When using FetchContent with many dependencies, the repeated calls to get the git version every time ExternalProject is used can be measurable on some platforms. This commit queries that version only once and then caches it in a global property for the rest of that run. The git version can still safely change between runs because it is not cached, only the GIT_EXECUTABLE location is cached. Relates: #21703
-rw-r--r--Modules/ExternalProject.cmake22
-rw-r--r--Modules/FetchContent.cmake16
-rw-r--r--Modules/FetchContent/CMakeLists.cmake.in2
-rw-r--r--Modules/FindGit.cmake44
4 files changed, 69 insertions, 15 deletions
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 3307bc6fe1..1e50c6dcd0 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -2630,10 +2630,13 @@ function(_ep_add_download_command name)
--non-interactive ${svn_trust_cert_args} ${svn_user_pw_args} ${src_name})
list(APPEND depends ${stamp_dir}/${name}-svninfo.txt)
elseif(git_repository)
- unset(CMAKE_MODULE_PATH) # Use CMake builtin find module
- find_package(Git QUIET)
- if(NOT GIT_EXECUTABLE)
- message(FATAL_ERROR "error: could not find git for clone of ${name}")
+ # 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
+ find_package(Git QUIET)
+ if(NOT GIT_EXECUTABLE)
+ message(FATAL_ERROR "error: could not find git for clone of ${name}")
+ endif()
endif()
_ep_get_git_submodules_recurse(git_submodules_recurse)
@@ -2951,10 +2954,13 @@ function(_ep_add_update_command name)
--non-interactive ${svn_trust_cert_args} ${svn_user_pw_args})
set(always 1)
elseif(git_repository)
- unset(CMAKE_MODULE_PATH) # Use CMake builtin find module
- find_package(Git QUIET)
- if(NOT GIT_EXECUTABLE)
- message(FATAL_ERROR "error: could not find git for fetch of ${name}")
+ # 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
+ find_package(Git QUIET)
+ if(NOT GIT_EXECUTABLE)
+ message(FATAL_ERROR "error: could not find git for fetch of ${name}")
+ endif()
endif()
set(work_dir ${source_dir})
set(comment "Performing update step for '${name}'")
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
index ba2acfce79..8adef47778 100644
--- a/Modules/FetchContent.cmake
+++ b/Modules/FetchContent.cmake
@@ -964,6 +964,22 @@ ExternalProject_Add_Step(${contentName}-populate copyfile
"-DCMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY=${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}")
endif()
+ # Avoid using if(... IN_LIST ...) so we don't have to alter policy settings
+ set(__FETCHCONTENT_CACHED_INFO "")
+ list(FIND ARG_UNPARSED_ARGUMENTS GIT_REPOSITORY indexResult)
+ if(indexResult GREATER_EQUAL 0)
+ find_package(Git QUIET)
+ set(__FETCHCONTENT_CACHED_INFO
+"# Pass through things we've already detected in the main project to avoid
+# paying the cost of redetecting them again in ExternalProject_Add()
+set(GIT_EXECUTABLE [==[${GIT_EXECUTABLE}]==])
+set(GIT_VERSION_STRING [==[${GIT_VERSION_STRING}]==])
+set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
+ [==[${GIT_EXECUTABLE};${GIT_VERSION_STRING}]==]
+)
+")
+ endif()
+
# Create and build a separate CMake project to carry out the population.
# If we've already previously done these steps, they will not cause
# anything to be updated, so extra rebuilds of the project won't occur.
diff --git a/Modules/FetchContent/CMakeLists.cmake.in b/Modules/FetchContent/CMakeLists.cmake.in
index 45e4df03ef..5ebb12f1ad 100644
--- a/Modules/FetchContent/CMakeLists.cmake.in
+++ b/Modules/FetchContent/CMakeLists.cmake.in
@@ -9,6 +9,8 @@ cmake_minimum_required(VERSION ${CMAKE_VERSION})
project(${contentName}-populate NONE)
+@__FETCHCONTENT_CACHED_INFO@
+
include(ExternalProject)
ExternalProject_Add(${contentName}-populate
${ARG_EXTRA}
diff --git a/Modules/FindGit.cmake b/Modules/FindGit.cmake
index f8346b61d0..83da707b74 100644
--- a/Modules/FindGit.cmake
+++ b/Modules/FindGit.cmake
@@ -77,14 +77,44 @@ unset(git_names)
unset(_git_sourcetree_path)
if(GIT_EXECUTABLE)
- execute_process(COMMAND ${GIT_EXECUTABLE} --version
- OUTPUT_VARIABLE git_version
- ERROR_QUIET
- OUTPUT_STRIP_TRAILING_WHITESPACE)
- if (git_version MATCHES "^git version [0-9]")
- string(REPLACE "git version " "" GIT_VERSION_STRING "${git_version}")
+ # Avoid querying the version if we've already done that this run. For
+ # projects that use things like ExternalProject or FetchContent heavily,
+ # this saving can be measurable on some platforms.
+ set(__doGitVersionCheck YES)
+ if(DEFINED GIT_VERSION_STRING)
+ # This is an internal property, projects must not try to use it.
+ # We don't want this stored in the cache because it might still change
+ # between CMake runs, but it shouldn't change during a run.
+ get_property(__gitVersionProp GLOBAL
+ PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
+ )
+ if(__gitVersionProp)
+ list(GET __gitVersionProp 0 __gitExe)
+ list(GET __gitVersionProp 1 __gitVersion)
+ if("${__gitExe}" STREQUAL "${GIT_EXECUTABLE}" AND
+ "${__gitVersion}" STREQUAL "${GIT_VERSION_STRING}")
+ set(__doGitVersionCheck NO)
+ endif()
+ endif()
+ unset(__gitVersionProp)
+ unset(__gitExe)
+ unset(__gitVersion)
+ endif()
+
+ if(__doGitVersionCheck)
+ execute_process(COMMAND ${GIT_EXECUTABLE} --version
+ OUTPUT_VARIABLE git_version
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if (git_version MATCHES "^git version [0-9]")
+ string(REPLACE "git version " "" GIT_VERSION_STRING "${git_version}")
+ set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
+ "${GIT_EXECUTABLE};${GIT_VERSION_STRING}"
+ )
+ endif()
+ unset(git_version)
endif()
- unset(git_version)
+ unset(__doGitVersionCheck)
get_property(_findgit_role GLOBAL PROPERTY CMAKE_ROLE)
if(_findgit_role STREQUAL "PROJECT" AND NOT TARGET Git::Git)