diff --git a/AGENTS.md b/AGENTS.md index 175a595bc6d..40fe1454dcf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,7 +35,13 @@ the minimum set for the task at hand. 4. **`Update()` is required** — filters don't execute until called; parameter changes after `Update()` need another call. 5. **Link errors → check `itk-module.cmake`** — missing `DEPENDS` or `PRIVATE_DEPENDS` is the usual cause. 6. **Licensing** — verify AI output does not reproduce third-party code in conflict with Apache 2.0. -7. **Verbose committed text is a review-blocking defect** — not a stylistic preference. Reviewers reject PRs whose commit messages, in-source comments, or PR bodies exceed the budgets in [prose-budget.md](./Documentation/AI/prose-budget.md). Cut prose, not code. +7. **Migration or API changes MUST bump `ITK_VERSION_TWEAK`** — any change + that requires a migration-guide entry or alters public API must also set + `ITK_VERSION_TWEAK` in `CMake/itkVersion.cmake` to the landing date + (`YYYYMMDD`, UTC); other key changes may bump it. The tweak component + gives downstream projects fine-grained version gating between release + tags (see `Documentation/docs/migration_guides/index.md`). +8. **Verbose committed text is a review-blocking defect** — not a stylistic preference. Reviewers reject PRs whose commit messages, in-source comments, or PR bodies exceed the budgets in [prose-budget.md](./Documentation/AI/prose-budget.md). Cut prose, not code. ## Resources diff --git a/CMake/ITKConfig.cmake.in b/CMake/ITKConfig.cmake.in index 367b1b096ae..e9557b05fb7 100644 --- a/CMake/ITKConfig.cmake.in +++ b/CMake/ITKConfig.cmake.in @@ -25,6 +25,13 @@ set(ITK_REQUIRED_LINK_FLAGS "@ITK_REQUIRED_LINK_FLAGS@") set(ITK_VERSION_MAJOR "@ITK_VERSION_MAJOR@") set(ITK_VERSION_MINOR "@ITK_VERSION_MINOR@") set(ITK_VERSION_PATCH "@ITK_VERSION_PATCH@") +# Tweak is the YYYYMMDD date of the newest migration/API change; enables +# fine-grained downstream gating between release tags via ITK_VERSION_FULL. +set(ITK_VERSION_TWEAK "@ITK_VERSION_TWEAK@") +set( + ITK_VERSION_FULL + "@ITK_VERSION_MAJOR@.@ITK_VERSION_MINOR@.@ITK_VERSION_PATCH@.@ITK_VERSION_TWEAK@" +) # Remove all legacy code completely. set(ITK_LEGACY_REMOVE "@ITK_LEGACY_REMOVE@") diff --git a/CMake/ITKConfigVersion.cmake.in b/CMake/ITKConfigVersion.cmake.in index 5ce84d5cbcb..5b869e32cb2 100644 --- a/CMake/ITKConfigVersion.cmake.in +++ b/CMake/ITKConfigVersion.cmake.in @@ -1,7 +1,14 @@ -set(PACKAGE_VERSION "@ITK_VERSION_MAJOR@.@ITK_VERSION_MINOR@.@ITK_VERSION_PATCH@") +set( + PACKAGE_VERSION + "@ITK_VERSION_MAJOR@.@ITK_VERSION_MINOR@.@ITK_VERSION_PATCH@.@ITK_VERSION_TWEAK@" +) if(NOT "${PACKAGE_FIND_VERSION}" VERSION_GREATER "${PACKAGE_VERSION}") set(PACKAGE_VERSION_COMPATIBLE 1) # compatible with older - if("${PACKAGE_FIND_VERSION}" VERSION_EQUAL "${PACKAGE_VERSION}") + if( + "${PACKAGE_FIND_VERSION}" VERSION_EQUAL "${PACKAGE_VERSION}" + OR "${PACKAGE_FIND_VERSION}" VERSION_EQUAL + "@ITK_VERSION_MAJOR@.@ITK_VERSION_MINOR@.@ITK_VERSION_PATCH@" + ) set(PACKAGE_VERSION_EXACT 1) # exact match for this version endif() endif() diff --git a/CMake/itkVersion.cmake b/CMake/itkVersion.cmake index e9509ab406a..78358e75de3 100644 --- a/CMake/itkVersion.cmake +++ b/CMake/itkVersion.cmake @@ -2,3 +2,7 @@ set(ITK_VERSION_MAJOR "6") set(ITK_VERSION_MINOR "0") set(ITK_VERSION_PATCH "0") +# Fine-grained ordering between release tags: YYYYMMDD (UTC) of the most +# recent migration-guide or public-API change; bump allowed for other key +# changes. Never reset; must not reach itkConfigure.h (ccache). +set(ITK_VERSION_TWEAK "20260715") diff --git a/CMakeLists.txt b/CMakeLists.txt index d34521cf0bd..2bb691f4e08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,9 +56,10 @@ set( ITK_VERSION "${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}.${ITK_VERSION_PATCH}" ) +set(ITK_VERSION_FULL "${ITK_VERSION}.${ITK_VERSION_TWEAK}") project( ITK - VERSION ${ITK_VERSION} + VERSION ${ITK_VERSION_FULL} DESCRIPTION "The Insight Toolkit (ITK) is an open-source, cross-platform toolkit for N-dimensional scientific image processing, segmentation, and registration." HOMEPAGE_URL "https://itk.org" diff --git a/Documentation/AI/git-commits.md b/Documentation/AI/git-commits.md index bca987589b4..442ecd85480 100644 --- a/Documentation/AI/git-commits.md +++ b/Documentation/AI/git-commits.md @@ -51,6 +51,15 @@ authoring any commit body. > work-in-progress, add `[WIP]` to the **PR title** — the GitHub "WIP" app > gates merging on that string; remove it when the PR is ready. +## Migration / API changes: tweak version bump required + +A commit that adds a migration-guide entry or changes public API MUST also +update `ITK_VERSION_TWEAK` in `CMake/itkVersion.cmake` to the landing date +(`YYYYMMDD`, UTC) — run `Utilities/Maintenance/BumpVersionTweak.py`. +Other key changes MAY bump it. The tweak enables +fine-grained downstream version gating between release tags; it is never +reset (see `Documentation/docs/migration_guides/index.md`). + ## Commit Message Length Keep commit messages concise. A 1-line subject plus a short body diff --git a/Documentation/docs/migration_guides/index.md b/Documentation/docs/migration_guides/index.md index 7af7696e24d..05982addebe 100644 --- a/Documentation/docs/migration_guides/index.md +++ b/Documentation/docs/migration_guides/index.md @@ -2,6 +2,35 @@ These migration guides explain how to update major versions of ITK, whic may contain breaking changes to the API. +## Fine-grained versioning between release tags + +ITK's fourth (tweak) version component is the `YYYYMMDD` (UTC) date of the +most recent migration-inducing change on `main`. It exists so downstream +projects building against untagged ITK commits — for example during a +`git bisect` — can make configure-time decisions at day granularity between +release tags: + +```cmake +find_package(ITK REQUIRED) +if(ITK_VERSION_FULL VERSION_GREATER_EQUAL 6.0.0.20260715) + set(BASELINE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Baseline/post-change) +else() + set(BASELINE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Baseline/legacy) +endif() +``` + +`ITK_VERSION_FULL` and `ITK_VERSION_TWEAK` are exported through +`ITKConfig.cmake`. Against an older ITK that predates the tweak component, +`ITK_VERSION_FULL` is unset and the comparison is false, so the legacy +branch is taken. + +Policy: any change that requires a migration-guide entry or alters public +API **must** update `ITK_VERSION_TWEAK` in `CMake/itkVersion.cmake` to the +date the change lands (`Utilities/Maintenance/BumpVersionTweak.py` does +this); other significant changes **may** bump it. The value +is never reset — release tags supersede it, and version ordering makes it +expire naturally. + ```{toctree} :hidden: :maxdepth: 3 diff --git a/Documentation/docs/migration_guides/itk_6_migration_guide.md b/Documentation/docs/migration_guides/itk_6_migration_guide.md index 0205b343109..feebaac7a6e 100644 --- a/Documentation/docs/migration_guides/itk_6_migration_guide.md +++ b/Documentation/docs/migration_guides/itk_6_migration_guide.md @@ -640,6 +640,14 @@ target_link_libraries(MyExample ITK::MyModuleModule) For backward compatibility, non-namespaced aliases are created with deprecation warnings. However, new code should use the namespaced `ITK::` targets exclusively. +## Minimum wrapped Python raised to 3.11 + +ITK 6 requires Python 3.11 or newer for Python wrapping; the previous floor was 3.10. Configuring the wrapping against an older interpreter now fails early. + +### What you need to do + +Use Python 3.11+ when building or installing the ITK Python wrapping. The prebuilt `itk` wheels already target 3.11 and newer. + ## NumPy bridge: `image_from_array(arr.T)` is no longer transpose-equivalent to `image_from_array(arr)` ### Rationale diff --git a/Utilities/Maintenance/BumpVersionTweak.py b/Utilities/Maintenance/BumpVersionTweak.py new file mode 100755 index 00000000000..906f458c0e7 --- /dev/null +++ b/Utilities/Maintenance/BumpVersionTweak.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Set ITK_VERSION_TWEAK in CMake/itkVersion.cmake to a YYYYMMDD date. + +Usage: + BumpVersionTweak.py # bump to today's UTC date + BumpVersionTweak.py 20260801 # bump to an explicit date +""" + +import argparse +import datetime +import re +import sys +from pathlib import Path + +VERSION_FILE = Path(__file__).resolve().parents[2] / "CMake" / "itkVersion.cmake" +PATTERN = re.compile(r'(set\(ITK_VERSION_TWEAK ")(\d{8})("\))') + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "date", + nargs="?", + default=datetime.datetime.now(datetime.UTC).strftime("%Y%m%d"), + help="YYYYMMDD value (default: today, UTC)", + ) + args = parser.parse_args() + + try: + datetime.datetime.strptime(args.date, "%Y%m%d") + except ValueError: + parser.error(f"'{args.date}' is not a valid YYYYMMDD date") + + content = VERSION_FILE.read_text() + match = PATTERN.search(content) + if match is None: + print(f"ERROR: ITK_VERSION_TWEAK not found in {VERSION_FILE}", file=sys.stderr) + return 1 + if args.date < match.group(2): + print( + f"ERROR: {args.date} is older than current tweak {match.group(2)}", + file=sys.stderr, + ) + return 1 + if args.date == match.group(2): + print(f"ITK_VERSION_TWEAK already {args.date}") + return 0 + + VERSION_FILE.write_text(PATTERN.sub(rf"\g<1>{args.date}\g<3>", content)) + print(f"ITK_VERSION_TWEAK: {match.group(2)} -> {args.date}") + return 0 + + +if __name__ == "__main__": + sys.exit(main())