From 406822840f1afa8ff270c8239c6c3679b073fe93 Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Sun, 19 Jul 2026 10:35:09 -0500 Subject: [PATCH 1/3] ENH: Add migration sentinel mechanism for between-tag downstream gating Downstream projects build against ITK main between tags, which can be two years apart, and cannot tell whether a specific change is present. Collect one Markdown file per downstream-visible change from CMake/MigrationSentinels into ITK_MIGRATION_SENTINELS and export it through ITKConfig.cmake. Consumers use the list variable rather than a function: CMake's if() cannot invoke a function, and calling one that older ITK does not define is a hard error. An unset variable simply yields false, so an ITK predating this mechanism takes the legacy branch with no guard. The list is exported to CMake only. Embedding it in itkConfigure.h would change every translation unit's preprocessed output on most merges and invalidate ccache fleet-wide; a test asserts it stays out. --- CMake/ITKConfig.cmake.in | 6 ++ CMake/MigrationSentinels/README.md | 12 +++ CMake/itkMigrationSentinels.cmake | 28 ++++++ CMakeLists.txt | 10 +++ .../MigrationSentinelTest/CMakeLists.txt | 36 ++++++++ .../MigrationSentinelTest/CollectTest.cmake | 34 +++++++ .../ConfigureHeaderGuardTest.cmake | 20 +++++ .../Consumer/CMakeLists.txt | 22 +++++ .../MigrationSentinelTest/ConsumerTest.cmake | 16 ++++ .../DownstreamIdiomTest.cmake | 88 +++++++++++++++++++ .../Fixtures/Empty/.gitkeep | 0 .../Populated/ITK_MIGRATION_MATH_SVD.md | 1 + .../Populated/ITK_MIGRATION_PR6532.md | 1 + .../Fixtures/Populated/README.md | 1 + 14 files changed, 275 insertions(+) create mode 100644 CMake/MigrationSentinels/README.md create mode 100644 CMake/itkMigrationSentinels.cmake create mode 100644 Utilities/MigrationSentinelTest/CMakeLists.txt create mode 100644 Utilities/MigrationSentinelTest/CollectTest.cmake create mode 100644 Utilities/MigrationSentinelTest/ConfigureHeaderGuardTest.cmake create mode 100644 Utilities/MigrationSentinelTest/Consumer/CMakeLists.txt create mode 100644 Utilities/MigrationSentinelTest/ConsumerTest.cmake create mode 100644 Utilities/MigrationSentinelTest/DownstreamIdiomTest.cmake create mode 100644 Utilities/MigrationSentinelTest/Fixtures/Empty/.gitkeep create mode 100644 Utilities/MigrationSentinelTest/Fixtures/Populated/ITK_MIGRATION_MATH_SVD.md create mode 100644 Utilities/MigrationSentinelTest/Fixtures/Populated/ITK_MIGRATION_PR6532.md create mode 100644 Utilities/MigrationSentinelTest/Fixtures/Populated/README.md diff --git a/CMake/ITKConfig.cmake.in b/CMake/ITKConfig.cmake.in index 367b1b096ae..7379f576dc4 100644 --- a/CMake/ITKConfig.cmake.in +++ b/CMake/ITKConfig.cmake.in @@ -26,6 +26,12 @@ set(ITK_VERSION_MAJOR "@ITK_VERSION_MAJOR@") set(ITK_VERSION_MINOR "@ITK_VERSION_MINOR@") set(ITK_VERSION_PATCH "@ITK_VERSION_PATCH@") +# Migration sentinels present in this ITK. Each entry asserts that one +# downstream-visible change is in the code. Intended only for fine-grained +# configure-time decisions between tagged releases; see +# Documentation/docs/contributing/migration_sentinels.md +set(ITK_MIGRATION_SENTINELS "@ITK_MIGRATION_SENTINELS@") + # Remove all legacy code completely. set(ITK_LEGACY_REMOVE "@ITK_LEGACY_REMOVE@") diff --git a/CMake/MigrationSentinels/README.md b/CMake/MigrationSentinels/README.md new file mode 100644 index 00000000000..a13a01a9519 --- /dev/null +++ b/CMake/MigrationSentinels/README.md @@ -0,0 +1,12 @@ +# Migration sentinels + +Each `ITK_MIGRATION_*.md` file in this directory asserts that one +downstream-visible change is present in this ITK source tree. The filenames are +collected into `ITK_MIGRATION_SENTINELS` at configure time and exported through +`ITKConfig.cmake`, so downstream projects can gate on a specific change during +the interval between ITK tags. + +Add one file per downstream-visible change. See +`Documentation/docs/contributing/migration_sentinels.md`. + +This file is not a sentinel: the glob matches only `ITK_MIGRATION_*.md`. diff --git a/CMake/itkMigrationSentinels.cmake b/CMake/itkMigrationSentinels.cmake new file mode 100644 index 00000000000..8e240239f84 --- /dev/null +++ b/CMake/itkMigrationSentinels.cmake @@ -0,0 +1,28 @@ +# Migration sentinels: one Markdown file per downstream-visible change. +# +# The resulting list is exported through ITKConfig.cmake ONLY. It must never +# reach itkConfigure.h or any other compiled header: the value changes on most +# merges, and that header is included nearly everywhere, so embedding it would +# invalidate ccache for every translation unit on every build machine. +function(itk_collect_migration_sentinels _sentinel_dir _out_var) + # CONFIGURE_DEPENDS is required during a real configure: without it, adding + # a sentinel file does not re-run CMake and the new sentinel is silently + # missed. It is rejected in -P script mode (CMAKE_SCRIPT_MODE_FILE set), + # which the CollectTest.cmake unit test runs under. + if(CMAKE_SCRIPT_MODE_FILE) + file(GLOB _sentinel_files "${_sentinel_dir}/ITK_MIGRATION_*.md") + else() + file( + GLOB _sentinel_files + CONFIGURE_DEPENDS + "${_sentinel_dir}/ITK_MIGRATION_*.md" + ) + endif() + set(_names "") + foreach(_file IN LISTS _sentinel_files) + get_filename_component(_name "${_file}" NAME_WE) + list(APPEND _names "${_name}") + endforeach() + list(SORT _names) + set(${_out_var} "${_names}" PARENT_SCOPE) +endfunction() diff --git a/CMakeLists.txt b/CMakeLists.txt index d34521cf0bd..d65124a8052 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,15 @@ set( ITK_VERSION "${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}.${ITK_VERSION_PATCH}" ) + +# Migration sentinels: exported through ITKConfig.cmake only, never into a +# compiled header (see CMake/itkMigrationSentinels.cmake). +include(itkMigrationSentinels) +itk_collect_migration_sentinels( + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/MigrationSentinels" + ITK_MIGRATION_SENTINELS +) + project( ITK VERSION ${ITK_VERSION} @@ -903,6 +912,7 @@ if(BUILD_TESTING) ) mark_as_advanced(CTEST_COST_DATA_FILE) add_subdirectory(Utilities/InstallTest) + add_subdirectory(Utilities/MigrationSentinelTest) endif() #----------------------------------------------------------------------------- diff --git a/Utilities/MigrationSentinelTest/CMakeLists.txt b/Utilities/MigrationSentinelTest/CMakeLists.txt new file mode 100644 index 00000000000..8bd87cf5690 --- /dev/null +++ b/Utilities/MigrationSentinelTest/CMakeLists.txt @@ -0,0 +1,36 @@ +itk_add_test( + NAME MigrationSentinelCollect + COMMAND + ${CMAKE_COMMAND} + -DITK_SOURCE_DIR=${ITK_SOURCE_DIR} + -P + ${CMAKE_CURRENT_SOURCE_DIR}/CollectTest.cmake +) + +itk_add_test( + NAME MigrationSentinelDownstreamIdiom + COMMAND + ${CMAKE_COMMAND} + -P + ${CMAKE_CURRENT_SOURCE_DIR}/DownstreamIdiomTest.cmake +) + +itk_add_test( + NAME MigrationSentinelConfigureHeaderGuard + COMMAND + ${CMAKE_COMMAND} + -DITK_CONFIGURE_HEADER=${ITK_BINARY_DIR}/Modules/Core/Common/itkConfigure.h + -P + ${CMAKE_CURRENT_SOURCE_DIR}/ConfigureHeaderGuardTest.cmake +) + +itk_add_test( + NAME MigrationSentinelConsumer + COMMAND + ${CMAKE_COMMAND} + -DCONSUMER_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/Consumer + -DCONSUMER_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/ConsumerBuild + -DITK_BINARY_DIR=${ITK_BINARY_DIR} + -P + ${CMAKE_CURRENT_SOURCE_DIR}/ConsumerTest.cmake +) diff --git a/Utilities/MigrationSentinelTest/CollectTest.cmake b/Utilities/MigrationSentinelTest/CollectTest.cmake new file mode 100644 index 00000000000..5cad54fd561 --- /dev/null +++ b/Utilities/MigrationSentinelTest/CollectTest.cmake @@ -0,0 +1,34 @@ +# Verifies itk_collect_migration_sentinels() against fixed fixtures. +# Fixtures are used rather than the real sentinel directory so these tests do +# not break when real sentinels are expired at tag time. +cmake_minimum_required(VERSION 3.16) +include("${ITK_SOURCE_DIR}/CMake/itkMigrationSentinels.cmake") + +set(_failures 0) +macro(expect_eq _actual _expected _what) + if(NOT "${_actual}" STREQUAL "${_expected}") + message(SEND_ERROR "${_what}: expected [${_expected}] got [${_actual}]") + math(EXPR _failures "${_failures} + 1") + endif() +endmacro() + +# Populated fixture: two sentinels, plus a README.md and a stray file that the +# glob must ignore. +itk_collect_migration_sentinels( + "${CMAKE_CURRENT_LIST_DIR}/Fixtures/Populated" _got +) +expect_eq("${_got}" "ITK_MIGRATION_MATH_SVD;ITK_MIGRATION_PR6532" + "populated fixture (sorted, README.md excluded)" +) + +# Empty fixture: models ITK immediately after a tag, when all sentinels have +# been expired. Must yield an empty list, not an error. +itk_collect_migration_sentinels( + "${CMAKE_CURRENT_LIST_DIR}/Fixtures/Empty" _got_empty +) +expect_eq("${_got_empty}" "" "empty fixture") + +if(_failures GREATER 0) + message(FATAL_ERROR "${_failures} collection check(s) failed") +endif() +message(STATUS "CollectTest: all checks passed") diff --git a/Utilities/MigrationSentinelTest/ConfigureHeaderGuardTest.cmake b/Utilities/MigrationSentinelTest/ConfigureHeaderGuardTest.cmake new file mode 100644 index 00000000000..8ffa42ebb5c --- /dev/null +++ b/Utilities/MigrationSentinelTest/ConfigureHeaderGuardTest.cmake @@ -0,0 +1,20 @@ +# ccache guard: the sentinel list must never reach a compiled header. +# If it did, every translation unit's preprocessed output would change on most +# merges and ccache would miss fleet-wide. +cmake_minimum_required(VERSION 3.16) + +if(NOT EXISTS "${ITK_CONFIGURE_HEADER}") + message(FATAL_ERROR "itkConfigure.h not found at ${ITK_CONFIGURE_HEADER}") +endif() + +file(READ "${ITK_CONFIGURE_HEADER}" _contents) +string(FIND "${_contents}" "ITK_MIGRATION" _pos) +if(NOT _pos EQUAL -1) + message( + FATAL_ERROR + "itkConfigure.h contains 'ITK_MIGRATION'. The migration sentinel list must " + "live only in ITKConfig.cmake; embedding it in a compiled header " + "invalidates ccache for every translation unit on every merge." + ) +endif() +message(STATUS "ConfigureHeaderGuardTest: itkConfigure.h is free of sentinels") diff --git a/Utilities/MigrationSentinelTest/Consumer/CMakeLists.txt b/Utilities/MigrationSentinelTest/Consumer/CMakeLists.txt new file mode 100644 index 00000000000..aa49f99fd77 --- /dev/null +++ b/Utilities/MigrationSentinelTest/Consumer/CMakeLists.txt @@ -0,0 +1,22 @@ +# Minimal consumer: proves ITKConfig.cmake exports ITK_MIGRATION_SENTINELS. +cmake_minimum_required(VERSION 3.16) +# CXX rather than NONE: ITK's config chain reaches find_package(OpenMP) +# via optional modules, and FindOpenMP requires a language to be enabled. +project(MigrationSentinelConsumer CXX) + +find_package(ITK REQUIRED) + +# DEFINED rather than truthiness: the list is legitimately empty when no +# sentinels are pending, but it must still be exported. +if(NOT DEFINED ITK_MIGRATION_SENTINELS) + message( + FATAL_ERROR + "ITKConfig.cmake did not export ITK_MIGRATION_SENTINELS. Downstream " + "projects cannot gate on migrations without it." + ) +endif() + +message( + STATUS + "Consumer saw ITK_MIGRATION_SENTINELS=[${ITK_MIGRATION_SENTINELS}]" +) diff --git a/Utilities/MigrationSentinelTest/ConsumerTest.cmake b/Utilities/MigrationSentinelTest/ConsumerTest.cmake new file mode 100644 index 00000000000..f62675f11be --- /dev/null +++ b/Utilities/MigrationSentinelTest/ConsumerTest.cmake @@ -0,0 +1,16 @@ +# Configures the minimal consumer against the ITK build tree. +cmake_minimum_required(VERSION 3.16) + +execute_process( + COMMAND + "${CMAKE_COMMAND}" -S "${CONSUMER_SOURCE_DIR}" -B "${CONSUMER_BINARY_DIR}" + -DITK_DIR:PATH=${ITK_BINARY_DIR} + RESULT_VARIABLE _result + OUTPUT_VARIABLE _output + ERROR_VARIABLE _output +) + +if(NOT _result EQUAL 0) + message(FATAL_ERROR "consumer configure failed:\n${_output}") +endif() +message(STATUS "ConsumerTest passed:\n${_output}") diff --git a/Utilities/MigrationSentinelTest/DownstreamIdiomTest.cmake b/Utilities/MigrationSentinelTest/DownstreamIdiomTest.cmake new file mode 100644 index 00000000000..bb250f6b40d --- /dev/null +++ b/Utilities/MigrationSentinelTest/DownstreamIdiomTest.cmake @@ -0,0 +1,88 @@ +# Verifies the two-arm downstream idiom in all four states. +cmake_minimum_required(VERSION 3.16) + +set(_failures 0) +macro(expect_branch _cond_result _expected _what) + if(NOT "${_cond_result}" STREQUAL "${_expected}") + message(SEND_ERROR "${_what}: expected ${_expected} got ${_cond_result}") + math(EXPR _failures "${_failures} + 1") + endif() +endmacro() + +# State 1: ITK predating the mechanism. ITK_MIGRATION_SENTINELS is never set. +set(ITK_VERSION 6.0.0) +unset(ITK_MIGRATION_SENTINELS) +if( + ITK_VERSION + VERSION_GREATER_EQUAL + 6.1.0 + OR + ITK_MIGRATION_PR6532 + IN_LIST + ITK_MIGRATION_SENTINELS +) + set(_r TRUE) +else() + set(_r FALSE) +endif() +expect_branch("${_r}" "FALSE" "old ITK, list unset -> legacy branch") + +# State 2: sentinel present, version still below the future tag. +set( + ITK_MIGRATION_SENTINELS + ITK_MIGRATION_MATH_SVD + ITK_MIGRATION_PR6532 +) +if( + ITK_VERSION + VERSION_GREATER_EQUAL + 6.1.0 + OR + ITK_MIGRATION_PR6532 + IN_LIST + ITK_MIGRATION_SENTINELS +) + set(_r TRUE) +else() + set(_r FALSE) +endif() +expect_branch("${_r}" "TRUE" "sentinel present -> new branch") + +# State 3: mechanism present but this sentinel absent. +if( + ITK_VERSION + VERSION_GREATER_EQUAL + 6.1.0 + OR + ITK_MIGRATION_PR9999 + IN_LIST + ITK_MIGRATION_SENTINELS +) + set(_r TRUE) +else() + set(_r FALSE) +endif() +expect_branch("${_r}" "FALSE" "sentinel absent -> legacy branch") + +# State 4: after the tag. Sentinels expired and removed, version arm carries it. +set(ITK_VERSION 6.1.0) +unset(ITK_MIGRATION_SENTINELS) +if( + ITK_VERSION + VERSION_GREATER_EQUAL + 6.1.0 + OR + ITK_MIGRATION_PR6532 + IN_LIST + ITK_MIGRATION_SENTINELS +) + set(_r TRUE) +else() + set(_r FALSE) +endif() +expect_branch("${_r}" "TRUE" "post-tag, sentinels expired -> new branch") + +if(_failures GREATER 0) + message(FATAL_ERROR "${_failures} downstream-idiom check(s) failed") +endif() +message(STATUS "DownstreamIdiomTest: all checks passed") diff --git a/Utilities/MigrationSentinelTest/Fixtures/Empty/.gitkeep b/Utilities/MigrationSentinelTest/Fixtures/Empty/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Utilities/MigrationSentinelTest/Fixtures/Populated/ITK_MIGRATION_MATH_SVD.md b/Utilities/MigrationSentinelTest/Fixtures/Populated/ITK_MIGRATION_MATH_SVD.md new file mode 100644 index 00000000000..ce6fc13d055 --- /dev/null +++ b/Utilities/MigrationSentinelTest/Fixtures/Populated/ITK_MIGRATION_MATH_SVD.md @@ -0,0 +1 @@ +itk::Math::SVD is available as the Eigen-backed SVD entry point. diff --git a/Utilities/MigrationSentinelTest/Fixtures/Populated/ITK_MIGRATION_PR6532.md b/Utilities/MigrationSentinelTest/Fixtures/Populated/ITK_MIGRATION_PR6532.md new file mode 100644 index 00000000000..3d166f92905 --- /dev/null +++ b/Utilities/MigrationSentinelTest/Fixtures/Populated/ITK_MIGRATION_PR6532.md @@ -0,0 +1 @@ +itk::Math::SVD replaces vnl_svd for ITK-internal consumers; see PR #6532. diff --git a/Utilities/MigrationSentinelTest/Fixtures/Populated/README.md b/Utilities/MigrationSentinelTest/Fixtures/Populated/README.md new file mode 100644 index 00000000000..beebbc260e3 --- /dev/null +++ b/Utilities/MigrationSentinelTest/Fixtures/Populated/README.md @@ -0,0 +1 @@ +Test fixture. Files here are inert; the glob matches only ITK_MIGRATION_*.md. From 4376a74c33e54ee38b70b5f5e9e19b4b9eb9d186 Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Sun, 19 Jul 2026 10:35:24 -0500 Subject: [PATCH 2/3] ENH: Add migration sentinel maintenance tooling and documentation Add MigrationSentinels.py with three subcommands: validate, which CI runs so a malformed sentinel cannot merge; expire-itk, which removes every sentinel at a tag because all of them ship in it; and expire-downstream, which reduces a consumer's two-arm condition to its version arm once the tag exists. validate also rejects a misspelled prefix. Such a file is invisible to the ITK_MIGRATION_*.md glob, so the sentinel would silently never be published. expire-downstream pins UTF-8 on the files it rewrites. The locale default is cp1252 on Windows, and the rewrite is in place and not atomic, so an unpinned encoding can silently corrupt non-ASCII content. --- .github/workflows/migration-sentinels.yml | 28 ++ Documentation/docs/contributing/index.md | 1 + .../docs/contributing/migration_sentinels.md | 85 ++++++ Utilities/Maintenance/MigrationSentinels.py | 278 ++++++++++++++++++ .../MigrationSentinelTest/CMakeLists.txt | 28 ++ .../Fixtures/Invalid/ITK_MIGRATION_PR1234.md | 3 + .../Invalid/ITK_MIGRATION_lowercase.md | 1 + .../Fixtures/Invalid/ITK_MIGRATON_PR5678.md | 1 + 8 files changed, 425 insertions(+) create mode 100644 .github/workflows/migration-sentinels.yml create mode 100644 Documentation/docs/contributing/migration_sentinels.md create mode 100755 Utilities/Maintenance/MigrationSentinels.py create mode 100644 Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATION_PR1234.md create mode 100644 Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATION_lowercase.md create mode 100644 Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATON_PR5678.md diff --git a/.github/workflows/migration-sentinels.yml b/.github/workflows/migration-sentinels.yml new file mode 100644 index 00000000000..8a23aaf763d --- /dev/null +++ b/.github/workflows/migration-sentinels.yml @@ -0,0 +1,28 @@ +name: Migration sentinels + +# A malformed sentinel is silently ignored by the CMake glob, so it would never +# be published to downstream projects. Validate on every PR that touches one. + +on: + pull_request: + paths: + - 'CMake/MigrationSentinels/**' + - 'Utilities/Maintenance/MigrationSentinels.py' + - '.github/workflows/migration-sentinels.yml' + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: '3.12' + - name: Validate migration sentinels + run: | + python3 Utilities/Maintenance/MigrationSentinels.py validate \ + || { + echo "::error::A migration sentinel is malformed. See" \ + "Documentation/docs/contributing/migration_sentinels.md" + exit 1 + } diff --git a/Documentation/docs/contributing/index.md b/Documentation/docs/contributing/index.md index bb36f8305b7..3ec32adcc40 100644 --- a/Documentation/docs/contributing/index.md +++ b/Documentation/docs/contributing/index.md @@ -468,6 +468,7 @@ ITK Git Cheatsheet dashboard.md updating_third_party.md +migration_sentinels.md python_packaging.md ../README.md ``` diff --git a/Documentation/docs/contributing/migration_sentinels.md b/Documentation/docs/contributing/migration_sentinels.md new file mode 100644 index 00000000000..d638d5ebf00 --- /dev/null +++ b/Documentation/docs/contributing/migration_sentinels.md @@ -0,0 +1,85 @@ +# Migration sentinels + +A migration sentinel lets a downstream project detect, at CMake configure time, +that one specific ITK change is present — during the interval between ITK tags, +which can run two years. + +The motivating case is `git bisect`. A downstream project bisecting ITK to find +which change broke it is configured against a sequence of arbitrary commits, and +at each step must choose which baseline images to test against. Sentinels are +plain files in the source tree, so they are readable in a detached `HEAD`, a +shallow clone, or an exported archive — states in which `git describe` fails and +`git rev-list --count` silently returns a wrong answer. + +Sentinels are **not** a general feature-detection API. They are removed at each +tag. Sentinels are intended **only** for fine-grained configure-time decisions +between tagged releases. + +## Adding a sentinel + +Add one file to `CMake/MigrationSentinels/` containing a single line of +description: + +``` +CMake/MigrationSentinels/ITK_MIGRATION_PR6532.md +``` + +```markdown +itk::Math::SVD replaces vnl_svd for ITK-internal consumers; see PR #6532. +``` + +Choose exactly one naming pattern: + +| Pattern | When | +|---|---| +| `ITK_MIGRATION_PR` | **Strongly preferred** — traceable to the pull request. | +| `ITK_MIGRATION_` | No single PR captures the change. | +| `ITK_MIGRATION_HASH_` | Only a commit identifies the change. | + +CI rejects a name matching none of these. It also rejects a misspelled prefix, +which would otherwise be silently ignored by the glob and never published. + +## Using a sentinel downstream + +```cmake +find_package(ITK REQUIRED) + +if(ITK_VERSION VERSION_GREATER_EQUAL 6.1.0 + OR ITK_MIGRATION_PR6532 IN_LIST ITK_MIGRATION_SENTINELS) + set(BASELINE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Baseline/post-6532) +else() + set(BASELINE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Baseline/legacy) +endif() +``` + +Name the ITK release you expect to contain the change as the version arm. Both +arms are required: + +- The **sentinel arm** is true between tags, before any release contains it. +- The **version arm** is true after the tag, once sentinels have been expired. + +Against an ITK predating this mechanism, `ITK_MIGRATION_SENTINELS` is unset, +`IN_LIST` is false, and the legacy branch is taken — no guard needed. This is +also why the mechanism is a list variable rather than a function: calling a +function that older ITK does not define is a hard `Unknown CMake command` +error, and CMake's `if()` cannot invoke a function anyway. + +## Expiring at a tag + +Run once after tagging: + +```bash +python3 Utilities/Maintenance/MigrationSentinels.py expire-itk +python3 Utilities/Maintenance/MigrationSentinels.py \ + expire-downstream --tag 6.1.0 --repo ../BRAINSTools +``` + +Order does not matter and neither step is urgent. A downstream not yet rewritten +keeps working against the tagged ITK because the version arm is now true. + +## Why this is not in a compiled header + +`ITK_MIGRATION_SENTINELS` is exported through `ITKConfig.cmake` only. Embedding +it in `itkConfigure.h` would change every translation unit's preprocessed output +on most merges and destroy ccache hit rates on every build machine. The +`MigrationSentinelConfigureHeaderGuard` test enforces this. diff --git a/Utilities/Maintenance/MigrationSentinels.py b/Utilities/Maintenance/MigrationSentinels.py new file mode 100755 index 00000000000..3f654632215 --- /dev/null +++ b/Utilities/Maintenance/MigrationSentinels.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python + + +description = """ +Manage ITK migration sentinels. + +Each CMake/MigrationSentinels/ITK_MIGRATION_*.md file asserts that one +downstream-visible change is present in the ITK source tree. The filenames are +collected into ITK_MIGRATION_SENTINELS at configure time and exported through +ITKConfig.cmake, letting downstream projects gate on a specific change during +the interval between ITK tags. + +Sentinels are deliberately short-lived. They are removed at each tag, after +which the version arm of the downstream condition carries the same meaning. +""" + +import argparse +import re +import subprocess +import sys +from pathlib import Path + +# Approved sentinel name patterns, most specific first. ITK_MIGRATION_PR is +# strongly preferred because it is traceable to the pull request. +SENTINEL_PATTERNS = ( + ("ITK_MIGRATION_PR", re.compile(r"^ITK_MIGRATION_PR[1-9][0-9]*$")), + ( + "ITK_MIGRATION_HASH_", + re.compile(r"^ITK_MIGRATION_HASH_[0-9a-f]{7,40}$"), + ), + ( + "ITK_MIGRATION_", + re.compile(r"^ITK_MIGRATION_[A-Z][A-Z0-9_]*$"), + ), +) + +SENTINEL_GLOB = "ITK_MIGRATION_*.md" + + +def default_sentinel_dir() -> Path: + """CMake/MigrationSentinels relative to this script's location in ITK.""" + return ( + Path(__file__).resolve().parent.parent.parent / "CMake" / "MigrationSentinels" + ) + + +def matched_pattern(name: str) -> str | None: + for label, pattern in SENTINEL_PATTERNS: + if pattern.match(name): + return label + return None + + +def sentinel_files(sentinel_dir: Path) -> list[Path]: + return sorted(sentinel_dir.glob(SENTINEL_GLOB)) + + +def validate(sentinel_dir: Path) -> int: + if not sentinel_dir.is_dir(): + print(f"error: {sentinel_dir} is not a directory", file=sys.stderr) + return 1 + + errors = [] + + for path in sentinel_files(sentinel_dir): + name = path.stem + if matched_pattern(name) is None: + errors.append( + f"{path.name}: '{name}' matches no approved pattern " + f"({', '.join(label for label, _ in SENTINEL_PATTERNS)})" + ) + lines = [line.strip() for line in path.read_text().splitlines()] + described = [line for line in lines if line] + if len(described) != 1: + errors.append( + f"{path.name}: expected exactly one non-empty description line, " + f"found {len(described)}" + ) + + # A file whose prefix is misspelled is silently ignored by the CMake glob, + # so the sentinel would never be published. Catch it here instead. + for path in sorted(sentinel_dir.iterdir()): + if path.is_dir() or path.name == "README.md": + continue + if path.suffix == ".md" and path.name.startswith("ITK_MIGRATION_"): + continue + errors.append( + f"{path.name}: not matched by the '{SENTINEL_GLOB}' glob, so it " + f"would be silently ignored; rename it or remove it" + ) + + for message in errors: + print(f"error: {message}", file=sys.stderr) + + if errors: + print(f"{len(errors)} sentinel problem(s) found", file=sys.stderr) + return 1 + + print(f"{len(sentinel_files(sentinel_dir))} sentinel(s) validated") + return 0 + + +def expire_itk(sentinel_dir: Path, dry_run: bool) -> int: + """Remove every sentinel file. Run once, immediately after tagging. + + Every sentinel present at a tag ships in that tag by definition, so the + directory is emptied rather than filtered. Downstream projects that have + not yet been rewritten keep working: the version arm of their condition is + now true even though the sentinel is gone. + """ + if not sentinel_dir.is_dir(): + print(f"error: {sentinel_dir} is not a directory", file=sys.stderr) + return 1 + + paths = sentinel_files(sentinel_dir) + if not paths: + print("no sentinels to expire") + return 0 + + for path in paths: + print(f"expiring {path.name}") + if dry_run: + print(f"dry run: {len(paths)} sentinel(s) left in place") + return 0 + + subprocess.run( + ["git", "rm", "--quiet", "--"] + [str(p) for p in paths], + check=True, + ) + print(f"expired {len(paths)} sentinel(s); commit the result") + return 0 + + +# The two-arm downstream condition. \s matches newlines, so the condition may +# be wrapped across lines. Both if() and elseif() are handled: a missed elseif +# would silently keep a sentinel reference alive after the sentinel is gone. +CONDITION_RE = re.compile( + r"(?P[ \t]*)(?Pif|elseif)\s*\(\s*" + r"ITK_VERSION\s+VERSION_GREATER_EQUAL\s+(?P[0-9][0-9A-Za-z.]*)\s+" + r"OR\s+(?PITK_MIGRATION_[A-Za-z0-9_]+)\s+" + r"IN_LIST\s+ITK_MIGRATION_SENTINELS\s*\)" +) + +CMAKE_FILE_GLOBS = ("*.cmake", "CMakeLists.txt") + + +EXCLUDED_DIR_NAMES = {".git", "build", "_build", ".pixi"} + + +def _cmake_files(repo: Path) -> list[Path]: + found: list[Path] = [] + for pattern in CMAKE_FILE_GLOBS: + found.extend(repo.rglob(pattern)) + return sorted(p for p in found if EXCLUDED_DIR_NAMES.isdisjoint(p.parts)) + + +def expire_downstream(repo: Path, tag: str, sentinel_dir: Path, dry_run: bool) -> int: + """Reduce two-arm conditions for `tag` to their version arm. + + Idempotent: a condition already reduced no longer matches CONDITION_RE. + """ + if not repo.is_dir(): + print(f"error: {repo} is not a directory", file=sys.stderr) + return 1 + + known = ( + {p.stem for p in sentinel_files(sentinel_dir)} + if sentinel_dir.is_dir() + else set() + ) + + rewritten = 0 + skipped_other_tag = 0 + unknown_sentinels = [] + + for path in _cmake_files(repo): + try: + original = path.read_text(encoding="utf-8") + except UnicodeDecodeError: + print(f"warning: skipping {path}: not valid UTF-8", file=sys.stderr) + continue + + def replace(match: re.Match) -> str: + nonlocal rewritten, skipped_other_tag + if match.group("tag") != tag: + skipped_other_tag += 1 + return match.group(0) + sentinel = match.group("sentinel") + if known and sentinel not in known: + unknown_sentinels.append(f"{path}: {sentinel}") + rewritten += 1 + return ( + f"{match.group('indent')}{match.group('kw')}" + f"(ITK_VERSION VERSION_GREATER_EQUAL {tag})" + ) + + updated = CONDITION_RE.sub(replace, original) + if updated != original and not dry_run: + path.write_text(updated, encoding="utf-8") + + for entry in unknown_sentinels: + print( + f"warning: {entry} is not published by ITK; it may be a typo or " + f"already expired", + file=sys.stderr, + ) + + action = "would rewrite" if dry_run else "rewrote" + print(f"{action} {rewritten} condition(s) for tag {tag}") + if skipped_other_tag: + print(f"left {skipped_other_tag} condition(s) for other tags untouched") + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser( + description=description, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + subparsers = parser.add_subparsers(dest="command", required=True) + + validate_parser = subparsers.add_parser( + "validate", help="check that every sentinel is well-formed" + ) + validate_parser.add_argument( + "--sentinel-dir", + type=Path, + default=default_sentinel_dir(), + help="directory holding the sentinel files", + ) + + expire_itk_parser = subparsers.add_parser( + "expire-itk", help="git rm every sentinel file (run once, after tagging)" + ) + expire_itk_parser.add_argument( + "--sentinel-dir", + type=Path, + default=default_sentinel_dir(), + help="directory holding the sentinel files", + ) + expire_itk_parser.add_argument( + "--dry-run", + action="store_true", + help="list what would be removed without removing it", + ) + + expire_downstream_parser = subparsers.add_parser( + "expire-downstream", + help="reduce two-arm conditions in a downstream repo to the version arm", + ) + expire_downstream_parser.add_argument( + "--tag", required=True, help="version whose conditions should be reduced" + ) + expire_downstream_parser.add_argument( + "--repo", type=Path, required=True, help="downstream repository to rewrite" + ) + expire_downstream_parser.add_argument( + "--sentinel-dir", + type=Path, + default=default_sentinel_dir(), + help="ITK sentinel directory, used to flag unknown sentinels", + ) + expire_downstream_parser.add_argument( + "--dry-run", action="store_true", help="report without writing" + ) + + args = parser.parse_args() + if args.command == "validate": + return validate(args.sentinel_dir) + if args.command == "expire-itk": + return expire_itk(args.sentinel_dir, args.dry_run) + if args.command == "expire-downstream": + return expire_downstream(args.repo, args.tag, args.sentinel_dir, args.dry_run) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Utilities/MigrationSentinelTest/CMakeLists.txt b/Utilities/MigrationSentinelTest/CMakeLists.txt index 8bd87cf5690..b0bb8b4c90a 100644 --- a/Utilities/MigrationSentinelTest/CMakeLists.txt +++ b/Utilities/MigrationSentinelTest/CMakeLists.txt @@ -34,3 +34,31 @@ itk_add_test( -P ${CMAKE_CURRENT_SOURCE_DIR}/ConsumerTest.cmake ) + +if(Python3_EXECUTABLE) + itk_add_test( + NAME MigrationSentinelValidate + COMMAND + ${Python3_EXECUTABLE} + ${ITK_SOURCE_DIR}/Utilities/Maintenance/MigrationSentinels.py + validate + --sentinel-dir + ${ITK_SOURCE_DIR}/CMake/MigrationSentinels + ) + + itk_add_test( + NAME MigrationSentinelValidateRejects + COMMAND + ${Python3_EXECUTABLE} + ${ITK_SOURCE_DIR}/Utilities/Maintenance/MigrationSentinels.py + validate + --sentinel-dir + ${ITK_SOURCE_DIR}/Utilities/MigrationSentinelTest/Fixtures/Invalid + ) + set_tests_properties( + MigrationSentinelValidateRejects + PROPERTIES + WILL_FAIL + TRUE + ) +endif() diff --git a/Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATION_PR1234.md b/Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATION_PR1234.md new file mode 100644 index 00000000000..5a13922f873 --- /dev/null +++ b/Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATION_PR1234.md @@ -0,0 +1,3 @@ +First line. + +Second paragraph makes this two description lines. diff --git a/Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATION_lowercase.md b/Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATION_lowercase.md new file mode 100644 index 00000000000..a9903b1e99d --- /dev/null +++ b/Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATION_lowercase.md @@ -0,0 +1 @@ +Lowercase name is not an approved pattern. diff --git a/Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATON_PR5678.md b/Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATON_PR5678.md new file mode 100644 index 00000000000..7ce20c3633a --- /dev/null +++ b/Utilities/MigrationSentinelTest/Fixtures/Invalid/ITK_MIGRATON_PR5678.md @@ -0,0 +1 @@ +Typo in the prefix means the glob silently ignores this file. From 91876b448d4d0f95f4e800c06f992a19e5e06a02 Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Sun, 19 Jul 2026 10:35:35 -0500 Subject: [PATCH 3/3] ENH: Pre-populate migration sentinels from the ITK 6 migration guide Every pull request that added a downstream-visible change to the ITK 6 migration guide gets a sentinel, so downstream projects can gate on any of them before a release contains one. The 6.0 prerelease tags all report ITK_VERSION 6.0.0, so version comparison cannot separate these changes from each other or from current main; the sentinel is the only signal that distinguishes them. Derived by mapping the guide's history to pull requests. Commits that edited the guide without introducing a migration -- a typo fix, a master-to-main prose update, a rewrite of an existing migration's wording, and the commit that created the file -- are excluded. --- CMake/MigrationSentinels/ITK_MIGRATION_PR4704.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR4715.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR4729.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR4737.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR4744.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR4925.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5076.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5286.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5382.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5721.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5752.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5775.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5776.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5791.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5803.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR5995.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6003.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6144.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6183.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6427.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6441.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6469.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6475.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6487.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6504.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6524.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6553.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6569.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6573.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6594.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6602.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6635.md | 1 + CMake/MigrationSentinels/ITK_MIGRATION_PR6649.md | 1 + 33 files changed, 33 insertions(+) create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR4704.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR4715.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR4729.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR4737.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR4744.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR4925.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5076.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5286.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5382.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5721.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5752.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5775.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5776.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5791.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5803.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR5995.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6003.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6144.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6183.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6427.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6441.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6469.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6475.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6487.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6504.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6524.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6553.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6569.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6573.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6594.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6602.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6635.md create mode 100644 CMake/MigrationSentinels/ITK_MIGRATION_PR6649.md diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR4704.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR4704.md new file mode 100644 index 00000000000..4cf01791234 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR4704.md @@ -0,0 +1 @@ +ENH: Finalize ITKv5 const function API change (PR #4704). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR4715.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR4715.md new file mode 100644 index 00000000000..86389bc57aa --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR4715.md @@ -0,0 +1 @@ +ENH: Remove ITKv4 compatibility support (PR #4715). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR4729.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR4729.md new file mode 100644 index 00000000000..153a8e8f521 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR4729.md @@ -0,0 +1 @@ +ENH: Remove features that were deprecated in ITKv5 (PR #4729). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR4737.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR4737.md new file mode 100644 index 00000000000..5788717cc8a --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR4737.md @@ -0,0 +1 @@ +Remove cxx aliases (PR #4737). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR4744.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR4744.md new file mode 100644 index 00000000000..d15bd3f24c2 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR4744.md @@ -0,0 +1 @@ +STYLE: Remove outdated maintenance scripts (PR #4744). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR4925.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR4925.md new file mode 100644 index 00000000000..93b5176e062 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR4925.md @@ -0,0 +1 @@ +ENH: Let `PointSet::Clone()` do a "deep copy", instead of no copy at all (PR #4925). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5076.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5076.md new file mode 100644 index 00000000000..cc7fab0f131 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5076.md @@ -0,0 +1 @@ +DOC: Note migration guide replacing `CoordinateType` with `CoordRepType` (PR #5076). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5286.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5286.md new file mode 100644 index 00000000000..0a6bf415223 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5286.md @@ -0,0 +1 @@ +ENH: Remove dummy VNLInstantiation library (PR #5286). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5382.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5382.md new file mode 100644 index 00000000000..e6d935dca03 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5382.md @@ -0,0 +1 @@ +DOC: Add migration documentation for AnatomicalOrientation (PR #5382). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5721.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5721.md new file mode 100644 index 00000000000..45774cbbc83 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5721.md @@ -0,0 +1 @@ +Modernize ITK Module System with CMake Interface Libraries (PR #5721). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5752.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5752.md new file mode 100644 index 00000000000..0bea773e51e --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5752.md @@ -0,0 +1 @@ +COMP: Remove undefined long double swig wrapping (PR #5752). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5775.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5775.md new file mode 100644 index 00000000000..c33ca7a5999 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5775.md @@ -0,0 +1 @@ +BUG: Fix image_from_array shape handling for F-contiguous arrays (PR #5775). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5776.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5776.md new file mode 100644 index 00000000000..340171ae2c3 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5776.md @@ -0,0 +1 @@ +ENH: Release Python GIL during ITK operations (PR #5776). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5791.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5791.md new file mode 100644 index 00000000000..0316e30d7b8 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5791.md @@ -0,0 +1 @@ +DOC: Add migration guide to updated GTest target names (PR #5791). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5803.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5803.md new file mode 100644 index 00000000000..4ed003554a8 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5803.md @@ -0,0 +1 @@ +Deprecate `ImageConstIterator::GetIndex()`, use ComputeIndex() in tests and examples (PR #5803). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR5995.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR5995.md new file mode 100644 index 00000000000..a6ebc074067 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR5995.md @@ -0,0 +1 @@ +COMP: Use std::unique_ptr in FEMLinearSystemWrapperDenseVNL (PR #5995). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6003.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6003.md new file mode 100644 index 00000000000..5f0da201671 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6003.md @@ -0,0 +1 @@ +ENH: Use NumberToString for lossless float metadata in NIfTI reader (PR #6003). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6144.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6144.md new file mode 100644 index 00000000000..cd43cf5c996 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6144.md @@ -0,0 +1 @@ +COMP: Require VTK >= 9.1 in ITKVtkGlue; drop legacy module-system shims (PR #6144). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6183.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6183.md new file mode 100644 index 00000000000..bba77c5f33a --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6183.md @@ -0,0 +1 @@ +ENH: Replace custom LazyITKModule with native PEP 562 lazy loading (PR #6183). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6427.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6427.md new file mode 100644 index 00000000000..89cc4c66d0f --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6427.md @@ -0,0 +1 @@ +ENH: Decouple itk::Math from vnl_math (constants and functions) (PR #6427). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6441.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6441.md new file mode 100644 index 00000000000..ce0d7b37330 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6441.md @@ -0,0 +1 @@ +ENH: PocketFFT as a permissive, dependency-free default FFT backend (PR #6441). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6469.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6469.md new file mode 100644 index 00000000000..0cfc2d644ea --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6469.md @@ -0,0 +1 @@ +ENH: Reimplement GDCMSeriesFileNames on gdcm::IPPSorter/Scanner (drop deprecated SerieHelper) (PR #6469). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6475.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6475.md new file mode 100644 index 00000000000..38d3e6c3674 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6475.md @@ -0,0 +1 @@ +ENH: Eigen-backed itk eigendecompositions; deprecate vnl_*_eigensystem and eispack (PR #6475). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6487.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6487.md new file mode 100644 index 00000000000..f48baa451d0 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6487.md @@ -0,0 +1 @@ +ENH: Add Eigen-backed itk::Math::SVD and migrate ITK's vnl_svd consumers (PR #6487). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6504.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6504.md new file mode 100644 index 00000000000..ff27d758390 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6504.md @@ -0,0 +1 @@ +COMP: Modernize deprecated macros in ingested filter/IO modules (PR #6504). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6524.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6524.md new file mode 100644 index 00000000000..b96ea0b06cc --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6524.md @@ -0,0 +1 @@ +ENH: Retire FEM modules to InsightSoftwareConsortium/ITKFEM remote (PR #6524). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6553.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6553.md new file mode 100644 index 00000000000..1e4c9831c74 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6553.md @@ -0,0 +1 @@ +DOC: Document itkDCMTKFileReader privatization in ITKv6 migration guide (PR #6553). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6569.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6569.md new file mode 100644 index 00000000000..c88b7ea83dc --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6569.md @@ -0,0 +1 @@ +BUG: Fix JointHistogramMutualInformation metric marginals and derivative (PR #6569). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6573.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6573.md new file mode 100644 index 00000000000..d99b7890140 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6573.md @@ -0,0 +1 @@ +Deprecate mpl::IsNumber (PR #6573). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6594.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6594.md new file mode 100644 index 00000000000..c13a41997a2 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6594.md @@ -0,0 +1 @@ +BUG: Center collapsed-axis origin in projection and accumulate filters (PR #6594). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6602.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6602.md new file mode 100644 index 00000000000..b57e92f40d8 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6602.md @@ -0,0 +1 @@ +BUG: Smooth structure tensor with an isotropic Gaussian kernel (PR #6602). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6635.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6635.md new file mode 100644 index 00000000000..f89693eeeff --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6635.md @@ -0,0 +1 @@ +BUG: Seed entropy threshold calculators with NonpositiveMin (PR #6635). diff --git a/CMake/MigrationSentinels/ITK_MIGRATION_PR6649.md b/CMake/MigrationSentinels/ITK_MIGRATION_PR6649.md new file mode 100644 index 00000000000..866d31c8c24 --- /dev/null +++ b/CMake/MigrationSentinels/ITK_MIGRATION_PR6649.md @@ -0,0 +1 @@ +DOC: note HDF5ImageIO compression-default change in migration guide (PR #6649).