From c5c1d579acd398fc6919cfadb9652969212256f3 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 13 Jul 2026 10:32:29 +0900 Subject: [PATCH 1/4] cmake: optimize fetch mechanism by checking the presence of the dir .git beforehand --- .../Config/cmake/SofaMacrosConfigure.cmake | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake index cdb43c48712..bd19ff8434c 100644 --- a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake +++ b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake @@ -194,7 +194,41 @@ macro(sofa_fetch_dependency name) set(${fixed_name}_SOURCE_DIR "${fetched_dir}" CACHE STRING "" FORCE ) - if( "${${upper_name}_LOCAL_DIRECTORY}" STREQUAL "" AND NOT FETCHCONTENT_FULLY_DISCONNECTED AND NOT FETCHCONTENT_UPDATES_DISCONNECTED AND NOT "${ARG_FETCH_ENABLED}" STREQUAL "OFF") + # Determine whether we actually need to (re)fetch. Driving ExternalProject + # through a nested CMake configure+build (below) is expensive and, on every + # reconfigure, only re-runs a git "update" step that almost always confirms the + # sources are already at the requested reference. If the repository is already + # checked out at the requested GIT_TAG, skip the fetch entirely. A re-fetch is + # still triggered automatically when the requested reference no longer matches + # what is currently checked out (e.g. a dependency version was bumped). + set(_sofa_need_fetch TRUE) + if(EXISTS "${fetched_dir}/.git") + if(NOT DEFINED GIT_EXECUTABLE) + find_package(Git QUIET) + endif() + if(GIT_EXECUTABLE) + # Resolve the current HEAD and the requested reference to commit hashes in a + # single git invocation (process spawns are expensive, especially on Windows). + # A non-zero result means the requested reference could not be resolved in the + # existing checkout (e.g. the GIT_TAG was bumped to a tag/SHA not present yet), + # in which case we fall through and (re)fetch. + execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD "${${upper_name}_GIT_TAG}^{commit}" + WORKING_DIRECTORY "${fetched_dir}" + OUTPUT_VARIABLE _sofa_refs OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _sofa_rev_result ERROR_QUIET) + if(_sofa_rev_result EQUAL 0) + string(REGEX REPLACE "[\r\n]+" ";" _sofa_refs "${_sofa_refs}") + list(GET _sofa_refs 0 _sofa_current_ref) + list(GET _sofa_refs 1 _sofa_requested_ref) + if("${_sofa_current_ref}" STREQUAL "${_sofa_requested_ref}") + set(_sofa_need_fetch FALSE) + message(STATUS "Dependency ${name} already at requested reference ${${upper_name}_GIT_TAG}; skipping fetch.") + endif() + endif() + endif() + endif() + + if( "${${upper_name}_LOCAL_DIRECTORY}" STREQUAL "" AND _sofa_need_fetch AND NOT FETCHCONTENT_FULLY_DISCONNECTED AND NOT FETCHCONTENT_UPDATES_DISCONNECTED AND NOT "${ARG_FETCH_ENABLED}" STREQUAL "OFF") # Fetch message("Fetching dependency ${name} in ${fetched_dir}") message(STATUS "Checkout reference ${${upper_name}_GIT_TAG} from repository ${${upper_name}_GIT_REPOSITORY} ") From 7376c1645215d07b6540cf0021629e210557cbb2 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Thu, 16 Jul 2026 17:21:53 +0200 Subject: [PATCH 2/4] When tag is a branch check the tip hash --- .../Config/cmake/SofaMacrosConfigure.cmake | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake index bd19ff8434c..91e59626227 100644 --- a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake +++ b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake @@ -221,8 +221,56 @@ macro(sofa_fetch_dependency name) list(GET _sofa_refs 0 _sofa_current_ref) list(GET _sofa_refs 1 _sofa_requested_ref) if("${_sofa_current_ref}" STREQUAL "${_sofa_requested_ref}") - set(_sofa_need_fetch FALSE) - message(STATUS "Dependency ${name} already at requested reference ${${upper_name}_GIT_TAG}; skipping fetch.") + # Local HEAD matches the requested ref. However, if the ref is a + # branch name (as opposed to a tag or a commit SHA), the upstream + # branch may have advanced since the last fetch. Detect this with a + # cheap ls-remote query so we don't silently skip upstream updates. + set(_sofa_ref_is_branch FALSE) + + # Full 40-hex-char SHA → immutable, no remote check needed. + string(REGEX MATCH "^[0-9a-fA-F]{40}$" _sofa_is_sha "${${upper_name}_GIT_TAG}") + if(_sofa_is_sha) + # Commit hash – never changes upstream. + else() + # Check whether the ref is a tag in the local repo. + execute_process( + COMMAND "${GIT_EXECUTABLE}" show-ref --verify --quiet "refs/tags/${${upper_name}_GIT_TAG}" + WORKING_DIRECTORY "${fetched_dir}" + RESULT_VARIABLE _sofa_tag_result) + if(NOT _sofa_tag_result EQUAL 0) + # Not a SHA and not a tag → treat as a branch name. + set(_sofa_ref_is_branch TRUE) + endif() + endif() + + if(_sofa_ref_is_branch) + # Ask the remote for the current tip of the branch without + # downloading objects. This is a lightweight network call. + execute_process( + COMMAND "${GIT_EXECUTABLE}" ls-remote origin "${${upper_name}_GIT_TAG}" + WORKING_DIRECTORY "${fetched_dir}" + OUTPUT_VARIABLE _sofa_ls_remote_out OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _sofa_ls_remote_result ERROR_QUIET) + if(_sofa_ls_remote_result EQUAL 0 AND _sofa_ls_remote_out) + # ls-remote output: "\t" + string(REGEX MATCH "^([0-9a-fA-F]+)" _sofa_remote_sha "${_sofa_ls_remote_out}") + if(_sofa_remote_sha AND NOT "${_sofa_remote_sha}" STREQUAL "${_sofa_current_ref}") + set(_sofa_need_fetch TRUE) + message(STATUS "Dependency ${name}: upstream branch ${${upper_name}_GIT_TAG} has new commits (local: ${_sofa_current_ref}, remote: ${_sofa_remote_sha}); re-fetching.") + else() + set(_sofa_need_fetch FALSE) + message(STATUS "Dependency ${name} already at requested branch ${${upper_name}_GIT_TAG} tip; skipping fetch.") + endif() + else() + # ls-remote failed (network issue, ref doesn't exist on remote, etc.). + # Fall through to a full fetch to be safe. + message(STATUS "Dependency ${name}: could not query remote for branch ${${upper_name}_GIT_TAG}; re-fetching to be safe.") + endif() + else() + # Tag or SHA – local match is authoritative. + set(_sofa_need_fetch FALSE) + message(STATUS "Dependency ${name} already at requested reference ${${upper_name}_GIT_TAG}; skipping fetch.") + endif() endif() endif() endif() From ef2e5ac9e29433df76ee64445c07c9a779523242 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Thu, 16 Jul 2026 17:29:07 +0200 Subject: [PATCH 3/4] Simplify the code removing the special case of tag --- .../Config/cmake/SofaMacrosConfigure.cmake | 66 +++++-------------- 1 file changed, 17 insertions(+), 49 deletions(-) diff --git a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake index 91e59626227..0f69e574eab 100644 --- a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake +++ b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake @@ -207,68 +207,36 @@ macro(sofa_fetch_dependency name) find_package(Git QUIET) endif() if(GIT_EXECUTABLE) - # Resolve the current HEAD and the requested reference to commit hashes in a - # single git invocation (process spawns are expensive, especially on Windows). - # A non-zero result means the requested reference could not be resolved in the - # existing checkout (e.g. the GIT_TAG was bumped to a tag/SHA not present yet), - # in which case we fall through and (re)fetch. execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD "${${upper_name}_GIT_TAG}^{commit}" WORKING_DIRECTORY "${fetched_dir}" OUTPUT_VARIABLE _sofa_refs OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE _sofa_rev_result ERROR_QUIET) if(_sofa_rev_result EQUAL 0) string(REGEX REPLACE "[\r\n]+" ";" _sofa_refs "${_sofa_refs}") - list(GET _sofa_refs 0 _sofa_current_ref) - list(GET _sofa_refs 1 _sofa_requested_ref) - if("${_sofa_current_ref}" STREQUAL "${_sofa_requested_ref}") - # Local HEAD matches the requested ref. However, if the ref is a - # branch name (as opposed to a tag or a commit SHA), the upstream - # branch may have advanced since the last fetch. Detect this with a - # cheap ls-remote query so we don't silently skip upstream updates. - set(_sofa_ref_is_branch FALSE) - - # Full 40-hex-char SHA → immutable, no remote check needed. + list(GET _sofa_refs 0 _sofa_head) + list(GET _sofa_refs 1 _sofa_want) + if("${_sofa_head}" STREQUAL "${_sofa_want}") + set(_sofa_need_fetch FALSE) + + # If the ref isn't a bare SHA, it may be a branch whose upstream + # tip has advanced. A lightweight ls-remote check catches that. + # (For tags this is a harmless no-op: the SHA will still match.) string(REGEX MATCH "^[0-9a-fA-F]{40}$" _sofa_is_sha "${${upper_name}_GIT_TAG}") - if(_sofa_is_sha) - # Commit hash – never changes upstream. - else() - # Check whether the ref is a tag in the local repo. + if(NOT _sofa_is_sha) execute_process( - COMMAND "${GIT_EXECUTABLE}" show-ref --verify --quiet "refs/tags/${${upper_name}_GIT_TAG}" + COMMAND "${GIT_EXECUTABLE}" ls-remote origin "${${upper_name}_GIT_TAG}" WORKING_DIRECTORY "${fetched_dir}" - RESULT_VARIABLE _sofa_tag_result) - if(NOT _sofa_tag_result EQUAL 0) - # Not a SHA and not a tag → treat as a branch name. - set(_sofa_ref_is_branch TRUE) + OUTPUT_VARIABLE _sofa_remote_out OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _sofa_remote_rc ERROR_QUIET) + string(REGEX MATCH "^[0-9a-fA-F]+" _sofa_remote_sha "${_sofa_remote_out}") + if(NOT _sofa_remote_rc EQUAL 0 OR NOT "${_sofa_remote_sha}" STREQUAL "${_sofa_head}") + set(_sofa_need_fetch TRUE) endif() endif() - if(_sofa_ref_is_branch) - # Ask the remote for the current tip of the branch without - # downloading objects. This is a lightweight network call. - execute_process( - COMMAND "${GIT_EXECUTABLE}" ls-remote origin "${${upper_name}_GIT_TAG}" - WORKING_DIRECTORY "${fetched_dir}" - OUTPUT_VARIABLE _sofa_ls_remote_out OUTPUT_STRIP_TRAILING_WHITESPACE - RESULT_VARIABLE _sofa_ls_remote_result ERROR_QUIET) - if(_sofa_ls_remote_result EQUAL 0 AND _sofa_ls_remote_out) - # ls-remote output: "\t" - string(REGEX MATCH "^([0-9a-fA-F]+)" _sofa_remote_sha "${_sofa_ls_remote_out}") - if(_sofa_remote_sha AND NOT "${_sofa_remote_sha}" STREQUAL "${_sofa_current_ref}") - set(_sofa_need_fetch TRUE) - message(STATUS "Dependency ${name}: upstream branch ${${upper_name}_GIT_TAG} has new commits (local: ${_sofa_current_ref}, remote: ${_sofa_remote_sha}); re-fetching.") - else() - set(_sofa_need_fetch FALSE) - message(STATUS "Dependency ${name} already at requested branch ${${upper_name}_GIT_TAG} tip; skipping fetch.") - endif() - else() - # ls-remote failed (network issue, ref doesn't exist on remote, etc.). - # Fall through to a full fetch to be safe. - message(STATUS "Dependency ${name}: could not query remote for branch ${${upper_name}_GIT_TAG}; re-fetching to be safe.") - endif() + if(_sofa_need_fetch) + message(STATUS "Dependency ${name}: upstream ${${upper_name}_GIT_TAG} has new commits; re-fetching.") else() - # Tag or SHA – local match is authoritative. - set(_sofa_need_fetch FALSE) message(STATUS "Dependency ${name} already at requested reference ${${upper_name}_GIT_TAG}; skipping fetch.") endif() endif() From 30d0b516448c16247093abd02561173c76121822 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Thu, 16 Jul 2026 17:37:33 +0200 Subject: [PATCH 4/4] Remove 40 character constraint for hash --- .../Config/cmake/SofaMacrosConfigure.cmake | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake index 0f69e574eab..f3cefcf44bc 100644 --- a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake +++ b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake @@ -218,20 +218,17 @@ macro(sofa_fetch_dependency name) if("${_sofa_head}" STREQUAL "${_sofa_want}") set(_sofa_need_fetch FALSE) - # If the ref isn't a bare SHA, it may be a branch whose upstream - # tip has advanced. A lightweight ls-remote check catches that. - # (For tags this is a harmless no-op: the SHA will still match.) - string(REGEX MATCH "^[0-9a-fA-F]{40}$" _sofa_is_sha "${${upper_name}_GIT_TAG}") - if(NOT _sofa_is_sha) - execute_process( - COMMAND "${GIT_EXECUTABLE}" ls-remote origin "${${upper_name}_GIT_TAG}" - WORKING_DIRECTORY "${fetched_dir}" - OUTPUT_VARIABLE _sofa_remote_out OUTPUT_STRIP_TRAILING_WHITESPACE - RESULT_VARIABLE _sofa_remote_rc ERROR_QUIET) - string(REGEX MATCH "^[0-9a-fA-F]+" _sofa_remote_sha "${_sofa_remote_out}") - if(NOT _sofa_remote_rc EQUAL 0 OR NOT "${_sofa_remote_sha}" STREQUAL "${_sofa_head}") - set(_sofa_need_fetch TRUE) - endif() + # If the ref names a branch, upstream may have advanced. + # ls-remote returns nothing for SHAs and the same SHA for tags, + # so we only re-fetch when it succeeds with a *different* hash. + execute_process( + COMMAND "${GIT_EXECUTABLE}" ls-remote origin "${${upper_name}_GIT_TAG}" + WORKING_DIRECTORY "${fetched_dir}" + OUTPUT_VARIABLE _sofa_remote_out OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _sofa_remote_rc ERROR_QUIET) + string(REGEX MATCH "^[0-9a-fA-F]+" _sofa_remote_sha "${_sofa_remote_out}") + if(_sofa_remote_rc EQUAL 0 AND _sofa_remote_sha AND NOT "${_sofa_remote_sha}" STREQUAL "${_sofa_head}") + set(_sofa_need_fetch TRUE) endif() if(_sofa_need_fetch)