From aa60223243ec572f53be57d459b75ebc218996cf Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 9 Nov 2026 23:39:36 +0000 Subject: [PATCH 1/5] itk-core, itk-numerics, itk-io, itk-filtering, itk-registration, itk-segmentation, itk: add version 5.4.7 ITKPythonPackage (InsightSoftwareConsortium/ITKPythonPackage v5.4.7) builds ITK's full Python wrapping once per interpreter and carves all 7 PyPI wheels out of that same tree with -DITKPythonPackage_ITK_BINARY_REUSE=ON; its CMakeLists.txt decides which module goes in which wheel from the whole module graph, so the split cannot be reproduced per wheel (gotcha 382, same shape as pyside6/pyside6-essentials/pyside6-addons). One combined build-itk.yml builds all 7 from a single standalone ITK configure+build per interpreter and 7 disjoint _publish-wheel.yml jobs, with docs/packages/itk.yaml, itk-numerics.yaml, itk-io.yaml, itk-filtering.yaml, itk-registration.yaml and itk-segmentation.yaml kept in lockstep with itk-core.yaml's versions (one `setup` job, keyed on itk-core, the same way build-mlx.yml keeps docs/packages/mlx-cpu.yaml in lockstep with mlx.yaml). riscv64-specific adjustments from upstream's own scripts/internal/manylinux-build-wheels.sh "multiple wheels" path: - WITH_RVV=OFF reaches ITK's vendored zlib-ng directly (no patch needed here, unlike build-simpleitk.yml's SuperBuild layer, since this configures ITK's own CMakeLists.txt directly). - cmake pinned to /usr/bin/cmake for the standalone ITK configure/build only (gotcha 358); the per-wheel scikit-build-core carve never reconfigures the vendored third-party trees that conflict with the image's newer cmake. - doxygen installed from Rocky 10's CRB repo (enabled by default on this image) instead of upstream's x86_64/aarch64-only prebuilt tarball. - No riscv64 CastXML prebuilt binary exists upstream (Wrapping/Generators/CastXML/CMakeLists.txt only lists Linux x86_64/aarch64, Windows, macOS), so this relies on that same CMakeLists.txt's existing generic fallback: ITK_USE_SYSTEM_CASTXML defaults OFF, and CastXML 0.6.5 is built from source together with LLVM/Clang 18.1.3 as an ExternalProject exactly as it would be on any other platform without a prebuilt binary - no override needed, just more build time. - Module_ITKTBB left OFF: oneTBB 2021.9.0 has no verified riscv64 support and is itself another from-source dependency; ITK's default Pool threader covers it. - timeout-minutes: 7000, close to the 5-day ceiling GitHub enforces on self-hosted runners rather than the 48h build-vtk.yml uses: ITK's own Azure CI (default wrap only) is already ~2h35-2h50 on 2 cores, this build adds unsigned_short/double/complex_double pixel types and 4-D wrapping plus the CastXML/LLVM from-source build on top, and build-vtk.yml's comparable full-module build already needed most of its 48h on these 4-core runners. --- .github/workflows/build-itk.yml | 435 ++++++++++++++++++++++++++++ docs/packages/itk-core.yaml | 9 + docs/packages/itk-filtering.yaml | 9 + docs/packages/itk-io.yaml | 9 + docs/packages/itk-numerics.yaml | 9 + docs/packages/itk-registration.yaml | 9 + docs/packages/itk-segmentation.yaml | 9 + docs/packages/itk.yaml | 9 + 8 files changed, 498 insertions(+) create mode 100644 .github/workflows/build-itk.yml create mode 100644 docs/packages/itk-core.yaml create mode 100644 docs/packages/itk-filtering.yaml create mode 100644 docs/packages/itk-io.yaml create mode 100644 docs/packages/itk-numerics.yaml create mode 100644 docs/packages/itk-registration.yaml create mode 100644 docs/packages/itk-segmentation.yaml create mode 100644 docs/packages/itk.yaml diff --git a/.github/workflows/build-itk.yml b/.github/workflows/build-itk.yml new file mode 100644 index 00000000000..a9d429996a2 --- /dev/null +++ b/.github/workflows/build-itk.yml @@ -0,0 +1,435 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# ITKPythonPackage (github.com/InsightSoftwareConsortium/ITKPythonPackage, tag v5.4.7) +# builds ITK's full Python wrapping ONCE per interpreter and then carves every wheel in +# scripts/WHEEL_NAMES.txt out of that same tree with -DITKPythonPackage_ITK_BINARY_REUSE=ON +# (its CMakeLists.txt's ipp_wheel_to_group logic decides which module goes in which wheel +# from the whole ITK module graph, so the split cannot be reproduced per-wheel). This mirrors +# scripts/internal/manylinux-build-wheels.sh's "multiple wheels" path directly rather than +# running that script verbatim, since it has no riscv64 case for ARCH/doxygen (CLAUDE.md +# gotcha 382, same shape as pyside6/pyside6-essentials/pyside6-addons). docs/packages/itk.yaml, +# itk-numerics.yaml, itk-io.yaml, itk-filtering.yaml, itk-registration.yaml and +# itk-segmentation.yaml are kept in lockstep with itk-core.yaml's versions (same build, same +# single `setup` job), the same way build-mlx.yml keeps docs/packages/mlx-cpu.yaml in lockstep +# with mlx.yaml. +name: Build itk wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/itk-core.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-itk.yml' + - 'docs/packages/itk-core.yaml' + - 'docs/packages/itk-numerics.yaml' + - 'docs/packages/itk-io.yaml' + - 'docs/packages/itk-filtering.yaml' + - 'docs/packages/itk-registration.yaml' + - 'docs/packages/itk-segmentation.yaml' + - 'docs/packages/itk.yaml' + push: + branches: [main] + paths: + - '.github/workflows/build-itk.yml' + - 'docs/packages/itk-core.yaml' + - 'docs/packages/itk-numerics.yaml' + - 'docs/packages/itk-io.yaml' + - 'docs/packages/itk-filtering.yaml' + - 'docs/packages/itk-registration.yaml' + - 'docs/packages/itk-segmentation.yaml' + - 'docs/packages/itk.yaml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: itk-core + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build itk ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + # 5 days: the hard ceiling GitHub enforces for jobs on self-hosted runners. ITK's own + # Azure LinuxPython CI (2-core, default wrap only) is ~2h35-2h50 + # (open.cdash.org, project Insight); this config adds unsigned_short/double/complex_double + # pixel types and 4-D wrapping on top of that, and has to ExternalProject-build CastXML + # 0.6.5 + LLVM/Clang 18.1.3 from source first since Wrapping/Generators/CastXML/CMakeLists.txt + # ships no riscv64 prebuilt binary (only Linux x86_64/aarch64, Windows, macOS). build-vtk.yml's + # comparable full-module C++ build needed most of its 48h budget on these 4-core runners; + # ITK's compiled payload is ~5x build-simpleitk.yml's (which itself took ~15h here, wrapping + # SimpleITK only, not ITK's own Python layer) - a single 48h job is not expected to be enough, + # but self-hosted jobs aren't capped there like build-vtk.yml's GitHub-visible precedent is; + # there is no cross-job checkpoint mechanism in this repo to split it further, so this uses + # the real ceiling instead. + timeout-minutes: 7000 + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(needs.setup.outputs.matrix) }} + + env: + ITK_VERSION: ${{ matrix.version }} + + steps: + # Checked out to the workspace root (no `path:`) so it lands at /work itself once the + # whole workspace is mounted below - pyproject_configure.py's --output-dir defaults to + # /work, and `python -m build .` needs that pyproject.toml next to the source it is + # pointed at. + - name: Checkout ITKPythonPackage v${{ env.ITK_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: InsightSoftwareConsortium/ITKPythonPackage + ref: v${{ env.ITK_VERSION }} + persist-credentials: false + + - name: Checkout ITK v${{ env.ITK_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: InsightSoftwareConsortium/ITK + ref: v${{ env.ITK_VERSION }} + path: itk-src + submodules: false + persist-credentials: false + + # Same OOM guard build-vtk.yml needs for a comparably large build; ITK's wrap codegen + # (CastXML runs over every wrapped class for every pixel type/dimension combination) + # generates substantially more translation units than that build's, so this is sized up. + - name: Set swap space + uses: pierotofy/set-swap-space@fc79b3f67fa8a838184ce84a674ca12238d2c761 # master + with: + swap-size-gb: 20 + + - name: Write container script + run: | + mkdir -p build/wheelhouse + cat > riscv64-build-and-test.sh <<'EOF' + #!/bin/bash + set -e -x + + case "${PYTHON_TAG}" in + *t) python_prefix="/opt/python/${PYTHON_TAG%t}-${PYTHON_TAG}" ;; + *) python_prefix="/opt/python/${PYTHON_TAG}-${PYTHON_TAG}" ;; + esac + python_exe="${python_prefix}/bin/python" + + case "$1" in + build) + dnf install -y --setopt=install_weak_deps=False cmake ninja-build doxygen + + # ITK's vendored third-party trees declare cmake_minimum_required below 3.5, + # which the image's own cmake 4.x - ahead of /usr/bin on PATH - rejects + # (gotcha 358). Only this standalone ITK configure/build touches those trees; + # the per-wheel scikit-build-core carve below only installs already-built + # components and never reconfigures them, so it is left to pick up whichever + # cmake is already on PATH. + cmake=/usr/bin/cmake + python_include=$(find -L "${python_prefix}/include" -name Python.h -printf '%h\n') + + source_path=/work/itk-src + build_path=/work/itk-build + + # Module_ITKTBB/TBB_DIR are omitted: oneTBB 2021.9.0 has no verified riscv64 + # support and is itself another from-source ExternalProject dependency; ITK + # already falls back to its default Pool threader without it. + "${cmake}" -S "${source_path}" -B "${build_path}" -G Ninja \ + -DCMAKE_BUILD_TYPE:STRING=Release \ + -DBUILD_TESTING:BOOL=OFF \ + -DPython3_EXECUTABLE:FILEPATH="${python_exe}" \ + -DPython3_INCLUDE_DIR:PATH="${python_include}" \ + -DCMAKE_CXX_COMPILER_TARGET:STRING="$(uname -m)-linux-gnu" \ + -DCMAKE_CXX_FLAGS:STRING="-O3 -DNDEBUG" \ + -DCMAKE_C_FLAGS:STRING="-O3 -DNDEBUG" \ + -DITK_C_OPTIMIZATION_FLAGS:STRING= \ + -DITK_CXX_OPTIMIZATION_FLAGS:STRING= \ + -DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER:STRING=PythonWheel \ + -DWRAP_ITK_INSTALL_COMPONENT_PER_MODULE:BOOL=ON \ + -DITK_WRAP_unsigned_short:BOOL=ON \ + -DITK_WRAP_double:BOOL=ON \ + -DITK_WRAP_complex_double:BOOL=ON \ + -DITK_WRAP_IMAGE_DIMS:STRING="2;3;4" \ + -DPY_SITE_PACKAGES_PATH:PATH=. \ + -DITK_LEGACY_SILENT:BOOL=ON \ + -DITK_WRAP_PYTHON:BOOL=ON \ + -DITK_WRAP_DOC:BOOL=ON \ + -DModule_ITKTBB:BOOL=OFF \ + -DWITH_RVV:BOOL=OFF + "${cmake}" --build "${build_path}" --parallel "$(nproc)" + + # Pinned to upstream's own scripts/../requirements-dev.txt (v5.4.7) rather than + # latest, since ITKPythonPackage's CMakeLists.txt/pyproject templates are only + # tested against this scikit-build-core release. + "${python_exe}" -m pip install -U \ + ninja==1.11.1.1 scikit-build-core==0.10.7 build==1.2.1 \ + pyproject-metadata pathspec setuptools_scm==8.1.0 + + cd /work + mkdir -p /work/dist + while read -r wheel_name; do + [ -z "${wheel_name}" ] && continue + "${python_exe}" scripts/pyproject_configure.py "${wheel_name}" + "${python_exe}" -m build \ + --wheel \ + --outdir /work/dist \ + --no-isolation \ + --skip-dependency-check \ + --config-setting=cmake.define.ITK_SOURCE_DIR:PATH="${source_path}" \ + --config-setting=cmake.define.ITK_BINARY_DIR:PATH="${build_path}" \ + --config-setting=cmake.define.ITKPythonPackage_ITK_BINARY_REUSE:BOOL=ON \ + --config-setting=cmake.define.ITKPythonPackage_WHEEL_NAME:STRING="${wheel_name}" \ + --config-setting=cmake.define.Python3_EXECUTABLE:FILEPATH="${python_exe}" \ + --config-setting=cmake.define.Python3_INCLUDE_DIR:PATH="${python_include}" \ + --config-setting=cmake.define.CMAKE_CXX_FLAGS:STRING="-O3 -DNDEBUG" \ + --config-setting=cmake.define.CMAKE_C_FLAGS:STRING="-O3 -DNDEBUG" \ + . + done < scripts/WHEEL_NAMES.txt + + mkdir -p /work/build/wheelhouse + for whl in /work/dist/itk_*"$(uname -m)"*.whl; do + auditwheel show "${whl}" + auditwheel repair "${whl}" -w /work/build/wheelhouse + done + + # auditwheel skips the pure-Python "itk" metawheel (itk-meta in + # WHEEL_NAMES.txt) since it carries no .so; retag it manylinux_riscv64 by + # hand the same way upstream's own manylinux-build-wheels.sh does. + for whl in /work/dist/itk-*linux_*.whl; do + "${python_exe}" -m wheel unpack --dest /work/metawheel "${whl}" + new_tag=$(basename "${whl/linux/manylinux_2_39}" .whl) + sed -i "s/^Tag: .*/Tag: ${new_tag}/" /work/metawheel/itk-*/itk*.dist-info/WHEEL + "${python_exe}" -m wheel pack --dest /work/build/wheelhouse /work/metawheel/itk-* + done + ;; + test) + "${python_exe}" -m venv /tmp/venv + . /tmp/venv/bin/activate + pip install -U pip + pip install numpy + pip install itk --no-index -f /work/build/wheelhouse + + python -c "from itk import ITKCommon" + python -c "import itk; image = itk.Image[itk.UC, 2].New()" + python -c "import itkConfig; itkConfig.LazyLoading = False; import itk" + python /work/docs/code/test.py + ;; + *) + echo "usage: $0 " >&2 + exit 1 + ;; + esac + EOF + + - name: Build wheels + run: | + podman run -t \ + --log-driver=none \ + --network=host \ + -v "${PWD}":/work \ + --workdir /work \ + -e PYTHON_TAG="${{ matrix.python }}" \ + --pull=newer \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash /work/riscv64-build-and-test.sh build + + # One artifact per wheel: _publish-wheel.yml asserts exactly one normalized package + # name and version across whatever its own artifact-pattern matches (gotcha 380), so + # the 7 wheels this one build produces cannot share a single artifact. + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: itk_core-${{ env.ITK_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: build/wheelhouse/itk_core-*.whl + if-no-files-found: error + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: itk_numerics-${{ env.ITK_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: build/wheelhouse/itk_numerics-*.whl + if-no-files-found: error + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: itk_io-${{ env.ITK_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: build/wheelhouse/itk_io-*.whl + if-no-files-found: error + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: itk_filtering-${{ env.ITK_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: build/wheelhouse/itk_filtering-*.whl + if-no-files-found: error + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: itk_registration-${{ env.ITK_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: build/wheelhouse/itk_registration-*.whl + if-no-files-found: error + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: itk_segmentation-${{ env.ITK_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: build/wheelhouse/itk_segmentation-*.whl + if-no-files-found: error + + # The metawheel is "itk--...": anchored so it doesn't also match + # itk_core/itk_numerics/.../itk_segmentation above (gotcha 380's mlx precedent). + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: itk-${{ env.ITK_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: build/wheelhouse/itk-[0-9]*.whl + if-no-files-found: error + + - name: Test wheels + run: | + podman run -t \ + --log-driver=none \ + --network=host \ + -v "${PWD}":/work \ + --workdir /work \ + -e PYTHON_TAG="${{ matrix.python }}" \ + -e PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ \ + -e PIP_ONLY_BINARY=numpy \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash /work/riscv64-build-and-test.sh test + + publish_core: + name: Publish itk-core ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + matrix: ${{ needs.setup.outputs.matrix }} + artifact-pattern: itk_core-${{ matrix.version }}-*-manylinux_riscv64 + + publish_numerics: + name: Publish itk-numerics ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + matrix: ${{ needs.setup.outputs.matrix }} + artifact-pattern: itk_numerics-${{ matrix.version }}-*-manylinux_riscv64 + + publish_io: + name: Publish itk-io ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + matrix: ${{ needs.setup.outputs.matrix }} + artifact-pattern: itk_io-${{ matrix.version }}-*-manylinux_riscv64 + + publish_filtering: + name: Publish itk-filtering ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + matrix: ${{ needs.setup.outputs.matrix }} + artifact-pattern: itk_filtering-${{ matrix.version }}-*-manylinux_riscv64 + + publish_registration: + name: Publish itk-registration ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + matrix: ${{ needs.setup.outputs.matrix }} + artifact-pattern: itk_registration-${{ matrix.version }}-*-manylinux_riscv64 + + publish_segmentation: + name: Publish itk-segmentation ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + matrix: ${{ needs.setup.outputs.matrix }} + artifact-pattern: itk_segmentation-${{ matrix.version }}-*-manylinux_riscv64 + + publish_meta: + name: Publish itk ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + matrix: ${{ needs.setup.outputs.matrix }} + artifact-pattern: itk-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/itk-core.yaml b/docs/packages/itk-core.yaml new file mode 100644 index 00000000000..8ea59b22ef8 --- /dev/null +++ b/docs/packages/itk-core.yaml @@ -0,0 +1,9 @@ +package-name: itk-core +source-code: https://github.com/InsightSoftwareConsortium/ITK +license: Apache-2.0 +versions: +- version: 5.4.7 + files: + - python: 3.9:manylinux + - python: 3.10:manylinux + - python: 3.11-abi3:manylinux diff --git a/docs/packages/itk-filtering.yaml b/docs/packages/itk-filtering.yaml new file mode 100644 index 00000000000..e17c2aedeca --- /dev/null +++ b/docs/packages/itk-filtering.yaml @@ -0,0 +1,9 @@ +package-name: itk-filtering +source-code: https://github.com/InsightSoftwareConsortium/ITK +license: Apache-2.0 +versions: +- version: 5.4.7 + files: + - python: 3.9:manylinux + - python: 3.10:manylinux + - python: 3.11-abi3:manylinux diff --git a/docs/packages/itk-io.yaml b/docs/packages/itk-io.yaml new file mode 100644 index 00000000000..e807fb6a927 --- /dev/null +++ b/docs/packages/itk-io.yaml @@ -0,0 +1,9 @@ +package-name: itk-io +source-code: https://github.com/InsightSoftwareConsortium/ITK +license: Apache-2.0 +versions: +- version: 5.4.7 + files: + - python: 3.9:manylinux + - python: 3.10:manylinux + - python: 3.11-abi3:manylinux diff --git a/docs/packages/itk-numerics.yaml b/docs/packages/itk-numerics.yaml new file mode 100644 index 00000000000..4c282d1cf53 --- /dev/null +++ b/docs/packages/itk-numerics.yaml @@ -0,0 +1,9 @@ +package-name: itk-numerics +source-code: https://github.com/InsightSoftwareConsortium/ITK +license: Apache-2.0 +versions: +- version: 5.4.7 + files: + - python: 3.9:manylinux + - python: 3.10:manylinux + - python: 3.11-abi3:manylinux diff --git a/docs/packages/itk-registration.yaml b/docs/packages/itk-registration.yaml new file mode 100644 index 00000000000..4b400f08b9c --- /dev/null +++ b/docs/packages/itk-registration.yaml @@ -0,0 +1,9 @@ +package-name: itk-registration +source-code: https://github.com/InsightSoftwareConsortium/ITK +license: Apache-2.0 +versions: +- version: 5.4.7 + files: + - python: 3.9:manylinux + - python: 3.10:manylinux + - python: 3.11-abi3:manylinux diff --git a/docs/packages/itk-segmentation.yaml b/docs/packages/itk-segmentation.yaml new file mode 100644 index 00000000000..3ead00729cf --- /dev/null +++ b/docs/packages/itk-segmentation.yaml @@ -0,0 +1,9 @@ +package-name: itk-segmentation +source-code: https://github.com/InsightSoftwareConsortium/ITK +license: Apache-2.0 +versions: +- version: 5.4.7 + files: + - python: 3.9:manylinux + - python: 3.10:manylinux + - python: 3.11-abi3:manylinux diff --git a/docs/packages/itk.yaml b/docs/packages/itk.yaml new file mode 100644 index 00000000000..c0b09e1f473 --- /dev/null +++ b/docs/packages/itk.yaml @@ -0,0 +1,9 @@ +package-name: itk +source-code: https://github.com/InsightSoftwareConsortium/ITK +license: Apache-2.0 +versions: +- version: 5.4.7 + files: + - python: 3.9:manylinux + - python: 3.10:manylinux + - python: 3.11-abi3:manylinux From 60a39eb1490109f98e5525be825a1b90bb4e24a4 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 11 Nov 2026 00:35:02 +0000 Subject: [PATCH 2/5] itk: work around CastXML/Clang's riscv64 _Float128 gap This image's glibc declares _Float128 math functions unconditionally once it sees a Clang new enough, but the ExternalProject-built CastXML/Clang 18.1.3 toolchain doesn't recognize _Float128 as a type on riscv64, so parsing ITKCommonBase.cxx (via -> math.h) dies with 'unknown type name _Float128' before any ITK-specific wrapping happens. long double is already IEEE binary128 on riscv64, so -D_Float128=long double in CMAKE_CXX_FLAGS (forwarded to castxml's --castxml-cc-gnu invocation) resolves it exactly, touching only glibc's distinctly-named _Float128 declarations. --- .github/workflows/build-itk.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-itk.yml b/.github/workflows/build-itk.yml index a9d429996a2..2b7b5cfaf86 100644 --- a/.github/workflows/build-itk.yml +++ b/.github/workflows/build-itk.yml @@ -149,13 +149,22 @@ jobs: # Module_ITKTBB/TBB_DIR are omitted: oneTBB 2021.9.0 has no verified riscv64 # support and is itself another from-source ExternalProject dependency; ITK # already falls back to its default Pool threader without it. + # + # -D_Float128=long double (forwarded to the from-source-built CastXML/Clang + # 18.1.3 via Wrapping's --castxml-cc-gnu "${CMAKE_CXX_FLAGS}") works around + # that Clang not recognizing _Float128 as a type on riscv64, which this + # image's glibc declares unconditionally in bits/mathcalls.h once it sees a + # Clang new enough. long double is already IEEE binary128 on riscv64, so the + # substitution is exact, and it only touches glibc's distinctly-named + # _Float128 declarations (__atanf128 etc.), never the existing long-double + # overloads. "${cmake}" -S "${source_path}" -B "${build_path}" -G Ninja \ -DCMAKE_BUILD_TYPE:STRING=Release \ -DBUILD_TESTING:BOOL=OFF \ -DPython3_EXECUTABLE:FILEPATH="${python_exe}" \ -DPython3_INCLUDE_DIR:PATH="${python_include}" \ -DCMAKE_CXX_COMPILER_TARGET:STRING="$(uname -m)-linux-gnu" \ - -DCMAKE_CXX_FLAGS:STRING="-O3 -DNDEBUG" \ + -DCMAKE_CXX_FLAGS:STRING="-O3 -DNDEBUG -D_Float128=long double" \ -DCMAKE_C_FLAGS:STRING="-O3 -DNDEBUG" \ -DITK_C_OPTIMIZATION_FLAGS:STRING= \ -DITK_CXX_OPTIMIZATION_FLAGS:STRING= \ From 7289e243c58b8e8aaea0ddba822cb58719c15fd8 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 11 Nov 2026 02:35:07 +0000 Subject: [PATCH 3/5] itk: escape the space in -D_Float128=long double CMAKE_CXX_FLAGS is a flat string that CMake writes verbatim into the generated ninja build command, which ninja then hands to /bin/sh -c. The previous commit's -D_Float128=long double therefore split into two shell words at the unescaped space ("-D_Float128=long" and "double"), so every compile - including CMake's own compiler-ABI check - failed immediately with "double: linker input file not found", before CastXML/LLVM was ever reached. Escaping the space (long\ double) keeps it one shell word, giving the compiler the intended single macro definition. --- .github/workflows/build-itk.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-itk.yml b/.github/workflows/build-itk.yml index 2b7b5cfaf86..73948d9f9f2 100644 --- a/.github/workflows/build-itk.yml +++ b/.github/workflows/build-itk.yml @@ -164,7 +164,7 @@ jobs: -DPython3_EXECUTABLE:FILEPATH="${python_exe}" \ -DPython3_INCLUDE_DIR:PATH="${python_include}" \ -DCMAKE_CXX_COMPILER_TARGET:STRING="$(uname -m)-linux-gnu" \ - -DCMAKE_CXX_FLAGS:STRING="-O3 -DNDEBUG -D_Float128=long double" \ + -DCMAKE_CXX_FLAGS:STRING="-O3 -DNDEBUG -D_Float128=long\ double" \ -DCMAKE_C_FLAGS:STRING="-O3 -DNDEBUG" \ -DITK_C_OPTIMIZATION_FLAGS:STRING= \ -DITK_CXX_OPTIMIZATION_FLAGS:STRING= \ From 0e464ec95550396abfa572d6b75b73d9d97d9575 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 11 Nov 2026 06:36:35 +0000 Subject: [PATCH 4/5] itk: define _Float128 via -include header, not an escaped CLI space The previous commit's -D_Float128=long\ double still failed: the backslash-escaped space is meant to survive bash -> cmake -> ninja's build.ninja -> sh -c as a single shell word, but CMake re-emits CMAKE_CXX_FLAGS into the generated ninja rule without preserving the backslash, so sh -c re-splits it into "long" and "double" and the compiler rejects "double" as a bogus input file - exactly the same symptom as before, confirmed in the real cp311 CI log (c++: error: double: linker input file not found). Writing the #define into its own header and pulling it in with -include /work/float128-workaround.h avoids the whole quoting chain: a path has no embedded space to lose, so there is nothing left for any of those layers to re-split. --- .github/workflows/build-itk.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-itk.yml b/.github/workflows/build-itk.yml index 73948d9f9f2..152880fb87c 100644 --- a/.github/workflows/build-itk.yml +++ b/.github/workflows/build-itk.yml @@ -150,21 +150,33 @@ jobs: # support and is itself another from-source ExternalProject dependency; ITK # already falls back to its default Pool threader without it. # - # -D_Float128=long double (forwarded to the from-source-built CastXML/Clang + # _Float128 -> long double (forwarded to the from-source-built CastXML/Clang # 18.1.3 via Wrapping's --castxml-cc-gnu "${CMAKE_CXX_FLAGS}") works around # that Clang not recognizing _Float128 as a type on riscv64, which this # image's glibc declares unconditionally in bits/mathcalls.h once it sees a # Clang new enough. long double is already IEEE binary128 on riscv64, so the # substitution is exact, and it only touches glibc's distinctly-named # _Float128 declarations (__atanf128 etc.), never the existing long-double - # overloads. + # overloads. The #define is put in its own header and pulled in with -include + # rather than passed as -D_Float128=long\ double on the command line: that + # flag string crosses bash (this heredoc), cmake's -D parsing, cmake's own + # Ninja-rule serialization, and ninja's /bin/sh -c re-invocation, and the + # escaped space does not survive all four - CMake re-emits CMAKE_CXX_FLAGS + # into build.ninja without preserving the backslash, so sh -c re-splits it + # into two words and "double" gets passed to the compiler as a bogus input + # file. A path has no embedded space to lose, so -include sidesteps the + # whole chain. + cat > /work/float128-workaround.h <<'HDREOF' + #define _Float128 long double + HDREOF + "${cmake}" -S "${source_path}" -B "${build_path}" -G Ninja \ -DCMAKE_BUILD_TYPE:STRING=Release \ -DBUILD_TESTING:BOOL=OFF \ -DPython3_EXECUTABLE:FILEPATH="${python_exe}" \ -DPython3_INCLUDE_DIR:PATH="${python_include}" \ -DCMAKE_CXX_COMPILER_TARGET:STRING="$(uname -m)-linux-gnu" \ - -DCMAKE_CXX_FLAGS:STRING="-O3 -DNDEBUG -D_Float128=long\ double" \ + -DCMAKE_CXX_FLAGS:STRING="-O3 -DNDEBUG -include /work/float128-workaround.h" \ -DCMAKE_C_FLAGS:STRING="-O3 -DNDEBUG" \ -DITK_C_OPTIMIZATION_FLAGS:STRING= \ -DITK_CXX_OPTIMIZATION_FLAGS:STRING= \ From 05c7a6a7d73ab30e0584f2028ecf3dc177e8d0c6 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 11 Nov 2026 08:33:29 +0000 Subject: [PATCH 5/5] itk: scope the _Float128 workaround to Clang/CastXML only CMAKE_CXX_FLAGS is both forwarded to CastXML's --castxml-cc-gnu and used directly by the real GCC build of ITK's own C++ sources. Text-substituting _Float128 -> long double unconditionally collides with glibc/libstdc++'s own separate template specializations for the two types under GCC (redefinition of __iseqsig_type<...>, numeric_limits). Guard the #define to __clang__ so it only affects the CastXML forwarding it was written for. --- .github/workflows/build-itk.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-itk.yml b/.github/workflows/build-itk.yml index 152880fb87c..9600a921526 100644 --- a/.github/workflows/build-itk.yml +++ b/.github/workflows/build-itk.yml @@ -155,11 +155,17 @@ jobs: # that Clang not recognizing _Float128 as a type on riscv64, which this # image's glibc declares unconditionally in bits/mathcalls.h once it sees a # Clang new enough. long double is already IEEE binary128 on riscv64, so the - # substitution is exact, and it only touches glibc's distinctly-named - # _Float128 declarations (__atanf128 etc.), never the existing long-double - # overloads. The #define is put in its own header and pulled in with -include - # rather than passed as -D_Float128=long\ double on the command line: that - # flag string crosses bash (this heredoc), cmake's -D parsing, cmake's own + # substitution is exact for Clang's purposes. But CMAKE_CXX_FLAGS is also the + # literal flags CMake hands to the real GCC build of ITK's own C++ library + # code (not just the forwarded castxml-cc-gnu string) - and GCC's riscv64 + # glibc/libstdc++ headers carry separate template specializations keyed on + # _Float128 and on long double (e.g. __iseqsig_type, numeric_limits). Text- + # substituting one into the other under GCC collapses both specializations + # into one and fails with "redefinition of struct ...". Guarding the #define + # to Clang only keeps it scoped to the CastXML forwarding it was written for. + # The #define is put in its own header and pulled in with -include rather + # than passed as -D_Float128=long\ double on the command line: that flag + # string crosses bash (this heredoc), cmake's -D parsing, cmake's own # Ninja-rule serialization, and ninja's /bin/sh -c re-invocation, and the # escaped space does not survive all four - CMake re-emits CMAKE_CXX_FLAGS # into build.ninja without preserving the backslash, so sh -c re-splits it @@ -167,7 +173,9 @@ jobs: # file. A path has no embedded space to lose, so -include sidesteps the # whole chain. cat > /work/float128-workaround.h <<'HDREOF' + #if defined(__clang__) #define _Float128 long double + #endif HDREOF "${cmake}" -S "${source_path}" -B "${build_path}" -G Ninja \