From ac760e346f0eb710f6ff3d53facbbe7a9a186d1d Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 1 Oct 2026 07:02:49 -0500 Subject: [PATCH] improve git describe this makes the "git describe" process more robust, by handling three cases better: * no git repository: use a fallback description instead of failing * git tags missing: skip tag matching process, just use commit hash * build is in a worktree: follow worktree metadata to get the repository root --- library/CMakeLists.txt | 59 ++++++++++++++++----- library/git-describe.cmake.in | 87 ++++++++++++++++++------------- library/include/git-describe.h.in | 8 +-- 3 files changed, 100 insertions(+), 54 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 83c27cc535..f4c5c08d0b 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -353,19 +353,52 @@ if(DFHACK_PRERELEASE) endif() configure_file(git-describe.cmake.in ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake @ONLY) -if(EXISTS ${dfhack_SOURCE_DIR}/.git/index AND EXISTS ${dfhack_SOURCE_DIR}/.git/modules/library/xml/index) - add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/include/git-describe.h - COMMAND ${CMAKE_COMMAND} - -D dfhack_SOURCE_DIR:STRING=${dfhack_SOURCE_DIR} - -D GIT_EXECUTABLE:STRING=${GIT_EXECUTABLE} - -P ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake - COMMENT "Obtaining git commit information" - DEPENDS ${dfhack_SOURCE_DIR}/.git/index - ${dfhack_SOURCE_DIR}/.git/modules/library/xml/index - ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake - include/git-describe.h.in - ) -endif() + +# .git is a directory in a normal repository, but a file containing +# "gitdir: " in git worktrees and submodules; resolve both. +function(dfhack_find_git_dir repo_dir out_var) + set(${out_var} "" PARENT_SCOPE) + if(IS_DIRECTORY "${repo_dir}/.git") + set(${out_var} "${repo_dir}/.git" PARENT_SCOPE) + elseif(EXISTS "${repo_dir}/.git") + file(STRINGS "${repo_dir}/.git" gitdir_line REGEX "^gitdir:" LIMIT_COUNT 1) + if(gitdir_line MATCHES "^gitdir:[ \t]*(.+)") + string(STRIP "${CMAKE_MATCH_1}" gitdir) + if(NOT IS_ABSOLUTE "${gitdir}") + get_filename_component(gitdir "${repo_dir}/${gitdir}" ABSOLUTE) + endif() + set(${out_var} "${gitdir}" PARENT_SCOPE) + endif() + endif() +endfunction() + +# Re-run git describe when HEAD or the index changes in the main repository or +# in the library/xml submodule, when they are present. The header is generated +# even outside a git checkout so the build works on plain source trees too. +set(GIT_DESCRIBE_DEPS + ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake + ${CMAKE_CURRENT_SOURCE_DIR}/include/git-describe.h.in +) +dfhack_find_git_dir("${dfhack_SOURCE_DIR}" DFHACK_GIT_DIR) +dfhack_find_git_dir("${dfhack_SOURCE_DIR}/library/xml" DFHACK_XML_GIT_DIR) +foreach(git_dir "${DFHACK_GIT_DIR}" "${DFHACK_XML_GIT_DIR}") + if(git_dir) + foreach(name HEAD index) + if(EXISTS "${git_dir}/${name}") + list(APPEND GIT_DESCRIBE_DEPS "${git_dir}/${name}") + endif() + endforeach() + endif() +endforeach() + +add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/include/git-describe.h +COMMAND ${CMAKE_COMMAND} + -D dfhack_SOURCE_DIR:STRING=${dfhack_SOURCE_DIR} + -D GIT_EXECUTABLE:STRING=${GIT_EXECUTABLE} + -P ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake +COMMENT "Obtaining git commit information" +DEPENDS ${GIT_DESCRIBE_DEPS} +) add_library(dfhack SHARED ${PROJECT_SOURCES}) target_compile_definitions(dfhack PRIVATE BUILD_DFHACK_LIB) diff --git a/library/git-describe.cmake.in b/library/git-describe.cmake.in index d7d469fe8a..ad3221a4dd 100644 --- a/library/git-describe.cmake.in +++ b/library/git-describe.cmake.in @@ -1,48 +1,61 @@ # Generated from git-describe.cmake.in set(DFHACK_BUILD_ID "@DFHACK_BUILD_ID@") -if(NOT(EXISTS "${GIT_EXECUTABLE}")) - message(FATAL_ERROR "git-describe failed: could not find git at ${GIT_EXECUTABLE}") -endif() +# Fallbacks used when the source tree is not a git checkout or when revision +# information cannot be obtained. +set(DFHACK_GIT_DESCRIPTION "@DFHACK_VERSION@-unknown") +set(DFHACK_GIT_COMMIT "unknown") +set(DFHACK_GIT_XML_EXPECTED_COMMIT "unknown") +set(DFHACK_GIT_XML_COMMIT "unknown") +set(DFHACK_GIT_TAGGED FALSE) +set(DFHACK_GIT_XML_MATCH FALSE) -execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --long - WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" - OUTPUT_VARIABLE DFHACK_GIT_DESCRIPTION - OUTPUT_STRIP_TRAILING_WHITESPACE - RESULT_VARIABLE DFHACK_GIT_DESCRIPTION_RESULT) -execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD - WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" - OUTPUT_VARIABLE DFHACK_GIT_COMMIT - OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --exact-match - WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" - RESULT_VARIABLE DFHACK_GIT_TAGGED_RESULT - OUTPUT_QUIET ERROR_QUIET) +if(EXISTS "${dfhack_SOURCE_DIR}/.git") + if(NOT GIT_EXECUTABLE OR NOT EXISTS "${GIT_EXECUTABLE}") + message(WARNING "git-describe: could not find git; version information will be unavailable") + else() + execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --long + WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" + OUTPUT_VARIABLE DFHACK_GIT_DESCRIPTION + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE DFHACK_GIT_DESCRIPTION_RESULT) + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" + OUTPUT_VARIABLE DFHACK_GIT_COMMIT + OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --exact-match + WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" + RESULT_VARIABLE DFHACK_GIT_TAGGED_RESULT + OUTPUT_QUIET ERROR_QUIET) -# Check library/xml for matching commits -execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD:library/xml - WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" - OUTPUT_VARIABLE DFHACK_GIT_XML_EXPECTED_COMMIT - OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD - WORKING_DIRECTORY "${dfhack_SOURCE_DIR}/library/xml" - OUTPUT_VARIABLE DFHACK_GIT_XML_COMMIT - OUTPUT_STRIP_TRAILING_WHITESPACE) + # Check library/xml for matching commits + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD:library/xml + WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" + OUTPUT_VARIABLE DFHACK_GIT_XML_EXPECTED_COMMIT + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(EXISTS "${dfhack_SOURCE_DIR}/library/xml/.git") + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY "${dfhack_SOURCE_DIR}/library/xml" + OUTPUT_VARIABLE DFHACK_GIT_XML_COMMIT + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif() -if(NOT(${DFHACK_GIT_DESCRIPTION_RESULT} EQUAL 0)) - message(FATAL_ERROR "git-describe failed: ${DFHACK_GIT_DESCRIPTION_RESULT}") -endif() + if(DFHACK_GIT_TAGGED_RESULT STREQUAL "0") + set(DFHACK_GIT_TAGGED TRUE) + endif() -if(${DFHACK_GIT_TAGGED_RESULT} EQUAL 0) - set(DFHACK_GIT_TAGGED TRUE) -else() - set(DFHACK_GIT_TAGGED FALSE) -endif() + if(NOT "${DFHACK_GIT_XML_COMMIT}" STREQUAL "" + AND NOT "${DFHACK_GIT_XML_COMMIT}" STREQUAL "unknown" + AND "${DFHACK_GIT_XML_COMMIT}" STREQUAL "${DFHACK_GIT_XML_EXPECTED_COMMIT}") + set(DFHACK_GIT_XML_MATCH TRUE) + endif() -if(${DFHACK_GIT_XML_COMMIT} STREQUAL ${DFHACK_GIT_XML_EXPECTED_COMMIT}) - set(DFHACK_GIT_XML_MATCH TRUE) -else() - set(DFHACK_GIT_XML_MATCH FALSE) + if(NOT DFHACK_GIT_DESCRIPTION_RESULT STREQUAL "0") + # e.g. a clone with no reachable tags; fall back to the commit hash + message(WARNING "git-describe: 'git describe' failed; using commit hash as description") + set(DFHACK_GIT_DESCRIPTION "${DFHACK_GIT_COMMIT}") + endif() + endif() endif() configure_file(${dfhack_SOURCE_DIR}/library/include/git-describe.h.in ${dfhack_SOURCE_DIR}/library/include/git-describe.h) diff --git a/library/include/git-describe.h.in b/library/include/git-describe.h.in index 761fe4ca5f..60dd514887 100644 --- a/library/include/git-describe.h.in +++ b/library/include/git-describe.h.in @@ -1,7 +1,7 @@ -#cmakedefine DFHACK_GIT_DESCRIPTION "@DFHACK_GIT_DESCRIPTION@" -#cmakedefine DFHACK_GIT_COMMIT "@DFHACK_GIT_COMMIT@" -#cmakedefine DFHACK_GIT_XML_EXPECTED_COMMIT "@DFHACK_GIT_XML_EXPECTED_COMMIT@" -#cmakedefine DFHACK_GIT_XML_COMMIT "@DFHACK_GIT_XML_COMMIT@" +#define DFHACK_GIT_DESCRIPTION "@DFHACK_GIT_DESCRIPTION@" +#define DFHACK_GIT_COMMIT "@DFHACK_GIT_COMMIT@" +#define DFHACK_GIT_XML_EXPECTED_COMMIT "@DFHACK_GIT_XML_EXPECTED_COMMIT@" +#define DFHACK_GIT_XML_COMMIT "@DFHACK_GIT_XML_COMMIT@" #define DFHACK_BUILD_ID "@DFHACK_BUILD_ID@" #cmakedefine DFHACK_GIT_TAGGED #cmakedefine DFHACK_GIT_XML_MATCH