Skip to content

Commit c6b9100

Browse files
hjmjohnsonblowekamp
andcommitted
ENH: Auto-compute ITK_VERSION_TWEAK from git commit count
Set the fourth (tweak) version component at configure time to the number of commits since the closest v* tag, instead of a hand-edited date. The computation is pure CMake, so a native build gains no interpreter dependency: `.git_archival.txt` is read first, so an exported tarball -- or a fork that commits a substituted file -- pins the count it declares, and a working tree falls through to `git describe`. A shallow or tagless clone has no reachable tag and yields 0. ITK_VERSION_TWEAK and ITK_VERSION_FULL are exported through ITKConfig.cmake for downstream between-tag gating. The value never reaches itkConfigure.h, so the per-commit count cannot invalidate ccache. Supersedes the manual-file approach of #6667. Co-Authored-By: Bradley Lowekamp <321061+blowekamp@users.noreply.github.com>
1 parent c8d0f07 commit c6b9100

7 files changed

Lines changed: 117 additions & 2 deletions

File tree

.git_archival.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node: $Format:%H$
2+
node-date: $Format:%cI$
3+
describe-name: $Format:%(describe:tags=true,match=v[0-9]*)$
4+
ref-names: $Format:%D$

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
.git* export-ignore
22
.hooks* export-ignore
33
.mailmap export-ignore
4+
# Override the .git* export-ignore above: this file MUST ship in archives, and
5+
# git substitutes the describe string into it so tarballs compute the tweak.
6+
.git_archival.txt -export-ignore export-subst
47

58
*.bat -crlf
69
*.bin -crlf

CMake/ITKConfig.cmake.in

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ set(ITK_REQUIRED_LINK_FLAGS "@ITK_REQUIRED_LINK_FLAGS@")
2525
set(ITK_VERSION_MAJOR "@ITK_VERSION_MAJOR@")
2626
set(ITK_VERSION_MINOR "@ITK_VERSION_MINOR@")
2727
set(ITK_VERSION_PATCH "@ITK_VERSION_PATCH@")
28+
# Tweak is the commit count since the closest v* tag; enables fine-grained
29+
# downstream gating between release tags via ITK_VERSION_FULL.
30+
set(ITK_VERSION_TWEAK "@ITK_VERSION_TWEAK@")
31+
set(
32+
ITK_VERSION_FULL
33+
"@ITK_VERSION_MAJOR@.@ITK_VERSION_MINOR@.@ITK_VERSION_PATCH@.@ITK_VERSION_TWEAK@"
34+
)
2835

2936
# Remove all legacy code completely.
3037
set(ITK_LEGACY_REMOVE "@ITK_LEGACY_REMOVE@")

CMake/ITKConfigVersion.cmake.in

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
set(PACKAGE_VERSION "@ITK_VERSION_MAJOR@.@ITK_VERSION_MINOR@.@ITK_VERSION_PATCH@")
1+
set(
2+
PACKAGE_VERSION
3+
"@ITK_VERSION_MAJOR@.@ITK_VERSION_MINOR@.@ITK_VERSION_PATCH@.@ITK_VERSION_TWEAK@"
4+
)
25
if(NOT "${PACKAGE_FIND_VERSION}" VERSION_GREATER "${PACKAGE_VERSION}")
36
set(PACKAGE_VERSION_COMPATIBLE 1) # compatible with older
4-
if("${PACKAGE_FIND_VERSION}" VERSION_EQUAL "${PACKAGE_VERSION}")
7+
if(
8+
"${PACKAGE_FIND_VERSION}" VERSION_EQUAL "${PACKAGE_VERSION}"
9+
OR "${PACKAGE_FIND_VERSION}" VERSION_EQUAL
10+
"@ITK_VERSION_MAJOR@.@ITK_VERSION_MINOR@.@ITK_VERSION_PATCH@"
11+
)
512
set(PACKAGE_VERSION_EXACT 1) # exact match for this version
613
endif()
714
endif()

