From aa60223243ec572f53be57d459b75ebc218996cf Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 9 Nov 2026 23:39:36 +0000 Subject: [PATCH 1/7] 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/7] 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/7] 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/7] 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/7] 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 \ From 2cfeea1e09d063ac6248208156d2a4e72b1c524a Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 12 Nov 2026 17:38:34 +0000 Subject: [PATCH 6/7] itk: scope the _Float128 workaround to castxml-cc-gnu's own probe flags CastXML's own Detect.cxx (detectCC_GNU()) runs the exact compiler+flags array ITK's Wrapping/macro_files/itk_auto_load_submodules.cmake hands it via --castxml-cc-gnu through the real GNU compiler (never Clang) with "-E -dM -v " appended, to dump its predefined macros for castxml's own internal Clang parse of the real wrapping input files to use. That macro mirrors CMAKE_CXX_FLAGS wholesale (confirmed against the real itk_auto_load_submodules.cmake source at the pinned v5.4.7 tag: set(_castxml_cc_flags ${CMAKE_CXX_FLAGS})), so the prior commit's #ifdef __clang__ guard - added to keep the _Float128->long double substitution off GCC's real build of ITK's own C++ library sources, which also reads CMAKE_CXX_FLAGS directly - excluded it from this probe just as much as from that real build, since GCC never defines __clang__ either way. Confirmed from the real cp311 CI log (job 130740385856) that this reproduces the exact pre-workaround failure one leg over: the probe never captures the override, so castxml's internal Clang parses ITKCommonBase.cxx (and friends) with _Float128 still undefined, hitting "unknown type name '_Float128'" in bits/mathcalls.h/wchar.h/stdlib.h -- on a head commit well after the guard commit, ruling out a stale build. patches/itk/5.4.7/0001-castxml-scope-the-_Float128-workaround-to-the-cast.patch moves the -include out of the string GCC's real library build consumes (CMAKE_CXX_FLAGS itself, left alone) and into itk_auto_load_submodules.cmake's own _castxml_cc_flags, right where that macro builds them up, guarded on the workaround header actually existing. This reaches the castxml-cc-gnu probe (and, directly, castxml's own internal Clang invocation, if it ever uses this string on its own) without the real GCC build ever seeing it, so the #define no longer needs the __clang__ guard that defeated the probe - verified the patch applies cleanly against a fresh clone of the real v5.4.7 tag, not guessed. --- .github/workflows/build-itk.yml | 52 +++++++------- ...the-_Float128-workaround-to-the-cast.patch | 68 +++++++++++++++++++ 2 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 patches/itk/5.4.7/0001-castxml-scope-the-_Float128-workaround-to-the-cast.patch diff --git a/.github/workflows/build-itk.yml b/.github/workflows/build-itk.yml index 9600a921526..ea0308324ea 100644 --- a/.github/workflows/build-itk.yml +++ b/.github/workflows/build-itk.yml @@ -109,6 +109,16 @@ jobs: submodules: false persist-credentials: false + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + fetch-depth: 1 + persist-credentials: false + + - name: Patch ITK + run: git apply --directory=itk-src python-wheels/patches/itk/${{ env.ITK_VERSION }}/0001-*.patch + # 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. @@ -150,32 +160,28 @@ jobs: # support and is itself another from-source ExternalProject dependency; ITK # already falls back to its default Pool threader without it. # - # _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 + # _Float128 -> long double works around the from-source-built CastXML/Clang + # 18.1.3 toolchain 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 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 - # 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. + # substitution is exact for Clang's purposes. This used to be forwarded via + # CMAKE_CXX_FLAGS (the same string GCC's real build of ITK's own C++ library + # sources reads directly, whose riscv64 glibc/libstdc++ headers carry separate + # template specializations keyed on _Float128 and on long double - e.g. + # __iseqsig_type, numeric_limits - that the substitution collapses into one and + # breaks), guarded to #ifdef __clang__ to keep it off that GCC build. But + # Wrapping/macro_files/itk_auto_load_submodules.cmake's own castxml-cc-gnu + # probe copies that same CMAKE_CXX_FLAGS string and runs it through the real + # GNU compiler (CastXML's detectCC_GNU(), never Clang) to dump its predefined + # macros for its own internal Clang parse to use - so the __clang__ guard + # excluded it from the probe just as much as from the real build, reproducing + # the exact pre-workaround error one leg over. patches/itk/5.4.7's own patch + # moves this -include into castxml-cc-gnu's own flags specifically (right + # where itk_auto_load_submodules.cmake builds them up), so it reaches the + # probe without ever reaching GCC's real library build - the #define no longer + # needs the __clang__ guard, since CMAKE_CXX_FLAGS itself no longer carries it. cat > /work/float128-workaround.h <<'HDREOF' - #if defined(__clang__) #define _Float128 long double - #endif HDREOF "${cmake}" -S "${source_path}" -B "${build_path}" -G Ninja \ @@ -184,7 +190,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 -include /work/float128-workaround.h" \ + -DCMAKE_CXX_FLAGS:STRING="-O3 -DNDEBUG" \ -DCMAKE_C_FLAGS:STRING="-O3 -DNDEBUG" \ -DITK_C_OPTIMIZATION_FLAGS:STRING= \ -DITK_CXX_OPTIMIZATION_FLAGS:STRING= \ diff --git a/patches/itk/5.4.7/0001-castxml-scope-the-_Float128-workaround-to-the-cast.patch b/patches/itk/5.4.7/0001-castxml-scope-the-_Float128-workaround-to-the-cast.patch new file mode 100644 index 00000000000..2f3239744f3 --- /dev/null +++ b/patches/itk/5.4.7/0001-castxml-scope-the-_Float128-workaround-to-the-cast.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Mon, 2 Nov 2026 00:00:00 +0000 +Subject: [PATCH] castxml: scope the _Float128 workaround to the castxml-cc-gnu + probe, not CMAKE_CXX_FLAGS + +Upstream-Status: Inappropriate [riscv64 CastXML/Clang 18.1.3 toolchain gap; not relevant to other platforms] + +build-itk.yml forces `#define _Float128 long double` via `-include` into the +global CMAKE_CXX_FLAGS, guarded to `#ifdef __clang__` so it only affects the +CastXML/Clang toolchain this -include was written for, not GCC's real build +of ITK's own C++ library sources (which also reads CMAKE_CXX_FLAGS directly +and collides with glibc/libstdc++'s own distinct template specializations +for _Float128 vs long double under GCC). + +That guard broke the castxml-cc-gnu probe this same flags string also feeds: +this macro's own `set(_castxml_cc_flags ${CMAKE_CXX_FLAGS})` forwards it +into `--castxml-cc-gnu ("${CMAKE_CXX_COMPILER}" ${_castxml_cc_flags})`, and +CastXML's own detectCC_GNU() (src/Detect.cxx) runs exactly that compiler and +flags array - appending only `-E -dM -v ` - through the REAL +GNU compiler to dump its predefined macros, never through Clang. GCC never +defines __clang__, so the probe's macro dump never captures the _Float128 +override, and castxml's own internal Clang then parses the real wrapping +input files (confirmed on ITKCommonBase.cxx) using that macro set, where +_Float128 is still undefined - reproducing the exact pre-workaround error +("unknown type name '_Float128'" in bits/mathcalls.h) a later commit on +this same branch already fixed once, this time via the fix meant to guard +against the GCC collision instead. + +Move the -include out of the string GCC's real library build consumes +(CMAKE_CXX_FLAGS, left alone in the workflow) and into castxml's own +_castxml_cc_flags specifically, right where it is built up, guarded on the +workaround header file actually existing (so an unrelated platform/build +calling this same macro is unaffected either way). This reaches both the +GCC-run probe (whose dumped macros castxml's own internal Clang then +parses the real file with) and, if castxml ever runs its internal Clang +directly against this flags string instead, that path too - without ever +reaching the real GCC compile of ITK's own .cxx sources, so the header's +#define no longer needs the __clang__ guard that defeated the probe. +--- + Wrapping/macro_files/itk_auto_load_submodules.cmake | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/Wrapping/macro_files/itk_auto_load_submodules.cmake b/Wrapping/macro_files/itk_auto_load_submodules.cmake +index 0bb618f..4427bc1 100644 +--- a/Wrapping/macro_files/itk_auto_load_submodules.cmake ++++ b/Wrapping/macro_files/itk_auto_load_submodules.cmake +@@ -19,6 +19,18 @@ function(generate_castxml_commandline_flags) + + # Avoid missing omp.h include + set(_castxml_cc_flags ${CMAKE_CXX_FLAGS}) ++ # riscv64: this image's glibc declares _Float128 math functions unconditionally once ++ # it sees a Clang new enough, but the CastXML/Clang 18.1.3 toolchain this castxml-cc-gnu ++ # probe (and castxml's own internal parse, which configures itself from this probe's ++ # output) ends up invoking does not recognize _Float128 as a type on riscv64. Force- ++ # including a long-double substitute here, rather than into CMAKE_CXX_FLAGS itself, ++ # keeps it out of GCC's real build of ITK's own C++ library sources, which reads ++ # CMAKE_CXX_FLAGS directly and whose riscv64 glibc/libstdc++ headers carry separate ++ # template specializations keyed on _Float128 and on long double (e.g. __iseqsig_type, ++ # numeric_limits) that the same substitution would collapse into one and break. ++ if(EXISTS "/work/float128-workaround.h") ++ set(_castxml_cc_flags "${_castxml_cc_flags} -include /work/float128-workaround.h") ++ endif() + if(CMAKE_CXX_EXTENSIONS) + set(_castxml_cc_flags "${_castxml_cc_flags} ${CMAKE_CXX${CMAKE_CXX_STANDARD}_EXTENSION_COMPILE_OPTION}") + else() +-- +2.43.0 From 6e919fcc5babae71703b1ee8b0100cdf0b7852cd Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 13 Nov 2026 16:05:24 +0000 Subject: [PATCH 7/7] itk: restore __clang__ for the castxml-cc-gnu probe CastXML's --castxml-cc-gnu mode (src/RunClang.cxx's detectCC_GNU()) passes -undef to its own internal Clang and splices in the real GCC probe's captured predefines as the complete builtin set, so that internal Clang parses every wrapping input file with __clang__ UNDEFINED - indistinguishable from real GCC to any !defined(__clang__) guard, even though Clang is the one actually doing the parse. This image's GCC 14 libstdc++ (bits/stl_bvector.h) guards its own __attribute__((__assume__(expr))) usage - an arbitrary-expression GNU attribute no Clang release has ever implemented under that spelling, confirmed against the real clang/include/clang/Basic/Attr.td at every tag from llvmorg-18.1.0 through llvmorg-23.1.0 - with exactly "#if __has_attribute(__assume__) && !defined(__clang__)", written by libstdc++'s own authors specifically to protect Clang from this false positive (Clang's __has_attribute(__assume__) recognizes the attribute name but not GCC's semantics for it). Confirmed from the real cp310 CI log (job 131369644553) that this is exactly what's hit: "error: expected string literal as argument of '__assume__' attribute" parsing stl_bvector.h. Add #define __clang__ 1 to the existing forced-include workaround header (already reached via itk_auto_load_submodules.cmake's own _castxml_cc_flags, patches/itk/5.4.7, never CMAKE_CXX_FLAGS itself). This isn't a suppression hack - it restores the parser's true identity at the exact point CastXML's substitution logic erases it, so this and every other #ifdef __clang__ / __has_attribute guard elsewhere in the headers sees the real state again rather than a false GCC identity. This resolves only that one parse failure. The same CI log shows two further, unrelated failures this commit does NOT address: - _Float128 -> long double (already forced via this same header) collides with a SEPARATE glibc math.h specialization (__iseqsig_type<_Float128>, gated by __HAVE_FLOAT128_UNLIKE_LDBL/__GNUC_PREREQ - plain GCC-version macros, true under the spliced GCC predefines regardless of __clang__, so this commit doesn't touch it) that only exists because genuine GCC keeps _Float128 and long double as distinct types sharing one representation; the blanket textual substitution collapses that distinction and the specializations collide. - _Float32 has no numeric_limits/literal-suffix/builtin support in Clang 18.1.3 on riscv64 at all: clang/lib/Basic/Targets/RISCV.h never sets Float128/Float32 target capabilities, so this is a missing Clang target feature, not a macro-guard problem, with no flag found to enable it. Both remain open. --- .github/workflows/build-itk.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/build-itk.yml b/.github/workflows/build-itk.yml index ea0308324ea..3fd15bf295c 100644 --- a/.github/workflows/build-itk.yml +++ b/.github/workflows/build-itk.yml @@ -180,8 +180,36 @@ jobs: # where itk_auto_load_submodules.cmake builds them up), so it reaches the # probe without ever reaching GCC's real library build - the #define no longer # needs the __clang__ guard, since CMAKE_CXX_FLAGS itself no longer carries it. + # + # __clang__ 1: CastXML's --castxml-cc-gnu mode (src/RunClang.cxx's + # detectCC_GNU()) passes -undef to its own internal Clang and splices in the + # real GCC probe's captured predefines as the complete builtin set, so that + # internal Clang parses every header with __clang__ undefined - indistinguishable + # from real GCC to any !defined(__clang__) guard, even though Clang is the one + # actually parsing. This image's GCC 14 libstdc++ (bits/stl_bvector.h) guards + # __attribute__((__assume__(expr))) - an arbitrary-expression GNU attribute no + # Clang release has ever implemented under that spelling - with exactly + # "#if __has_attribute(__assume__) && !defined(__clang__)", written by + # libstdc++'s own authors to protect Clang from this exact false-positive + # (Clang's __has_attribute(__assume__) recognizes the name but not GCC's + # semantics for it). Restoring __clang__ here is not a suppression hack: it + # corrects the compiler identity CastXML's own substitution logic erases, so + # this and every other #ifdef __clang__/__has_attribute guard in the headers + # sees the parser's true identity again. + # + # This does NOT make the build succeed on its own. _Float128 -> long double + # (above) still collides with a SEPARATE specialization glibc's math.h keeps + # for genuine Float128 support (__iseqsig_type<_Float128>, gated by + # __HAVE_FLOAT128_UNLIKE_LDBL/__GNUC_PREREQ - plain GCC-version macros, true + # under the spliced GCC predefines regardless of __clang__, so this fix doesn't + # touch it), and _Float32's own numeric_limits/literal-suffix/builtin support is + # simply absent from Clang 18.1.3's riscv64 TargetInfo (clang/lib/Basic/Targets/ + # RISCV.h never sets HasFloat128/_Float32 support) - a missing target capability, + # not a macro-guard problem, with no flag to enable it. Both remain open; this + # commit resolves only the stl_bvector.h parse failure. cat > /work/float128-workaround.h <<'HDREOF' #define _Float128 long double + #define __clang__ 1 HDREOF "${cmake}" -S "${source_path}" -B "${build_path}" -G Ninja \