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
This commit is contained in:
Craig Scott 2021-01-20 10:15:45 +11:00
parent ddd9545895
commit 315a200f0c
4 changed files with 69 additions and 15 deletions

View File

@ -2630,11 +2630,14 @@ 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)
# 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,11 +2954,14 @@ function(_ep_add_update_command name)
--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
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}'")
get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG)

View File

@ -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.

View File

@ -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}

View File

@ -77,14 +77,44 @@ unset(git_names)
unset(_git_sourcetree_path)
if(GIT_EXECUTABLE)
# 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(__doGitVersionCheck)
get_property(_findgit_role GLOBAL PROPERTY CMAKE_ROLE)
if(_findgit_role STREQUAL "PROJECT" AND NOT TARGET Git::Git)