CMake/itkVersionTweak.cmake

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Compute ITK_VERSION_TWEAK: commits since the closest v* tag, at configure
2+
# time. The tweak must never reach itkConfigure.h, so a per-commit change of
3+
# this count cannot invalidate ccache.
4+
#
5+
# .git_archival.txt is consulted before git so that an archive, or a fork that
6+
# commits a substituted file, pins the count it declares. In this repository
7+
# the file holds unexpanded $Format:$ placeholders, so a working tree falls
8+
# through to `git describe`.
9+
function(_itk_version_tweak_from_describe describe outvar)
10+
if(describe MATCHES "-([0-9]+)-g[0-9a-f]+$")
11+
set(${outvar} "${CMAKE_MATCH_1}" PARENT_SCOPE)
12+
elseif(describe MATCHES "v[0-9][^ \t]*$")
13+
# Exactly on a tag.
14+
set(${outvar} "0" PARENT_SCOPE)
15+
else()
16+
set(${outvar} "" PARENT_SCOPE)
17+
endif()
18+
endfunction()
19+
20+
function(_itk_compute_version_tweak outvar)
21+
set(tweak "")
22+
23+
if(EXISTS "${ITK_SOURCE_DIR}/.git_archival.txt")
24+
file(
25+
STRINGS
26+
"${ITK_SOURCE_DIR}/.git_archival.txt"
27+
archival
28+
REGEX "^describe-name:"
29+
)
30+
if(archival)
31+
list(GET archival 0 describe)
32+
# Unexpanded placeholders mean this is a checkout, not an archive.
33+
if(NOT describe MATCHES "\\$Format:")
34+
_itk_version_tweak_from_describe("${describe}" tweak)
35+
endif()
36+
endif()
37+
endif()
38+
39+
if(NOT tweak MATCHES "^[0-9]+$")
40+
find_package(Git QUIET)
41+
if(Git_FOUND)
42+
execute_process(
43+
COMMAND
44+
"${GIT_EXECUTABLE}" -C "${ITK_SOURCE_DIR}" describe --tags --long
45+
--match "v[0-9]*"
46+
OUTPUT_VARIABLE describe
47+
OUTPUT_STRIP_TRAILING_WHITESPACE
48+
ERROR_QUIET
49+
)
50+
_itk_version_tweak_from_describe("${describe}" tweak)
51+
endif()
52+
endif()
53+
54+
# No archive metadata and no reachable tag, e.g. a shallow or tagless clone.
55+
if(NOT tweak MATCHES "^[0-9]+$")
56+
set(tweak "0")
57+
endif()
58+
set(${outvar} "${tweak}" PARENT_SCOPE)
59+
endfunction()
60+
61+
_itk_compute_version_tweak(ITK_VERSION_TWEAK)
62+
set(ITK_VERSION_FULL "${ITK_VERSION}.${ITK_VERSION_TWEAK}")

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ project(
6767
C
6868
)
6969

70+
# Fourth (tweak) version component, auto-computed as the commit count since
71+
# the closest v* tag. Enables downstream between-tag gating via
72+
# ITK_VERSION_FULL; see Documentation/docs/migration_guides/index.md.
73+
include(itkVersionTweak)
74+
7075
# Required as a cache variable for FetchContent parent projects can
7176
# resolve ITK's CMake macros and config paths.
7277
set(

Documentation/docs/migration_guides/index.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,33 @@
22

33
These migration guides explain how to update major versions of ITK, whic may contain breaking changes to the API.
44

5+
## Fine-grained versioning between release tags
6+
7+
ITK's fourth (tweak) version component is the number of commits since the
8+
closest `v*` tag, computed automatically at configure time. It exists so
9+
downstream projects building against untagged ITK commits — for example during
10+
a `git bisect` — can make configure-time decisions at commit granularity
11+
between release tags:
12+
13+
```cmake
14+
find_package(ITK REQUIRED)
15+
if(ITK_VERSION_FULL VERSION_GREATER_EQUAL 6.0.0.6461)
16+
set(BASELINE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Baseline/post-change)
17+
else()
18+
set(BASELINE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Baseline/legacy)
19+
endif()
20+
```
21+
22+
`ITK_VERSION_FULL` and `ITK_VERSION_TWEAK` are exported through
23+
`ITKConfig.cmake`. Against an older ITK that predates the tweak component,
24+
`ITK_VERSION_FULL` is unset and the comparison is false, so the legacy branch
25+
is taken.
26+
27+
The count comes from `git describe` in a working tree, and from the
28+
`git archive`-substituted `.git_archival.txt` in an exported source tarball; it
29+
falls back to `0` only when neither git history nor that substitution is
30+
available. No manual edits are needed — the tweak advances with every commit.
31+
532
```{toctree}
633
:hidden:
734
:maxdepth: 3

0 commit comments

Comments
 (0)