From e8c7318bf28f949465a36d4830096de431572d47 Mon Sep 17 00:00:00 2001 From: Cavan Riley Date: Thu, 16 Apr 2026 15:37:52 -0500 Subject: [PATCH 01/22] ENH: Add Utilities/conda-packages/ rattler-build recipe Produces three conda packages from a single ITK build: - libitk: runtime shared libraries - libitk-devel: headers + CMake config (depends on libitk) - libitk-wrapping: SWIG Python wrapping artifacts Uses rattler-build staging: build-cache so the build runs once and the three outputs each run their own install script. ITK is built against conda-forge system libs via ITK_USE_SYSTEM_* for EXPAT, FFTW, HDF5, JPEG, PNG, TIFF, ZLIB, EIGEN, DOUBLECONVERSION, and CASTXML. Full context and opt-in features (ccache, compile flags) in Utilities/conda-packages/{README,README_Advanced,AUDIT}.md. Co-Authored-By: Hans Johnson --- Utilities/conda-packages/AUDIT.md | 329 ++++++++++++++++++ Utilities/conda-packages/README.md | 223 ++++++++++++ Utilities/conda-packages/README_Advanced.md | 217 ++++++++++++ Utilities/conda-packages/build.bat | 52 +++ Utilities/conda-packages/build.sh | 116 ++++++ .../cross-compile/TryRunResults.cmake | 129 +++++++ .../conda-packages/libitk-devel_install.bat | 6 + .../conda-packages/libitk-devel_install.sh | 5 + .../libitk-wrapping_install.bat | 9 + .../conda-packages/libitk-wrapping_install.sh | 9 + Utilities/conda-packages/libitk_install.bat | 7 + Utilities/conda-packages/libitk_install.sh | 26 ++ Utilities/conda-packages/recipe.yaml | 165 +++++++++ .../tests/example/CMakeLists.txt | 8 + .../conda-packages/tests/example/main.cxx | 29 ++ .../conda-packages/tests/test_wrapping.py | 11 + pixi.lock | 143 +++++++- pyproject.toml | 2 + 18 files changed, 1480 insertions(+), 6 deletions(-) create mode 100644 Utilities/conda-packages/AUDIT.md create mode 100644 Utilities/conda-packages/README.md create mode 100644 Utilities/conda-packages/README_Advanced.md create mode 100755 Utilities/conda-packages/build.bat create mode 100755 Utilities/conda-packages/build.sh create mode 100644 Utilities/conda-packages/cross-compile/TryRunResults.cmake create mode 100755 Utilities/conda-packages/libitk-devel_install.bat create mode 100755 Utilities/conda-packages/libitk-devel_install.sh create mode 100755 Utilities/conda-packages/libitk-wrapping_install.bat create mode 100755 Utilities/conda-packages/libitk-wrapping_install.sh create mode 100755 Utilities/conda-packages/libitk_install.bat create mode 100755 Utilities/conda-packages/libitk_install.sh create mode 100644 Utilities/conda-packages/recipe.yaml create mode 100644 Utilities/conda-packages/tests/example/CMakeLists.txt create mode 100644 Utilities/conda-packages/tests/example/main.cxx create mode 100644 Utilities/conda-packages/tests/test_wrapping.py diff --git a/Utilities/conda-packages/AUDIT.md b/Utilities/conda-packages/AUDIT.md new file mode 100644 index 00000000000..dbd42d6cd43 --- /dev/null +++ b/Utilities/conda-packages/AUDIT.md @@ -0,0 +1,329 @@ +# Branch Audit: `add-conda-recipe` + +## What This Branch Does + +Adds `Utilities/conda-packages/` to the ITK main repo with a rattler-build `recipe.yaml` +that produces three conda packages from a single ITK build: `libitk` (runtime +libraries), `libitk-devel` (headers + CMake config), and `libitk-wrapping` +(Python SWIG artifacts). Also contains five CMake bug fixes required to make +ITK build correctly against system (conda-forge) libraries. + +Full context: `Utilities/conda-packages/README.md`. + +--- + +## Bug Fixes (5 commits) + +Each fix targets a failure mode specific to `ITK_USE_SYSTEM_*=ON` builds. +None affect the bundled-library path. + +### `cdc0eaaeb7` — HDF5 namespace mismatch +**File:** `Modules/ThirdParty/HDF5/CMakeLists.txt` + +When `ITK_USE_SYSTEM_HDF5=ON`, the bundled HDF5 source (`src/CMakeLists.txt`) +is skipped (`ITKHDF5_NO_SRC=1`), so `itk_module_target()` never runs. That +function is what normally creates `ITK::hdf5-shared` etc. as alias targets. +Meanwhile, `itk_module_impl()` prepends `ITK_LIBRARY_NAMESPACE::` to all +dependency names, so it looks for `ITK::hdf5-shared` — which doesn't exist. + +**Fix:** Create `ITK::hdf5-*` INTERFACE IMPORTED wrapper targets around the +bare system HDF5 targets when `ITK_USE_SYSTEM_HDF5=ON`. + +**Verify:** Build with `-DITK_USE_SYSTEM_HDF5=ON` and confirm no "target not +found" CMake errors for `ITK::hdf5-shared`. + +--- + +### `db09bd1371` — Eigen3 include dirs missing for COMPILE_DEPENDS modules +**File:** `Modules/ThirdParty/Eigen3/CMakeLists.txt` + +Modules that declare only `COMPILE_DEPENDS ITKEigen3` (e.g. +`ITKDiffusionTensorImage`) are not linked through the target chain, so they +don't receive transitive `INTERFACE_INCLUDE_DIRECTORIES` from `Eigen3::Eigen`. +They rely on `include_directories(${ITKEigen3_INCLUDE_DIRS})` called by +`_itk_module_use_recurse()`. A prior upstream commit dropped the +`get_target_property()` that populated this variable. + +**Fix:** Restore the `get_target_property()` call that extracts +`INTERFACE_INCLUDE_DIRECTORIES` from `Eigen3::Eigen` into `ITKEigen3_INCLUDE_DIRS`. + +**Verify:** Build with `-DITK_USE_SYSTEM_EIGEN=ON`; confirm no "Eigen/Eigenvalues +not found" compile errors in modules that only have COMPILE_DEPENDS on ITKEigen3. + +--- + +### `63a9c6ba41` — Missing ITKZLIB dependency in ITKReview +**File:** `Modules/Nonunit/Review/itk-module.cmake` + +`itkVoxBoCUBImageIO.cxx` calls `gzopen`, `gzclose`, `gzread`, `gzwrite` +directly via `itk_zlib.h`. ITKReview had no explicit dependency on ITKZLIB. +This was masked on Linux by lazy dynamic linking but produced a link failure +on macOS ARM64 (which requires all symbols resolved at link time). + +**Fix:** Add `ITKZLIB` to `PRIVATE_DEPENDS` in ITKReview's `itk-module.cmake`. + +**Verify:** Build on macOS ARM64 with `Module_ITKReview=ON`; confirm no +undefined symbol errors for `gzopen` etc. + +--- + +### `090106d316` — Missing `if(NOT TARGET)` guard in EXPORT_CODE_INSTALL shims +**File:** `CMake/ITKModuleMacros.cmake` + +The BUILD backward-compatibility shims (created at ITK build time for +downstream projects building against the build tree) already had an +`if(NOT TARGET ${dep})` guard. The EXPORT_CODE_INSTALL shims (embedded in +the installed `ITKConfig.cmake` for installed-package consumers) did not. +When a downstream project calls `find_package(HDF5)` before +`find_package(ITK)`, the bare HDF5 targets already exist and the shim +`add_library()` call fails with "target already exists". + +**Fix:** Add the same `if(NOT TARGET)` guard to the EXPORT_CODE_INSTALL shims. + +**Verify:** In a downstream CMake project, call `find_package(HDF5)` then +`find_package(ITK)` and confirm no "cannot create imported target" error. + +--- + +### `a8d7a90430` — ITKConfig.cmake ordering + HDF5 EXPORT_CODE in installed config +**Files:** `CMake/ITKConfig.cmake.in`, `Modules/ThirdParty/HDF5/CMakeLists.txt` + +**Root cause of libitk-devel test failure.** + +`ITKTargets.cmake` sets `INTERFACE_LINK_LIBRARIES` on `ITK::ITKHDF5Module` to +`ITK::hdf5-shared` etc. CMake policy CMP0028 (NEW) requires all `::` targets +to exist at the time `set_target_properties()` is called (i.e. at +`include(ITKTargets.cmake)` time). The `ITK::hdf5-*` wrappers are created +inside module EXPORT_CODE, which runs during `itk_module_config()`. + +**Fix part 1 (`ITKConfig.cmake.in`):** Reorder so `itk_module_config()` runs +before `include(ITKTargets.cmake)`. + +**Fix part 2 (`HDF5/CMakeLists.txt`):** The build-time fix (`cdc0eaaeb7`) +creates wrappers during the ITK build. The installed config also needs them. +Extend `ITKHDF5_EXPORT_CODE_INSTALL` (the code embedded in the installed +`ITKConfig.cmake`) to also create `ITK::hdf5-*` INTERFACE IMPORTED wrappers +in downstream CMake sessions. + +**Verify:** Install libitk-devel, then run the `Utilities/conda-packages/tests/example/` +CMake build test: `cmake -DCMAKE_PREFIX_PATH=$PREFIX ../tests/example && +cmake --build .` — must complete without errors. + +--- + +## Recipe Implementation + +### Structure +Single rattler-build `recipe.yaml` with a `staging:` build cache (compiles ITK +once) and three `outputs:` that run separate install scripts against the shared +build tree. See `Utilities/conda-packages/README.md` for the full package split and +install component mapping. + +### Known gaps vs conda-forge feedstock (non-blocking) +These are in the feedstock but not yet in this recipe. They should be +investigated before or after upstreaming: + +| Item | Notes | +|------|-------| +| `CMAKE_FIND_ROOT_PATH_MODE_*` flags | Feedstock constrains all CMake finds to conda PREFIX. We use `CMAKE_PREFIX_PATH` instead. May matter for cross-compilation edge cases. | +| `GDCM_USE_COREFOUNDATION_LIBRARY:BOOL=OFF` | macOS-specific GDCM fix; unclear if still needed in 6.0.0. | +| ~~`NIFTI_SYSTEM_MATH_LIB=`~~ | **Implemented in `build.sh`.** `find_library(… m)` in `Modules/ThirdParty/NIFTI/src/nifti/CMakeLists.txt` was baking the rattler-build sandbox sysroot path into exported `ITKTargets.cmake`, causing downstream consumers (e.g. libitk-devel test) to fail with `ninja: error: '…/build_env/…/sysroot/usr/lib/libm.so' missing`. | +| `Module_SimpleITKFilters` | Feedstock enables this; requires SimpleITK available at configure time. Not investigated for 6.0.0 compatibility. | + +### Modules intentionally omitted +`Module_ITKIOTransformMINC` — `EXCLUDE_FROM_DEFAULT`, needs `libminc` host dep +not in recipe; caused missing-dylib error in libitk-devel test. Removed. + +`Module_GenericLabelInterpolator`, `Module_AdaptiveDenoising` — remote modules +using `itk_fetch_module()` (git clone at configure time); incompatible with +`-DITK_FORBID_DOWNLOADS=ON`. Removed. + +--- + +## Open Investigation: "Overdepending" warnings on system libraries + +`rattler-build build` emits warnings of the form: + +``` +⚠ warning Overdepending against libzlib +⚠ warning Overdepending against libexpat +⚠ warning Overdepending against libtiff +⚠ warning Overdepending against tbb +⚠ warning Overdepending against libpng +⚠ warning Overdepending against libjpeg-turbo +⚠ warning Overdepending against hdf5 +⚠ warning Overdepending against fftw +``` + +Per rattler-build's definition, "overdepending" means the package declares a +run-dep that no binary inside the package actually links against +(no `NEEDED` entry in any ELF scan). + +**Partial fix applied:** `libitk_install.sh` and `libitk_install.bat` now +include the `Applications` component in the install loop, which was the only +runtime-flavored component name found in the ITK source that the previous +loop missed (covers bundled OpenJPEG and DoubleConversion executables). + +**What needs confirming:** whether the ITKIO* module shared libraries +(`libITKIOTIFF-6.0.so`, `libITKIOPNG-6.0.so`, `libITKIOJPEG-6.0.so`, +`libITKIOHDF5-6.0.so`, `libITKIOMeta-6.0.so`, `libITKFFT-6.0.so`) are +present in the packaged `libitk-*.conda` archive and have the expected +`NEEDED` entries. Per `CMake/ITKModuleMacros.cmake:823`, ITK modules +install their shared libraries under the `RuntimeLibraries` component, +which is already in the loop — so either (a) the install is correct and +rattler-build's overdepending check has a false positive, or (b) some +modules aren't being built or captured. + +**How to investigate:** +```bash +# After a build that preserved the output: +unzip -l output//*/libitk-6.0.0-*.conda | grep ITKIO +# For any that are present, confirm they link against system libs: +cd $(mktemp -d) && unzip /path/to/libitk-6.0.0-*.conda +tar -xf pkg-libitk-*.tar.zst +find lib -name 'libITKIO*.so*' -exec sh -c \ + 'echo "=== $1 ==="; readelf -d "$1" | grep NEEDED' _ {} \; +``` + +Tracked as a separate follow-up; not blocking initial recipe upstreaming. + +--- + +## Suggested ITK CMake Modernizations to Simplify Conda Packaging + +These are forward-looking suggestions for reducing the complexity of the +install scripts in `Utilities/conda-packages/`. None are blocking for the initial +recipe upstreaming; they are potential follow-up patches for the ITK +repository itself. Ordered by expected payoff vs. effort. + +### 1. Consolidate install component names (highest payoff) + +ITK currently uses nine distinct component names across the tree: + +``` +Runtime, RuntimeLibraries, Libraries, libraries (lowercase!), +Unspecified, Applications, Development, Headers, +PythonWrappingRuntimeLibraries, PythonWheelRuntimeLibraries +``` + +This is why `libitk_install.sh` is a component loop rather than a one-line +`cmake --install --component Runtime` — bundled third-party CMakeLists +(MINC uses `libraries` lowercase, OpenJPEG uses `Applications`/`Libraries`/ +`Headers`) do not agree with ITK core (`RuntimeLibraries`/`Runtime`/ +`Development`). + +**Proposal:** normalize to three components across the entire tree +(including vendored third-party), matching what conda-forge feedstocks for +VTK and OpenCV use: + +| Component | Goes to conda package | Contents | +|---|---|---| +| `Runtime` | `libitk` | shared libs + executables | +| `Development` | `libitk-devel` | headers, static libs, CMake config | +| `PythonWrapping` | `libitk-wrapping` | SWIG `.py`, compiled `_*.so` modules | + +With that, each conda install script collapses to: + +```bash +cmake --install "${BUILD_DIR}" --component Runtime # libitk +cmake --install "${BUILD_DIR}" --component Development # libitk-devel +cmake --install "${BUILD_DIR}" --component PythonWrapping # libitk-wrapping +``` + +**Implementation notes:** changes to vendored third-party CMakeLists need +to be applied as local patches (the originals may change on re-import). +One approach is to set `CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Runtime` +before `add_subdirectory()` on each bundled dep, overriding bare +`install(COMPONENT ...)` calls. An alternative is to add a CMake include +that post-processes install rules at configure time. + +**Trade-offs:** this is a ~weeks-of-review-discussion change touching many +files. High payoff for conda packaging; low payoff for other distribution +methods (CPack DEB/RPM users would need to adapt their spec files). + +### 2. Switch to `cmake --install --component X` + +**Current (legacy, CMake < 3.15):** +```bash +cmake -DCOMPONENT=RuntimeLibraries -P "${BUILD_DIR}/cmake_install.cmake" +``` + +**Modern (CMake 3.15+, which ITK 6.0 already requires via its 3.16.3 floor):** +```bash +cmake --install "${BUILD_DIR}" --component RuntimeLibraries +``` + +Functionally identical, more idiomatic, plays better with `--verbose` and +`--dry-run`. Trivial cleanup; could be applied to +`libitk_install.sh`, `libitk-devel_install.sh`, and `libitk-wrapping_install.sh` +in a single commit. Not ITK-side — this is a `Utilities/conda-packages/` cleanup. + +### 3. Make `FILE_SET HEADERS` adoption unconditional + +`CMake/ITKModuleMacros.cmake:793-812` conditionally uses +`target_sources(TARGET PUBLIC FILE_SET HEADERS ...)` only for CMake >= 3.23: + +```cmake +if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.23") + ... +endif() +``` + +Bumping the minimum CMake to 3.23 (released 2022-03) would: +- Remove the conditional fork in `ITKModuleMacros.cmake` +- Let `install(TARGETS … FILE_SET HEADERS …)` replace most per-module + `install(FILES … DESTINATION … COMPONENT Development)` calls +- Consolidate header install logic into the module macro + +**Trade-offs:** platforms still on CMake 3.16.3 (some LTS distros) would +need to install a newer CMake. The Kitware apt repository and pypi +`cmake` package cover this on affected platforms. + +### 4. What NOT to adopt for conda-compatible builds + +**`install(RUNTIME_DEPENDENCY_SET)` (CMake 3.21+)** — CMake's mechanism +for copying transitive runtime deps into the install tree. Useful for +standalone installers, **counterproductive for conda** because conda wants +its own packaged copies of the deps, not duplicates bundled by the build. + +**`install(IMPORTED_RUNTIME_ARTIFACTS)` (CMake 3.21+)** — same category; +duplicates deps conda already tracks. + +**`CMAKE_INSTALL_DO_STRIP` / `cmake --install --strip`** — conda-build and +rattler-build already strip binaries post-install. No-op. + +### 5. What CMake fundamentally cannot help with + +- **Generating `recipe.yaml`** — no CPack Conda generator exists upstream. + Writing one would duplicate rattler-build's metadata schema, which is + still evolving. Not worth the maintenance cost. +- **Conda activation / deactivation scripts** (`$PREFIX/etc/conda/activate.d/`). + A conda concept, not a CMake concept. +- **RPATH rewriting between build-prefix and install-prefix** — handled + by `patchelf` (Linux), `install_name_tool` (macOS), binary string + rewrites (Windows) inside conda-build / rattler-build. CMake + configures the initial RPATH via `CMAKE_INSTALL_RPATH` and + `CMAKE_BUILD_WITH_INSTALL_RPATH` (already set correctly by ITK). +- **`run_exports`, `pin_subpackage()`, dep version constraints** — pure + conda recipe metadata; lives in `recipe.yaml`, outside CMake's scope. + +### Summary + +| Suggestion | Effort | Payoff | Where the change lives | +|---|---|---|---| +| #1 consolidate components | high | high (one-line install scripts) | ITK repo, many files | +| #2 `cmake --install --component X` | trivial | low (cosmetic) | `Utilities/conda-packages/` | +| #3 unconditional `FILE_SET HEADERS` | low | medium | ITK repo, ~1 file | +| #4 avoid `RUNTIME_DEPENDENCY_SET` | n/a | n/a (don't do it) | — | + +--- + +## Checklist for Reviewer + +- [ ] All 5 BUG commits touch only CMake infrastructure files (no recipe files) +- [ ] libitk-devel CMake test passes: `cmake -DCMAKE_PREFIX_PATH=$PREFIX ../tests/example` +- [ ] `rattler-build build --render-only` parses all three outputs without error +- [ ] `libitk` package contains `lib/ITK-6.0/*.dylib` (or `.so`/`.dll`), no headers +- [ ] `libitk-devel` package contains `include/ITK-6.0/` and `lib/cmake/ITK-6.0/` +- [ ] `libitk-wrapping` package: `python -c "import itk"` succeeds +- [ ] No `itk-build/` or other local artifacts accidentally staged in any commit diff --git a/Utilities/conda-packages/README.md b/Utilities/conda-packages/README.md new file mode 100644 index 00000000000..aea4b5c00fa --- /dev/null +++ b/Utilities/conda-packages/README.md @@ -0,0 +1,223 @@ +# ITK Conda Packages Reference + +This document provides context for working on the `Utilities/conda-packages/` directory, +which contains a rattler-build recipe producing three conda packages from a +single ITK build: `libitk`, `libitk-devel`, and `libitk-wrapping`. + +## Why This Exists + +The conda-forge `libitk-feedstock` (external repo, ITK 5.4.5) maintains +`libitk` and `libitk-devel` as separate packages from the main ITK repo. +This causes recipes to lag releases and requires external maintainer +coordination. The `libitk-wrapping` package (new — not on conda-forge) bundles +compiled SWIG artifacts so that downstream remote module CI can skip ITK +recompilation, cutting build times from ~1–2 hours to ~15 minutes. + +Consensus from InsightSoftwareConsortium/ITKPythonPackage#302: move recipe infrastructure to the main ITK repo so +recipes stay in sync with releases automatically. + +## Package Split + +| Package | Contents | Install components | +|---------|----------|--------------------| +| `libitk` | Shared libraries (`.so`/`.dylib`/`.dll`) | `Runtime`, `RuntimeLibraries`, `Libraries`, `Unspecified`, `libraries` | +| `libitk-devel` | Headers, CMake config (`ITKConfig.cmake`, `ITKTargets.cmake`), static libs | `Development`, `Headers` | +| `libitk-wrapping` | Python extension modules (`.so`), `itk` Python package files | `PythonWrappingRuntimeLibraries` | + +The wrapping component name comes from the CMake flag +`-DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER:STRING=PythonWrapping`, which causes +all wrapping install rules to use component `PythonWrappingRuntimeLibraries` +(pattern: `${WRAP_ITK_INSTALL_COMPONENT_IDENTIFIER}RuntimeLibraries`). + +## Recipe Format: rattler-build vs conda-build + +The feedstock uses `meta.yaml` (conda-build format). This repo uses +`recipe.yaml` (rattler-build format). Key differences: + +| Feature | conda-build (feedstock) | rattler-build (this repo) | +|---------|------------------------|--------------------------| +| Format | `meta.yaml` | `recipe.yaml` | +| Build cache | Rebuilds ITK 3× (once per output) | Single build via `staging:` + `inherit:` | +| Selectors | `# [condition]` inline comments | `if/then/else` blocks | +| Jinja | `{{ compiler('cxx') }}` | `${{ compiler('cxx') }}` | +| Pin subpackage | `{{ pin_subpackage(...) }}` | `${{ pin_subpackage(...) }}` | + +The `staging:` + `inherit: build-cache` pattern in `recipe.yaml` builds ITK +once and reuses the build tree for all three output install phases. This is +the primary reason for using rattler-build over conda-build for this recipe. + +## CMake Flag Alignment with conda-forge Feedstock + +Reference: `conda-forge/libitk-feedstock` recipe at ITK 5.4.5. + +### Flags shared with feedstock (both use these) + +```cmake +-DBUILD_SHARED_LIBS:BOOL=ON +-DBUILD_TESTING:BOOL=OFF +-DBUILD_EXAMPLES:BOOL=OFF +-DITK_BUILD_DEFAULT_MODULES:BOOL=ON +-DITK_USE_KWSTYLE:BOOL=OFF +-DITK_DEFAULT_THREADER:STRING=Pool +-DITK_USE_SYSTEM_HDF5:BOOL=ON +-DITK_USE_SYSTEM_JPEG:BOOL=ON +-DITK_USE_SYSTEM_TIFF:BOOL=ON +-DITK_USE_SYSTEM_EIGEN:BOOL=ON +-DITK_USE_SYSTEM_FFTW:BOOL=ON +-DITK_USE_FFTWD:BOOL=ON +-DITK_USE_FFTWF:BOOL=ON +-DModule_ITKReview:BOOL=ON +-DModule_MGHIO:BOOL=ON +-DModule_ITKTBB:BOOL=OFF # macOS; ON for Linux/Windows +``` + +### Flags in our recipe not in feedstock + +```cmake +-DITK_FORBID_DOWNLOADS:BOOL=ON # prevents remote module downloads at configure time +-DITK_WRAP_PYTHON:BOOL=ON # required for libitk-wrapping output +-DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER:STRING=PythonWrapping +-DPython3_EXECUTABLE:FILEPATH="${PYTHON}" +``` + +### Flags in feedstock NOT in our recipe (investigate before upstreaming) + +```cmake +# Feedstock sets CMAKE_FIND_ROOT_PATH and CMAKE_FIND_ROOT_PATH_MODE_* to +# constrain all find_package/find_library calls to the conda PREFIX. +# We use CMAKE_PREFIX_PATH instead. These may matter for cross-compilation. +-DCMAKE_FIND_ROOT_PATH:PATH="${PREFIX}" +-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE:STRING=ONLY +-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY:STRING=ONLY +-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM:STRING=NEVER +-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE:STRING=ONLY +-DCMAKE_FIND_FRAMEWORK:STRING=NEVER +-DCMAKE_FIND_APPBUNDLE:STRING=NEVER + +# macOS-specific GDCM fix (feedstock has this, we do not) +-DGDCM_USE_COREFOUNDATION_LIBRARY:BOOL=OFF + +# NIFTI math lib — feedstock passes empty string to disable +-DNIFTI_SYSTEM_MATH_LIB= + +# Module_SimpleITKFilters — feedstock enables this, we do not +# It requires SimpleITK to be available; needs investigation for 6.0.0 +-DModule_SimpleITKFilters=ON +``` + +### Modules omitted from our recipe that the feedstock enables + +These are in the feedstock (5.4.5) but cannot be included here: + +| Module | Why omitted | +|--------|-------------| +| `Module_ITKIOTransformMINC` | Marked `EXCLUDE_FROM_DEFAULT` in `itk-module.cmake`; depends on `ITKMINC` (libminc), which is not in the recipe host requirements. The library appears in `ITKTargets.cmake` but isn't installed, breaking downstream `find_package(ITK)`. Needs a `libminc` host dep and install component investigation before re-enabling. | +| `Module_GenericLabelInterpolator` | Remote module (uses `itk_fetch_module()` to clone from GitHub). Incompatible with `-DITK_FORBID_DOWNLOADS=ON`. | +| `Module_AdaptiveDenoising` | Same as above — remote module requiring git fetch at configure time. | + +## Bug Fixes Required for System Library Builds + +These five commits were added to the branch to make the conda system-library +build work. All are legitimate fixes for issues that appear when +`ITK_USE_SYSTEM_*=ON` and do not regress the bundled-library path. + +| Commit | File(s) | Root cause | +|--------|---------|------------| +| `cdc0eaaeb7` | `Modules/ThirdParty/HDF5/CMakeLists.txt` | `itk_module_impl()` prepends `ITK_LIBRARY_NAMESPACE::` to all dep names. System HDF5 exports bare targets (`hdf5-shared`, not `ITK::hdf5-shared`). Bundled path creates wrappers via `itk_module_target()`; system path (`ITKHDF5_NO_SRC=1`) skips that. Fix: create `ITK::hdf5-*` INTERFACE IMPORTED wrapper targets when `ITK_USE_SYSTEM_HDF5=ON`. | +| `db09bd1371` | `Modules/ThirdParty/Eigen3/CMakeLists.txt` | Modules with only `COMPILE_DEPENDS` on ITKEigen3 (not link deps) rely on `include_directories(${ITKEigen3_INCLUDE_DIRS})` from `_itk_module_use_recurse()`. A prior commit dropped the `get_target_property()` that populated this variable. Fix: restore it. | +| `63a9c6ba41` | `Modules/Nonunit/Review/itk-module.cmake` | `itkVoxBoCUBImageIO.cxx` calls `gzopen/gzclose/gzread/gzwrite` directly. Missing explicit dep masked by Linux lazy dynamic linking; fatal on macOS ARM64. Fix: add `ITKZLIB` to `PRIVATE_DEPENDS`. | +| `090106d316` | `CMake/ITKModuleMacros.cmake` | BUILD backward-compat shims had `if(NOT TARGET)` guard; EXPORT_CODE_INSTALL shims did not. When a downstream project calls `find_package(HDF5)` before `find_package(ITK)`, bare HDF5 targets already exist and the shim `add_library` call fails. Fix: add the same guard to EXPORT_CODE_INSTALL. | +| `a8d7a90430` | `CMake/ITKConfig.cmake.in`, `Modules/ThirdParty/HDF5/CMakeLists.txt` | **Root cause of libitk-devel test failure.** `ITKTargets.cmake` sets `INTERFACE_LINK_LIBRARIES` on `ITK::ITKHDF5Module` to `ITK::hdf5-shared` etc. CMake policy CMP0028 (NEW) requires those `::` targets to exist at `include()` time. Fix part 1: reorder `ITKConfig.cmake.in` so `itk_module_config()` runs before `include(ITKTargets.cmake)`. Fix part 2: extend `ITKHDF5_EXPORT_CODE_INSTALL` to also create the `ITK::hdf5-*` wrappers in the installed config (so downstream `find_package(ITK)` in the libitk-devel test works). | + +## Platform Matrix + +| Platform | Runner | Notes | +|----------|--------|-------| +| `linux-64` | `ubuntu-latest` | Native; all system deps ON | +| `linux-aarch64` | `ubuntu-24.04-arm` | Native ARM runner | +| `osx-arm64` | `macos-latest` | TBB disabled (`use_tbb=OFF`) | +| `osx-64` | `macos-13` | Last Intel runner; TBB disabled | +| `win-64` | `windows-latest` | EXPAT, PNG, ZLIB bundled (not system); TBB ON | + +Cross-compilation (e.g. osx-64 host building osx-arm64) uses +`Utilities/conda-packages/cross-compile/TryRunResults.cmake` with pre-computed +`TRY_RUN` results. Sourced from the conda-forge feedstock's +`TryRunResults-osx-arm64.cmake`. + +## Build Guard (Iteration Helper) + +`Utilities/conda-packages/build.sh` exits early if `CMakeCache.txt` already exists in +`$SRC_DIR/build/`. This lets you iterate on install scripts and tests without +recompiling ITK. Use `rattler-build build --keep-build` to preserve the work +directory between runs. Delete `$SRC_DIR/build/CMakeCache.txt` to force a +clean rebuild. + +## libitk-devel Test + +The CMake build test in `Utilities/conda-packages/tests/example/` calls +`find_package(ITK)` on the installed `libitk-devel` package. This test +validates the complete install chain: ITKConfig.cmake ordering, HDF5 +EXPORT_CODE wrapper creation, and ITKTargets.cmake integrity. + +The test cmake project uses `find_package(ITK REQUIRED)` and +`include(${ITK_USE_FILE})`, so any broken target reference in +ITKTargets.cmake surfaces here first. + +## Relationship to ITKPythonPackage PR #302 + +InsightSoftwareConsortium/ITKPythonPackage#302 is a Python build system +refactor for generating ITK Python wheels. The relevant overlap: + +- The PR introduces `libitk-wrapping` as a build dependency concept: remote + modules would detect a pre-built `libitk-wrapping` conda package via + `CONDA_PREFIX/lib/cmake/ITK-*/ITKConfig.cmake` and skip recompiling ITK. +- Consensus (thewtex, 2026-04-14): move conda recipe infrastructure to the + main ITK repo. This branch implements that consensus. +- The `libitk-wrapping` package in this recipe is the C++ side of the + speedup described in that PR (Python wrapping artifacts pre-built and + installable without recompiling ITK from source). + +## conda-forge Feedstock Relationship + +The conda-forge `libitk-feedstock` (https://github.com/conda-forge/libitk-feedstock) +is currently at ITK 5.4.5 using `meta.yaml` (conda-build format). Once this +recipe is merged and stable, the feedstock should be updated to: +1. Migrate from `meta.yaml` to `recipe.yaml` (rattler-build format) +2. Reference or inherit from the canonical recipe in this repo +3. Add `libitk-wrapping` as a third output + +The feedstock maintainers are @blowekamp and @bluescarni. + +## Running Locally + +Two equivalent interfaces. Both require `preview = ["pixi-build"]` in +`[tool.pixi.workspace]` of `pyproject.toml` (already set). The `--experimental` +flag is required for direct `rattler-build` invocations because this recipe +uses the `staging:` + `inherit:` build-cache pattern, which is gated behind +rattler-build's experimental feature flag. + +```bash +# Via pixi build (staging outputs enabled by workspace preview flag) +pixi build \ + --path Utilities/conda-packages/recipe.yaml \ + --output-dir /tmp/itk-conda-test/ + +# Via rattler-build directly (also supports --keep-build for fast iteration) +pixi run --environment dev rattler-build build --experimental \ + --recipe Utilities/conda-packages/recipe.yaml \ + --output-dir /tmp/itk-conda-test/ \ + --keep-build + +# Render only — validate YAML without building +pixi run --environment dev rattler-build build --experimental \ + --recipe Utilities/conda-packages/recipe.yaml \ + --render-only +``` + +`rattler-build build --keep-build` is preferred during development because it +preserves the work directory so subsequent runs skip the cmake compile step +(see build guard in `build.sh`). + +For opt-in ccache passthrough (fast incremental rebuilds when the work +directory is discarded between runs), see +[`README_Advanced.md`](./README_Advanced.md). diff --git a/Utilities/conda-packages/README_Advanced.md b/Utilities/conda-packages/README_Advanced.md new file mode 100644 index 00000000000..6f1da1ec561 --- /dev/null +++ b/Utilities/conda-packages/README_Advanced.md @@ -0,0 +1,217 @@ +# Advanced Utilities/conda-packages build options + +Advanced / opt-in features of the `Utilities/conda-packages/` build. The baseline +workflow is in [`README.md`](./README.md); read that first. + +## ccache — compiler caching across rattler-build runs + +Rattler-build creates a fresh work directory (`rattler-build_libitk_/`) +per invocation. Without caching, every run does a full ITK rebuild (~30+ +minutes on a workstation). Enabling ccache drops subsequent builds to a +few minutes when only install scripts or recipe metadata change. + +### Opting in + +ccache is disabled by default. To enable it, set `ITK_CONDA_USE_CCACHE=1` +in the host shell before invoking `pixi build` or `rattler-build build`. +When the gate is off, ccache is not pulled into `build:` requirements and +the CMake compiler-launcher flags are not set — the build is byte-for-byte +identical to a non-ccache build. + +### Full usage — host shell variables + +```bash +# ── REQUIRED ──────────────────────────────────────────────── +export ITK_CONDA_USE_CCACHE=1 # gate: activates everything below + +# ── STRONGLY RECOMMENDED ─────────────────────────────────── +export CCACHE_DIR=$HOME/.ccache # where cache lives (persists across runs) + +# ── OPTIONAL ─────────────────────────────────────────────── +export CCACHE_MAXSIZE=40G # cap cache size (default 5 GB is too small for ITK) + +# ── RUN ───────────────────────────────────────────────────── +pixi run --environment dev rattler-build build --experimental \ + --recipe Utilities/conda-packages/recipe.yaml \ + --output-dir ~/my-itk-channel/ +``` + +### Variable reference + +| Variable | Set by | Purpose | +|---|---|---| +| `ITK_CONDA_USE_CCACHE` | **host** | Gate. Only `=1` activates the ccache path. | +| `CCACHE_DIR` | **host** | Path to the cache store. Falls back to `$XDG_CACHE_HOME/ccache` or `~/.cache/ccache`. | +| `CCACHE_MAXSIZE` | **host** | Cache size cap (e.g. `40G`). Alternatively run `ccache -M 40G` once. | +| `CCACHE_BASEDIR` | `build.sh` | Set to `$SRC_DIR` so ccache rewrites rattler-build's timestamped paths relative. | +| `CCACHE_NOHASHDIR` | `build.sh` | Excludes cwd from the hash (same reason). | +| `CCACHE_SLOPPINESS` | `build.sh` | `file_macro,time_macros,include_file_ctime,include_file_mtime,pch_defines,system_headers` — relaxes checks that otherwise prevent hits across sandbox reruns. | +| `CCACHE_COMPILERCHECK` | `build.sh` | `content` — hashes the compiler binary contents rather than mtime, so conda reinstalls of the toolchain don't invalidate the entire cache. | + +Sandbox-specific variables (the bottom four) are set inside `build.sh` +because `$SRC_DIR` changes per rattler-build invocation and the host +shell cannot know it in advance. + +### How it is wired + +1. **`recipe.yaml`** + - `build.script.env` forwards `ITK_CONDA_USE_CCACHE`, `CCACHE_DIR`, + and `CCACHE_MAXSIZE` from the host shell into the sandbox via + `${{ env.get(...) }}` jinja. + - `requirements.build` conditionally adds `ccache` when the env var + equals `"1"`: + ```yaml + - if: env.get("ITK_CONDA_USE_CCACHE", default="0") == "1" + then: ccache + ``` +2. **`build.sh`** — detects the gate, exports the sandbox-specific vars, + prints `ccache --show-stats`, and appends + `-DCMAKE_{C,CXX}_COMPILER_LAUNCHER=ccache` to the CMake line. +3. **`build.bat`** — the same pattern for Windows (note: ccache on Windows + is less mature; recent `ccache >=4.8` is recommended). + +### Verifying hit rate + +After the first (cold) build completes: + +```bash +ccache --show-stats +``` + +Expected ordering for the next run: + +| Run | Cache behavior | Wall time | +|-----|----------------|-----------| +| 1st (cold) | All misses, cache populates | ~30 min (baseline) | +| 2nd (no source changes, e.g. install-script tweak) | ~100% hits | ~2–5 min | +| Nth (incremental source change) | Hits on untouched TUs | proportional to changed files | + +If the second run still shows many misses, check: + +- Compiler binary changed between runs (happens when conda updates the + compiler package) — `CCACHE_COMPILERCHECK=content` mitigates this. +- Source tree path changed — `CCACHE_BASEDIR` is supposed to handle this + via path relativization; verify `$SRC_DIR` is consistent. +- Recipe changed a compile flag — a genuine miss, not a bug. + +### Turning it back off + +Unset the gate: + +```bash +unset ITK_CONDA_USE_CCACHE +``` + +Or just start a fresh shell. With the gate off, the recipe does not depend +on `ccache` and the build script does not enable the compiler launcher. + +--- + +## Building with a specific set of compiler flags + +Conda-forge's compiler packages populate `CFLAGS`, `CXXFLAGS`, and `LDFLAGS` +from their activation scripts (`{build_env}/etc/conda/activate.d/*.sh`). +These flags carry `--sysroot`, target triples, and conda-forge's hardening +defaults — replacing them wholesale will break the build. Therefore the +recipe exposes **append-only** variables prefixed `ITK_CONDA_EXTRA_*`. +Anything you set there is appended to what conda's activation produced. + +### Full usage — host shell variables + +```bash +# ── BUILD TYPE OVERRIDE (default: Release) ───────────────── +export ITK_CONDA_BUILD_TYPE=RelWithDebInfo # or Debug, MinSizeRel + +# ── APPEND TO CONDA-FORGE COMPILER FLAGS ─────────────────── +export ITK_CONDA_EXTRA_CFLAGS="-march=native -DFOO=1" +export ITK_CONDA_EXTRA_CXXFLAGS="-march=native -Wno-deprecated-declarations" +export ITK_CONDA_EXTRA_LDFLAGS="-Wl,--as-needed" + +# ── RAW EXTRA CMAKE ARGS (whole tokens, quoted) ──────────── +export ITK_CONDA_EXTRA_CMAKE_ARGS="-DITK_USE_GPU:BOOL=ON -DITK_WRAP_DOC:BOOL=OFF" + +# ── RUN ──────────────────────────────────────────────────── +pixi run --environment dev rattler-build build --experimental \ + --recipe Utilities/conda-packages/recipe.yaml \ + --output-dir ~/my-itk-channel/ +``` + +All five are optional — unset any you do not need. + +### Variable reference + +| Variable | Default | What it does | +|---|---|---| +| `ITK_CONDA_BUILD_TYPE` | `Release` | Value of `-DCMAKE_BUILD_TYPE`. Useful values: `Release`, `RelWithDebInfo`, `Debug`, `MinSizeRel`. | +| `ITK_CONDA_EXTRA_CFLAGS` | empty | Appended to `CFLAGS` inside the sandbox (after conda activation). C-only flags. | +| `ITK_CONDA_EXTRA_CXXFLAGS` | empty | Appended to `CXXFLAGS`. C++-only flags. | +| `ITK_CONDA_EXTRA_LDFLAGS` | empty | Appended to `LDFLAGS`. Linker flags. | +| `ITK_CONDA_EXTRA_CMAKE_ARGS` | empty | Pasted verbatim into the `cmake` command line **before** the hardcoded `-D…` flags. Each token becomes a separate cmake argument. | + +### Recipes for common scenarios + +**Native-architecture optimized build (desktop/workstation use only — +not redistributable as a conda package):** + +```bash +export ITK_CONDA_EXTRA_CFLAGS="-march=native -mtune=native" +export ITK_CONDA_EXTRA_CXXFLAGS="-march=native -mtune=native" +``` + +**RelWithDebInfo for profiling:** + +```bash +export ITK_CONDA_BUILD_TYPE=RelWithDebInfo +# CMake's RelWithDebInfo defaults to -O2 -g; add -fno-omit-frame-pointer +# if you want flame graphs to resolve properly. +export ITK_CONDA_EXTRA_CXXFLAGS="-fno-omit-frame-pointer" +``` + +**AddressSanitizer-instrumented build:** + +```bash +export ITK_CONDA_BUILD_TYPE=Debug +export ITK_CONDA_EXTRA_CFLAGS="-fsanitize=address -fno-omit-frame-pointer" +export ITK_CONDA_EXTRA_CXXFLAGS="-fsanitize=address -fno-omit-frame-pointer" +export ITK_CONDA_EXTRA_LDFLAGS="-fsanitize=address" +``` + +**Override module selection without editing `build.sh`:** + +```bash +export ITK_CONDA_EXTRA_CMAKE_ARGS="-DModule_ITKReview:BOOL=OFF -DModule_ITKVideoBridgeVXL:BOOL=ON" +``` + +Note: `ITK_CONDA_EXTRA_CMAKE_ARGS` is pasted **before** the recipe's +hardcoded `-D` flags, so it **cannot** override a flag that `build.sh` +sets explicitly (the last `-D` wins in CMake). To change a hardcoded +flag, edit `build.sh` directly. + +### Verifying flags reached the compiler + +After the build starts, `build.sh` prints lines like: + +``` +Appended to CXXFLAGS: -march=native -Wno-deprecated-declarations +``` + +For the authoritative view, check the `compile_commands.json` that ninja +generates in the rattler-build work directory (use +`rattler-build build --keep-build` so the work dir survives): + +```bash +jq '.[0].command' \ + output/bld/rattler-build_libitk_*/work/build/compile_commands.json +``` + +### Host-shell `CFLAGS` is NOT forwarded + +A deliberate choice. Conda-forge's compiler activation sets `CFLAGS` inside +the sandbox **before** the build script runs; forwarding the host's value +as-is would either replace the activation output (breaking the build) or +silently double up. The append-only `ITK_CONDA_EXTRA_CFLAGS` pattern keeps +both the conda-forge flags and the user's additions, with no ambiguity. + +If you genuinely need to replace conda-forge's compiler flags (rare, and +usually a sign something else is wrong), edit the `cmake` command in +`build.sh` directly — do not try to do it via environment variables. diff --git a/Utilities/conda-packages/build.bat b/Utilities/conda-packages/build.bat new file mode 100755 index 00000000000..a589594b74c --- /dev/null +++ b/Utilities/conda-packages/build.bat @@ -0,0 +1,52 @@ +@echo off +setlocal enabledelayedexpansion + +set BUILD_DIR=%SRC_DIR%\build +mkdir "%BUILD_DIR%" +cd /d "%BUILD_DIR%" + +set CL=/MP + +REM Optional ccache passthrough. Activated only when the host shell sets +REM ITK_CONDA_USE_CCACHE=1 AND the recipe's build requirements pulled in +REM the ccache binary (see recipe.yaml:build conditional). +set CCACHE_CMAKE_ARGS= +if "%ITK_CONDA_USE_CCACHE%"=="1" ( + where ccache >nul 2>nul && ( + echo ccache enabled for Windows conda build + set "CCACHE_CMAKE_ARGS=-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" + ) +) + +cmake -GNinja %CCACHE_CMAKE_ARGS% ^ + -DCMAKE_BUILD_TYPE:STRING=Release ^ + -DCMAKE_INSTALL_PREFIX:PATH="%LIBRARY_PREFIX%" ^ + -DCMAKE_PREFIX_PATH:PATH="%LIBRARY_PREFIX%" ^ + -DBUILD_SHARED_LIBS:BOOL=ON ^ + -DBUILD_TESTING:BOOL=OFF ^ + -DBUILD_EXAMPLES:BOOL=OFF ^ + -DITK_BUILD_DEFAULT_MODULES:BOOL=ON ^ + -DITK_USE_KWSTYLE:BOOL=OFF ^ + -DITK_DEFAULT_THREADER:STRING=Pool ^ + -DModule_ITKReview:BOOL=ON ^ + -DModule_ITKTBB:BOOL=ON ^ + -DModule_MGHIO:BOOL=ON ^ + -DModule_ITKIOTransformInsightLegacy:BOOL=ON ^ + -DModule_ITKDeprecated:BOOL=ON ^ + -DITK_USE_SYSTEM_FFTW:BOOL=ON ^ + -DITK_USE_SYSTEM_HDF5:BOOL=ON ^ + -DITK_USE_SYSTEM_JPEG:BOOL=ON ^ + -DITK_USE_SYSTEM_TIFF:BOOL=ON ^ + -DITK_USE_SYSTEM_EIGEN:BOOL=ON ^ + -DITK_USE_SYSTEM_DOUBLECONVERSION:BOOL=ON ^ + -DITK_USE_FFTWD:BOOL=ON ^ + -DITK_USE_FFTWF:BOOL=ON ^ + -DITK_FORBID_DOWNLOADS:BOOL=ON ^ + -DITK_WRAP_PYTHON:BOOL=ON ^ + -DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER:STRING=PythonWrapping ^ + -DPython3_EXECUTABLE:FILEPATH="%PYTHON%" ^ + .. +if errorlevel 1 exit 1 + +cmake --build . --config Release +if errorlevel 1 exit 1 diff --git a/Utilities/conda-packages/build.sh b/Utilities/conda-packages/build.sh new file mode 100755 index 00000000000..52b0222c695 --- /dev/null +++ b/Utilities/conda-packages/build.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +set -ex + +# Remove any stale in-source CMake artifacts that would cause path-mismatch errors +rm -f "${SRC_DIR}/CMakeCache.txt" +rm -rf "${SRC_DIR}/CMakeFiles" "${SRC_DIR}/CMakeTmp" + +BUILD_DIR="${SRC_DIR}/build" +mkdir -p "${BUILD_DIR}" + +# Skip configure+build if a previous run already produced the build tree. +# Useful when iterating on install/test steps without recompiling ITK. +# Remove $BUILD_DIR/CMakeCache.txt manually to force a clean rebuild. +if [ -f "${BUILD_DIR}/CMakeCache.txt" ]; then + echo "Existing build detected — skipping cmake configure and build." + exit 0 +fi + +# Inject pre-computed TryRun results for cross-compilation targets +CROSS_CMAKE_ARGS="" +if [ "${BUILD_PLATFORM}" != "${TARGET_PLATFORM}" ]; then + cp "${RECIPE_DIR}/cross-compile/TryRunResults.cmake" "${BUILD_DIR}/" + CROSS_CMAKE_ARGS="-C ${BUILD_DIR}/TryRunResults.cmake" +fi + +# Use TBB on Linux; macOS has known issues with conda-forge TBB +use_tbb=ON +if [ "$(uname)" = "Darwin" ]; then + use_tbb=OFF +fi + +# Optional ccache passthrough. Activated only when the host shell sets +# ITK_CONDA_USE_CCACHE=1 AND the recipe's build requirements pulled in +# the ccache binary (see recipe.yaml:build conditional). +CCACHE_CMAKE_ARGS="" +if [ "${ITK_CONDA_USE_CCACHE:-0}" = "1" ] && command -v ccache >/dev/null 2>&1; then + # Sandbox-specific vars: rattler-build creates a fresh work dir per build + # (rattler-build_libitk_/work/), so tell ccache to: + # - rewrite absolute paths relative to SRC_DIR (BASEDIR) + # - ignore cwd in the hash (NOHASHDIR) + # - skip __FILE__/__TIME__/__DATE__ macros and mtime noise (SLOPPINESS) + # - hash the compiler binary contents, not mtime (COMPILERCHECK) + export CCACHE_BASEDIR="${SRC_DIR}" + export CCACHE_NOHASHDIR=true + export CCACHE_SLOPPINESS="file_macro,time_macros,include_file_ctime,include_file_mtime,pch_defines,system_headers" + export CCACHE_COMPILERCHECK=content + + echo "ccache enabled: $(ccache --version | head -1)" + echo "CCACHE_DIR=${CCACHE_DIR:-}" + echo "CCACHE_BASEDIR=${CCACHE_BASEDIR}" + echo "CCACHE_MAXSIZE=${CCACHE_MAXSIZE:-}" + ccache --show-stats 2>/dev/null || true + CCACHE_CMAKE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" +fi + +# Optional compile/link flag passthrough. Appends to — does not replace — +# conda-forge's compiler activation flags (CFLAGS/CXXFLAGS/LDFLAGS are +# already populated by {build_env}/etc/conda/activate.d/*.sh). +# See README_Advanced.md for usage. +if [ -n "${ITK_CONDA_EXTRA_CFLAGS:-}" ]; then + export CFLAGS="${CFLAGS:-} ${ITK_CONDA_EXTRA_CFLAGS}" + echo "Appended to CFLAGS: ${ITK_CONDA_EXTRA_CFLAGS}" +fi +if [ -n "${ITK_CONDA_EXTRA_CXXFLAGS:-}" ]; then + export CXXFLAGS="${CXXFLAGS:-} ${ITK_CONDA_EXTRA_CXXFLAGS}" + echo "Appended to CXXFLAGS: ${ITK_CONDA_EXTRA_CXXFLAGS}" +fi +if [ -n "${ITK_CONDA_EXTRA_LDFLAGS:-}" ]; then + export LDFLAGS="${LDFLAGS:-} ${ITK_CONDA_EXTRA_LDFLAGS}" + echo "Appended to LDFLAGS: ${ITK_CONDA_EXTRA_LDFLAGS}" +fi + +# Build type is Release by default; override with ITK_CONDA_BUILD_TYPE. +ITK_BUILD_TYPE="${ITK_CONDA_BUILD_TYPE:-Release}" + +cmake ${CROSS_CMAKE_ARGS} ${CCACHE_CMAKE_ARGS} ${ITK_CONDA_EXTRA_CMAKE_ARGS:-} \ + -S "${SRC_DIR}" \ + -B "${BUILD_DIR}" \ + -GNinja \ + -DCMAKE_C_COMPILER_AR="${AR}" \ + -DCMAKE_C_COMPILER_RANLIB="${RANLIB}" \ + -DCMAKE_CXX_COMPILER_AR="${AR}" \ + -DCMAKE_CXX_COMPILER_RANLIB="${RANLIB}" \ + -DCMAKE_BUILD_TYPE:STRING="${ITK_BUILD_TYPE}" \ + -DCMAKE_INSTALL_PREFIX:PATH="${PREFIX}" \ + -DCMAKE_PREFIX_PATH:PATH="${PREFIX}" \ + -DBUILD_SHARED_LIBS:BOOL=ON \ + -DBUILD_TESTING:BOOL=OFF \ + -DBUILD_EXAMPLES:BOOL=OFF \ + -DITK_BUILD_DEFAULT_MODULES:BOOL=ON \ + -DITK_USE_KWSTYLE:BOOL=OFF \ + -DITK_DEFAULT_THREADER:STRING=Pool \ + -DModule_ITKReview:BOOL=ON \ + -DModule_ITKTBB:BOOL=${use_tbb} \ + -DModule_MGHIO:BOOL=ON \ + -DModule_ITKIOTransformInsightLegacy:BOOL=ON \ + -DModule_ITKDeprecated:BOOL=ON \ + -DITK_USE_SYSTEM_EXPAT:BOOL=ON \ + -DITK_USE_SYSTEM_FFTW:BOOL=ON \ + -DITK_USE_SYSTEM_HDF5:BOOL=ON \ + -DITK_USE_SYSTEM_JPEG:BOOL=ON \ + -DITK_USE_SYSTEM_PNG:BOOL=ON \ + -DITK_USE_SYSTEM_TIFF:BOOL=ON \ + -DITK_USE_SYSTEM_ZLIB:BOOL=ON \ + -DITK_USE_SYSTEM_EIGEN:BOOL=ON \ + -DITK_USE_SYSTEM_DOUBLECONVERSION:BOOL=ON \ + -DITK_USE_FFTWD:BOOL=ON \ + -DITK_USE_FFTWF:BOOL=ON \ + -DNIFTI_SYSTEM_MATH_LIB:STRING="" \ + -DITK_FORBID_DOWNLOADS:BOOL=ON \ + -DITK_WRAP_PYTHON:BOOL=ON \ + -DITK_USE_SYSTEM_CASTXML:BOOL=ON \ + -DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER:STRING=PythonWrapping \ + -DPython3_EXECUTABLE:FILEPATH="${PYTHON}" + +cmake --build "${BUILD_DIR}" --config Release diff --git a/Utilities/conda-packages/cross-compile/TryRunResults.cmake b/Utilities/conda-packages/cross-compile/TryRunResults.cmake new file mode 100644 index 00000000000..8317425e4b7 --- /dev/null +++ b/Utilities/conda-packages/cross-compile/TryRunResults.cmake @@ -0,0 +1,129 @@ +# Pre-computed TryRun results for cross-compilation targets (aarch64/arm64). +# Used by build.sh when BUILD_PLATFORM != TARGET_PLATFORM. +# Sourced from conda-forge/libitk-feedstock TryRunResults-osx-arm64.cmake. +# Update this file when ITK adds new TryRun checks (CMake will warn at build time). +# This file was generated by CMake because it detected TRY_RUN() commands +# in crosscompiling mode. It will be overwritten by the next CMake run. +# Copy it to a safe location, set the variables to appropriate values +# and use it then to preset the CMake cache (using -C). + +# DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +# indicates whether the executable would have been able to run on its +# target platform. If so, set DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS to +# the exit code (in many cases 0 for success), otherwise enter "FAILED_TO_RUN". +# DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS__TRYRUN_OUTPUT +# contains the text the executable would have printed on stdout and stderr. +# If the executable would not have been able to run, set DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS__TRYRUN_OUTPUT empty. +# Otherwise check if the output is evaluated by the calling CMake code. If so, +# check what the source file would have printed when called with the given arguments. +# The DOUBLE_CORRECT_NEEDED_COMPILED variable holds the build result for this TRY_RUN(). +# +# Source file : /scratch/blowekamp/ITK/Modules/ThirdParty/DoubleConversion/src/double-conversion/CMakeFiles/double-correction-needed.cc +# Executable : /scratch/blowekamp/ITK/CMakeFiles/cmTC_8661a-DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +# Run arguments : +# Called from: [1] /home/blowekamp/src/ITK/Modules/ThirdParty/DoubleConversion/src/double-conversion/CMakeLists.txt + +set( + DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS + 1 + CACHE STRING + "Result from TRY_RUN" + FORCE +) + +# VCL_HAS_LFS +# indicates whether the executable would have been able to run on its +# target platform. If so, set VCL_HAS_LFS to +# the exit code (in many cases 0 for success), otherwise enter "FAILED_TO_RUN". +# VCL_HAS_LFS__TRYRUN_OUTPUT +# contains the text the executable would have printed on stdout and stderr. +# If the executable would not have been able to run, set VCL_HAS_LFS__TRYRUN_OUTPUT empty. +# Otherwise check if the output is evaluated by the calling CMake code. If so, +# check what the source file would have printed when called with the given arguments. +# The VCL_HAS_LFS_COMPILED variable holds the build result for this TRY_RUN(). +# +# Source file : /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/config/cmake/config/vxl_platform_tests.cxx +# Executable : /scratch/blowekamp/ITK/CMakeFiles/cmTC_22ba0-VCL_HAS_LFS +# Run arguments : +# Called from: [3] /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/config/cmake/config/VXLIntrospectionConfig.cmake +# [2] /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/config/cmake/config/VXLIntrospectionConfig.cmake +# [1] /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/CMakeLists.txt + +set(VCL_HAS_LFS 1 CACHE STRING "Result from TRY_RUN" FORCE) + +# VXL_HAS_SSE2_HARDWARE_SUPPORT +# indicates whether the executable would have been able to run on its +# target platform. If so, set VXL_HAS_SSE2_HARDWARE_SUPPORT to +# the exit code (in many cases 0 for success), otherwise enter "FAILED_TO_RUN". +# VXL_HAS_SSE2_HARDWARE_SUPPORT__TRYRUN_OUTPUT +# contains the text the executable would have printed on stdout and stderr. +# If the executable would not have been able to run, set VXL_HAS_SSE2_HARDWARE_SUPPORT__TRYRUN_OUTPUT empty. +# Otherwise check if the output is evaluated by the calling CMake code. If so, +# check what the source file would have printed when called with the given arguments. +# The VXL_HAS_SSE2_HARDWARE_SUPPORT_COMPILED variable holds the build result for this TRY_RUN(). +# +# Source file : /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/config/cmake/config/vxl_platform_tests.cxx +# Executable : /scratch/blowekamp/ITK/CMakeFiles/cmTC_df9f4-VXL_HAS_SSE2_HARDWARE_SUPPORT +# Run arguments : +# Called from: [3] /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/config/cmake/config/VXLIntrospectionConfig.cmake +# [2] /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/config/cmake/config/VXLIntrospectionConfig.cmake +# [1] /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/CMakeLists.txt + +set(VXL_HAS_SSE2_HARDWARE_SUPPORT 0 CACHE STRING "Result from TRY_RUN" FORCE) + +# VXL_SSE2_HARDWARE_SUPPORT_POSSIBLE +# indicates whether the executable would have been able to run on its +# target platform. If so, set VXL_SSE2_HARDWARE_SUPPORT_POSSIBLE to +# the exit code (in many cases 0 for success), otherwise enter "FAILED_TO_RUN". +# VXL_SSE2_HARDWARE_SUPPORT_POSSIBLE__TRYRUN_OUTPUT +# contains the text the executable would have printed on stdout and stderr. +# If the executable would not have been able to run, set VXL_SSE2_HARDWARE_SUPPORT_POSSIBLE__TRYRUN_OUTPUT empty. +# Otherwise check if the output is evaluated by the calling CMake code. If so, +# check what the source file would have printed when called with the given arguments. +# The VXL_SSE2_HARDWARE_SUPPORT_POSSIBLE_COMPILED variable holds the build result for this TRY_RUN(). +# +# Source file : /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/config/cmake/config/vxl_platform_tests.cxx +# Executable : /scratch/blowekamp/ITK/CMakeFiles/cmTC_06eb5-VXL_SSE2_HARDWARE_SUPPORT_POSSIBLE +# Run arguments : +# Called from: [3] /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/config/cmake/config/VXLIntrospectionConfig.cmake +# [2] /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/config/cmake/config/VXLIntrospectionConfig.cmake +# [1] /home/blowekamp/src/ITK/Modules/ThirdParty/VNL/src/vxl/CMakeLists.txt + +set( + VXL_SSE2_HARDWARE_SUPPORT_POSSIBLE + 0 + CACHE STRING + "Result from TRY_RUN" + FORCE +) + +set(_libcxx_run_result 0 CACHE STRING "Results from TRY_RUN" FORCE) +set( + _libcxx_run_result__TRYRUN_OUTPUT + "nothing" + CACHE STRING + "Results from TRY_RUN" + FORCE +) + +# QNANHIBIT_VALUE +# indicates whether the executable would have been able to run on its +# target platform. If so, set QNANHIBIT_VALUE to +# the exit code (in many cases 0 for success), otherwise enter "FAILED_TO_RUN". +# QNANHIBIT_VALUE__TRYRUN_OUTPUT +# contains the text the executable would have printed on stdout and stderr. +# If the executable would not have been able to run, set QNANHIBIT_VALUE__TRYRUN_OUTPUT empty. +# Otherwise check if the output is evaluated by the calling CMake code. If so, +# check what the source file would have printed when called with the given arguments. +# The HAVE_QNANHIBIT_VALUE variable holds the build result for this TRY_RUN(). +# +# Source file : /home/blowekamp/src/ITK/Modules/ThirdParty/NrrdIO/src/NrrdIO/CMake/TestQnanhibit.c +# Executable : /scratch/blowekamp/ITK/CMakeFiles/cmTC_d55bf-QNANHIBIT_VALUE +# Run arguments : +# Called from: [2] /home/blowekamp/src/ITK/Modules/ThirdParty/NrrdIO/src/NrrdIO/CMake/TestQnanhibit.cmake +# [1] /home/blowekamp/src/ITK/Modules/ThirdParty/NrrdIO/src/NrrdIO/CMakeLists.txt + +set(QNANHIBIT_VALUE 1 CACHE STRING "Result from TRY_RUN" FORCE) +set(QNANHIBIT_VALUE__TRYRUN_OUTPUT "" CACHE STRING "Result from TRY_RUN" FORCE) + +set(HAVE_CLOCK_GETTIME_RUN 0 CACHE STRING "Result from TRY_RUN" FORCE) diff --git a/Utilities/conda-packages/libitk-devel_install.bat b/Utilities/conda-packages/libitk-devel_install.bat new file mode 100755 index 00000000000..528d1b909f0 --- /dev/null +++ b/Utilities/conda-packages/libitk-devel_install.bat @@ -0,0 +1,6 @@ +@echo off +set BUILD_DIR=%SRC_DIR%\build +cmake -DCOMPONENT=Development -P "%BUILD_DIR%\cmake_install.cmake" +if errorlevel 1 exit 1 +cmake -DCOMPONENT=Headers -P "%BUILD_DIR%\cmake_install.cmake" +if errorlevel 1 exit 1 diff --git a/Utilities/conda-packages/libitk-devel_install.sh b/Utilities/conda-packages/libitk-devel_install.sh new file mode 100755 index 00000000000..b4957361a89 --- /dev/null +++ b/Utilities/conda-packages/libitk-devel_install.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -ex +BUILD_DIR="${SRC_DIR}/build" +cmake -DCOMPONENT=Development -P "${BUILD_DIR}/cmake_install.cmake" +cmake -DCOMPONENT=Headers -P "${BUILD_DIR}/cmake_install.cmake" diff --git a/Utilities/conda-packages/libitk-wrapping_install.bat b/Utilities/conda-packages/libitk-wrapping_install.bat new file mode 100755 index 00000000000..62fdebcaea0 --- /dev/null +++ b/Utilities/conda-packages/libitk-wrapping_install.bat @@ -0,0 +1,9 @@ +@echo off +set BUILD_DIR=%SRC_DIR%\build + +rem Install Python wrapping artifacts (compiled extension modules + Python files). +rem The PythonWrappingRuntimeLibraries component is isolated from the core C++ +rem runtime by building with -DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER=PythonWrapping, +rem which directs all wrapping install() calls to this dedicated component. +cmake -DCOMPONENT=PythonWrappingRuntimeLibraries -P "%BUILD_DIR%\cmake_install.cmake" +if errorlevel 1 exit 1 diff --git a/Utilities/conda-packages/libitk-wrapping_install.sh b/Utilities/conda-packages/libitk-wrapping_install.sh new file mode 100755 index 00000000000..b0384befa59 --- /dev/null +++ b/Utilities/conda-packages/libitk-wrapping_install.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -ex +BUILD_DIR="${SRC_DIR}/build" + +# Install Python wrapping artifacts (compiled extension modules + Python files). +# The PythonWrappingRuntimeLibraries component is isolated from the core C++ +# runtime by building with -DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER=PythonWrapping, +# which directs all wrapping install() calls to this dedicated component. +cmake -DCOMPONENT=PythonWrappingRuntimeLibraries -P "${BUILD_DIR}/cmake_install.cmake" diff --git a/Utilities/conda-packages/libitk_install.bat b/Utilities/conda-packages/libitk_install.bat new file mode 100755 index 00000000000..3c75228a950 --- /dev/null +++ b/Utilities/conda-packages/libitk_install.bat @@ -0,0 +1,7 @@ +@echo off +set BUILD_DIR=%SRC_DIR%\build +REM See libitk_install.sh for component list rationale. Keep these two lists in sync. +for %%c in (Runtime RuntimeLibraries Libraries Unspecified libraries Applications) do ( + cmake -DCOMPONENT=%%c -P "%BUILD_DIR%\cmake_install.cmake" + if errorlevel 1 exit 1 +) diff --git a/Utilities/conda-packages/libitk_install.sh b/Utilities/conda-packages/libitk_install.sh new file mode 100755 index 00000000000..fda56faea3b --- /dev/null +++ b/Utilities/conda-packages/libitk_install.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -ex +BUILD_DIR="${SRC_DIR}/build" +# Runtime-flavored CMake install components. The split between libitk, +# libitk-devel, and libitk-wrapping is defined by which components each +# install script claims. Keep this list in sync with the COMPONENT names +# that appear in ITK's install() calls: +# +# Runtime - ITK module executables (itk_module_target_install) +# RuntimeLibraries - ITK module shared libs + bundled 3rd-party .so +# Libraries - bundled DoubleConversion, MINC, OpenJPEG libs +# libraries - lowercase, used by bundled libminc +# Unspecified - CMake default when no COMPONENT= is given +# Applications - bundled OpenJPEG + DoubleConversion executables +# +# Development / Headers go to libitk-devel (see libitk-devel_install.sh). +# PythonWrappingRuntimeLibraries goes to libitk-wrapping. +# +# NOTE: if rattler-build continues to emit "Overdepending against " +# warnings for system deps (libtiff, libpng, hdf5, ...) after this change, +# inspect the produced libitk-*.conda archive to confirm that the ITKIO* +# module shared libraries (libITKIOTIFF-6.0.so etc.) are actually present +# and have correct NEEDED entries via `readelf -d`. Tracked in AUDIT.md. +for component in Runtime RuntimeLibraries Libraries Unspecified libraries Applications; do + cmake -DCOMPONENT="${component}" -P "${BUILD_DIR}/cmake_install.cmake" +done diff --git a/Utilities/conda-packages/recipe.yaml b/Utilities/conda-packages/recipe.yaml new file mode 100644 index 00000000000..6f0a93dae7b --- /dev/null +++ b/Utilities/conda-packages/recipe.yaml @@ -0,0 +1,165 @@ +context: + # Keep in sync with CMake/itkVersion.cmake — update both at release time + version: "6.0.0" + +outputs: + - staging: + name: build-cache + build: + script: + env: + # Opt-in ccache passthrough. Set ITK_CONDA_USE_CCACHE=1 in the + # host shell to enable compiler caching. CCACHE_DIR and + # CCACHE_MAXSIZE (if set on host) forward the cache location + # and size cap so hits persist across rattler-build invocations. + # Sandbox-specific ccache vars (BASEDIR, SLOPPINESS, etc.) are + # set inside build.sh since $SRC_DIR changes per invocation. + ITK_CONDA_USE_CCACHE: ${{ env.get("ITK_CONDA_USE_CCACHE", default="0") }} + CCACHE_DIR: ${{ env.get("CCACHE_DIR", default="") }} + CCACHE_MAXSIZE: ${{ env.get("CCACHE_MAXSIZE", default="") }} + # Host-shell flag passthrough. These APPEND to conda-forge's + # compiler activation flags — they do not replace them. + # Documented in README_Advanced.md. + ITK_CONDA_BUILD_TYPE: ${{ env.get("ITK_CONDA_BUILD_TYPE", default="Release") }} + ITK_CONDA_EXTRA_CFLAGS: ${{ env.get("ITK_CONDA_EXTRA_CFLAGS", default="") }} + ITK_CONDA_EXTRA_CXXFLAGS: ${{ env.get("ITK_CONDA_EXTRA_CXXFLAGS", default="") }} + ITK_CONDA_EXTRA_LDFLAGS: ${{ env.get("ITK_CONDA_EXTRA_LDFLAGS", default="") }} + ITK_CONDA_EXTRA_CMAKE_ARGS: ${{ env.get("ITK_CONDA_EXTRA_CMAKE_ARGS", default="") }} + content: + - if: unix + then: bash Utilities/conda-packages/build.sh + else: call Utilities\conda-packages\build.bat + requirements: + build: + - cmake >=3.16.3 + - ninja + - ${{ compiler('c') }} + - ${{ compiler('cxx') }} + - castxml >=0.7.0 + - if: env.get("ITK_CONDA_USE_CCACHE", default="0") == "1" + then: ccache + - if: build_platform != target_platform + then: python + host: + - if: unix + then: + - expat + - libpng + - zlib + - fftw + - hdf5 + - libjpeg-turbo + - libtiff + - eigen + - double-conversion + - tbb-devel + - python + source: + path: ../.. + + - package: + name: libitk + version: ${{ version }} + inherit: build-cache + build: + number: 0 + script: + - if: unix + then: bash Utilities/conda-packages/libitk_install.sh + else: call Utilities\conda-packages\libitk_install.bat + requirements: + run: + - if: unix + then: + - expat + - libpng + - zlib + - fftw + - hdf5 + - libjpeg-turbo + - libtiff + - double-conversion + - tbb + tests: + - script: + - if: osx + then: test -f "$PREFIX/lib/libITKCommon-6.0.dylib" + - if: linux + then: test -f "$PREFIX/lib/libITKCommon-6.0.so" + - if: win + then: if not exist "%LIBRARY_BIN%\ITKCommon-6.0.dll" exit 1 + + - package: + name: libitk-devel + version: ${{ version }} + inherit: build-cache + build: + number: 0 + script: + - if: unix + then: bash Utilities/conda-packages/libitk-devel_install.sh + else: call Utilities\conda-packages\libitk-devel_install.bat + requirements: + run_exports: + - libitk ==${{ version }} + host: + - ${{ pin_subpackage('libitk', exact=True) }} + run: + - ${{ pin_subpackage('libitk', exact=True) }} + - tbb-devel + - cmake + - eigen + tests: + - script: + - if: unix + then: test -d "$PREFIX/include/ITK-6.0" + else: if not exist "%LIBRARY_INC%\ITK-6.0" exit 1 + - files: + source: + - Utilities/conda-packages/tests/example/ + requirements: + build: + - cmake + - ninja + - ${{ compiler('cxx') }} + script: + - if: unix + then: | + mkdir build-test && cd build-test + cmake -GNinja -DCMAKE_PREFIX_PATH:PATH="$PREFIX" ../Utilities/conda-packages/tests/example + cmake --build . + else: | + mkdir build-test && cd build-test + cmake -GNinja -DCMAKE_PREFIX_PATH:PATH="%PREFIX%" ..\Utilities\conda-packages\tests\example + cmake --build . --config Release + + - package: + name: libitk-wrapping + version: ${{ version }} + inherit: build-cache + build: + number: 0 + script: + - if: unix + then: bash Utilities/conda-packages/libitk-wrapping_install.sh + else: call Utilities\conda-packages\libitk-wrapping_install.bat + requirements: + host: + - ${{ pin_subpackage('libitk', exact=True) }} + - python >=3.9 + run: + - ${{ pin_subpackage('libitk', exact=True) }} + - python >=3.9 + - numpy + tests: + - python: + imports: + - itk + +about: + homepage: https://itk.org + license: Apache-2.0 + license_file: + - LICENSE + - NOTICE + summary: Runtime libraries, development headers, and Python wrapping for the Insight Toolkit. diff --git a/Utilities/conda-packages/tests/example/CMakeLists.txt b/Utilities/conda-packages/tests/example/CMakeLists.txt new file mode 100644 index 00000000000..5a5266565b8 --- /dev/null +++ b/Utilities/conda-packages/tests/example/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.16) +project(ITKCondaTest CXX) + +find_package(ITK REQUIRED) +include(${ITK_USE_FILE}) + +add_executable(itkCondaTest main.cxx) +target_link_libraries(itkCondaTest ${ITK_LIBRARIES}) diff --git a/Utilities/conda-packages/tests/example/main.cxx b/Utilities/conda-packages/tests/example/main.cxx new file mode 100644 index 00000000000..6021ed3e8e9 --- /dev/null +++ b/Utilities/conda-packages/tests/example/main.cxx @@ -0,0 +1,29 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#include "itkImage.h" +#include "itkVersion.h" +#include + +int +main() +{ + using ImageType = itk::Image; + auto image = ImageType::New(); + std::cout << "ITK version: " << itk::Version::GetITKVersion() << std::endl; + return EXIT_SUCCESS; +} diff --git a/Utilities/conda-packages/tests/test_wrapping.py b/Utilities/conda-packages/tests/test_wrapping.py new file mode 100644 index 00000000000..3ed3e291ba3 --- /dev/null +++ b/Utilities/conda-packages/tests/test_wrapping.py @@ -0,0 +1,11 @@ +import itk +import numpy as np + +# Smoke test: create a simple ITK image via Python +ImageType = itk.Image[itk.F, 3] +image = ImageType.New() +region = itk.ImageRegion[3]() +region.SetSize([10, 10, 10]) +image.SetRegions(region) +image.Allocate() +print(f"ITK Python wrapping OK — version: {itk.Version.GetITKVersion()}") diff --git a/pixi.lock b/pixi.lock index 743d5a06de6..4e264e987b1 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3,6 +3,8 @@ environments: cxx: channels: - url: https://conda.anaconda.org/conda-forge/ + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -50,9 +52,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/patchelf-0.17.2-h58526e2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.9-hc97d973_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rattler-build-0.62.0-ha759004_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rhash-1.4.6-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda @@ -104,9 +108,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ninja-1.13.1-hdc560ac_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.2-h546c87b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/patchelf-0.17.2-h884eca8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.13.9-h4c0d347_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rattler-build-0.62.0-h1d7f6d8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rhash-1.4.6-h86ecc28_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-aarch64-2.28-h585391f_8.conda @@ -161,6 +167,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.5.4-h230baf5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.9-h17c18a5_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/rattler-build-0.62.0-hcb3c93d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h7cca4af_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/rhash-1.4.6-h6e16a3a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/sigtool-0.1.3-h88f4db0_0.tar.bz2 @@ -217,6 +224,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.4-h5503f6c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.9-hfc2f54d_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rattler-build-0.62.0-h2307240_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rhash-1.4.6-h5505292_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sigtool-0.1.3-h44b9a77_0.tar.bz2 @@ -243,6 +251,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.5.4-h725018a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.9-h09917c8_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rattler-build-0.62.0-he94b42d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -255,6 +264,8 @@ environments: default: channels: - url: https://conda.anaconda.org/conda-forge/ + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -357,6 +368,8 @@ environments: dev: channels: - url: https://conda.anaconda.org/conda-forge/ + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -404,9 +417,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/patchelf-0.17.2-h58526e2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.9-hc97d973_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rattler-build-0.62.0-ha759004_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rhash-1.4.6-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda @@ -458,9 +473,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ninja-1.13.1-hdc560ac_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.2-h546c87b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/patchelf-0.17.2-h884eca8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.13.9-h4c0d347_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rattler-build-0.62.0-h1d7f6d8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rhash-1.4.6-h86ecc28_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-aarch64-2.28-h585391f_8.conda @@ -515,6 +532,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.5.4-h230baf5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.9-h17c18a5_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/rattler-build-0.62.0-hcb3c93d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h7cca4af_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/rhash-1.4.6-h6e16a3a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/sigtool-0.1.3-h88f4db0_0.tar.bz2 @@ -571,6 +589,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.4-h5503f6c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.9-hfc2f54d_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rattler-build-0.62.0-h2307240_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rhash-1.4.6-h5505292_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sigtool-0.1.3-h44b9a77_0.tar.bz2 @@ -597,6 +616,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.5.4-h725018a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.9-h09917c8_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rattler-build-0.62.0-he94b42d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -609,6 +629,8 @@ environments: pre-commit: channels: - url: https://conda.anaconda.org/conda-forge/ + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -788,6 +810,8 @@ environments: python: channels: - url: https://conda.anaconda.org/conda-forge/ + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -845,13 +869,15 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.4-py313hf6604e3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/patchelf-0.17.2-h58526e2_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.9-hc97d973_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rattler-build-0.62.0-ha759004_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rhash-1.4.6-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda @@ -915,13 +941,15 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ninja-1.13.1-hdc560ac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-2.3.4-py313h11e5ff7_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.2-h546c87b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/patchelf-0.17.2-h884eca8_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.13.9-h4c0d347_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rattler-build-0.62.0-h1d7f6d8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rhash-1.4.6-h86ecc28_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-aarch64-2.28-h585391f_8.conda @@ -992,6 +1020,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.9-h17c18a5_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/rattler-build-0.62.0-hcb3c93d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h7cca4af_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/rhash-1.4.6-h6e16a3a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/sigtool-0.1.3-h88f4db0_0.tar.bz2 @@ -1064,6 +1093,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.9-hfc2f54d_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rattler-build-0.62.0-h2307240_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rhash-1.4.6-h5505292_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sigtool-0.1.3-h44b9a77_0.tar.bz2 @@ -1115,6 +1145,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.9-h09917c8_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rattler-build-0.62.0-he94b42d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda @@ -4024,6 +4055,17 @@ packages: license_family: Apache size: 3119624 timestamp: 1759324353651 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda + sha256: c0ef482280e38c71a08ad6d71448194b719630345b0c9c60744a2010e8a8e0cb + md5: da1b85b6a87e141f5140bb9924cecab0 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + size: 3167099 + timestamp: 1775587756857 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda sha256: a24b318733c98903e2689adc7ef73448e27cbb10806852032c023f0ea4446fc5 md5: 9303e8887afe539f78517951ce25cd13 @@ -4034,6 +4076,16 @@ packages: license_family: Apache size: 3644584 timestamp: 1759326000128 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.2-h546c87b_0.conda + sha256: 348cb74c1530ac241215d047ef65d134cf797af935c97a68655319362b7e6a01 + md5: 3b129669089e4d6a5c6871dbb4669b99 + depends: + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + size: 3706406 + timestamp: 1775589602258 - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.5.4-h230baf5_0.conda sha256: 3ce8467773b2472b2919412fd936413f05a9b10c42e52c27bbddc923ef5da78a md5: 075eaad78f96bbf5835952afbe44466e @@ -4076,6 +4128,26 @@ packages: license_family: APACHE size: 62477 timestamp: 1745345660407 +- conda: https://conda.anaconda.org/conda-forge/linux-64/patchelf-0.17.2-h58526e2_0.conda + sha256: eb355ac225be2f698e19dba4dcab7cb0748225677a9799e9cc8e4cadc3cb738f + md5: ba76a6a448819560b5f8b08a9c74f415 + depends: + - libgcc-ng >=7.5.0 + - libstdcxx-ng >=7.5.0 + license: GPL-3.0-or-later + license_family: GPL + size: 94048 + timestamp: 1673473024463 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/patchelf-0.17.2-h884eca8_0.conda + sha256: 8b98158f36a7a92013a1982ab7a60947151350ac5c513c1d1575825d0fa52518 + md5: bbd8dee69c4ac2e2d07bca100b8fcc31 + depends: + - libgcc-ng >=7.5.0 + - libstdcxx-ng >=7.5.0 + license: GPL-3.0-or-later + license_family: GPL + size: 101306 + timestamp: 1673473812166 - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda sha256: 7efd51b48d908de2d75cbb3c4a2e80dd9454e1c5bb8191b261af3136f7fa5888 md5: 5c7a868f8241e64e1cf5fdf4962f23e2 @@ -4341,6 +4413,65 @@ packages: license_family: MIT size: 182043 timestamp: 1758892011955 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rattler-build-0.62.0-ha759004_0.conda + sha256: 6db9a88596f9dc7413fba19bcca5b74b6ced9b490cf11e527746b4019037fc30 + md5: e70457813c314798c8b55c4fad55b812 + depends: + - patchelf + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - openssl >=3.5.6,<4.0a0 + constrains: + - __glibc >=2.17 + license: BSD-3-Clause + size: 19003017 + timestamp: 1776080433888 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rattler-build-0.62.0-h1d7f6d8_0.conda + sha256: 81bd89c41068bad9f364be35e13a113eb6a96637e588ab3f82e3b62c10c84e41 + md5: 2a098e502d5deda8c116a2697430b15b + depends: + - patchelf + - libstdcxx >=14 + - libgcc >=14 + - openssl >=3.5.6,<4.0a0 + constrains: + - __glibc >=2.17 + license: BSD-3-Clause + size: 18687265 + timestamp: 1776080450389 +- conda: https://conda.anaconda.org/conda-forge/osx-64/rattler-build-0.62.0-hcb3c93d_0.conda + sha256: 23ec05e378d81cd1c94da1a667781b735ac37eb6708c9875a795e18fd271dd7e + md5: d0375d580c32bebfaf21208fb88bf06f + depends: + - libcxx >=19 + - __osx >=11.0 + constrains: + - __osx >=10.13 + license: BSD-3-Clause + size: 18052972 + timestamp: 1776080517117 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rattler-build-0.62.0-h2307240_0.conda + sha256: 5e6906cd3d6a3c097b1011aec9837bac2ba1d83ef09d867d56e6a6710fab2d86 + md5: 89ded9f696a563317fc0e12b88cc5551 + depends: + - __osx >=11.0 + - libcxx >=19 + constrains: + - __osx >=11.0 + license: BSD-3-Clause + size: 16519458 + timestamp: 1776080487851 +- conda: https://conda.anaconda.org/conda-forge/win-64/rattler-build-0.62.0-he94b42d_0.conda + sha256: b597ed4543ad1af4e02d263466c7cf4fb6a4949a514760c873c882c867c375e2 + md5: 61260ef9696d9c6b194751257bb66227 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: BSD-3-Clause + size: 18567666 + timestamp: 1776080517878 - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c md5: 283b96675859b20a825f8fa30f311446 diff --git a/pyproject.toml b/pyproject.toml index f3edca7cd2e..b474a6f968c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ line-length = 88 name = "ITK" channels = ["conda-forge"] platforms = ["linux-64", "linux-aarch64", "win-64", "osx-64", "osx-arm64"] +preview = ["pixi-build"] [tool.pixi.tasks] clean = { cmd = "git clean -fdx", description = "Clean the repository" } @@ -23,6 +24,7 @@ pre-commit-run = { cmd = "pre-commit run --all-files", description = "Run pre-co cmake = ">=4.0.3,<5" cxx-compiler = ">=1.10.0,<2" ninja = ">=1.13.0,<2" +rattler-build = ">=0.36,<1" [tool.pixi.feature.cxx.dependencies] From e13ad7628b2270062b3f78ab335dc653f182b01b Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Thu, 16 Apr 2026 16:45:47 -0500 Subject: [PATCH 02/22] BUG: Do not ITK-namespace external IMPORTED targets itk_module_impl() prepended ITK:: to every ${itk-module}_LIBRARIES entry lacking "::". For ITK_USE_SYSTEM_*=ON third-party modules, entries are IMPORTED targets produced by find_package (hdf5-shared, etc.) that ship under the upstream package's own naming; prepending ITK:: creates a non-existent target in INTERFACE_LINK_LIBRARIES and fails CMake's generate step. Use if(TARGET) + the IMPORTED target property to detect externally- provided targets and link them verbatim, without the backward-compat shims. ITK-owned targets retain the existing namespace and shims. --- CMake/ITKModuleMacros.cmake | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CMake/ITKModuleMacros.cmake b/CMake/ITKModuleMacros.cmake index 96ef854c4db..df9425eb0d0 100644 --- a/CMake/ITKModuleMacros.cmake +++ b/CMake/ITKModuleMacros.cmake @@ -191,6 +191,35 @@ macro(itk_module_impl) message(DEBUG "Linking ${itk-module} to namespaced dependency: ${dep}") list(APPEND _libraries "${dep}") else() + # Decide whether to apply the ITK:: namespace. + # External IMPORTED targets (e.g. hdf5-shared from find_package(HDF5) + # in module-mode, bare GDCM targets, etc.) ship under the upstream + # package's own naming and MUST NOT be renamed into the ITK:: + # namespace — doing so creates a non-existent target in the + # INTERFACE_LINK_LIBRARIES of ${itk-module}Module and fails CMake's + # generate step. ITK-owned targets (either already defined in this + # session as non-imported, or to be defined later via + # add_subdirectory into a bundled third-party) still receive the + # ITK:: namespace plus the backward-compat EXPORT_CODE shims below. + set(_itk_apply_namespace TRUE) + if(TARGET ${dep}) + get_target_property(_itk_dep_imported ${dep} IMPORTED) + if(_itk_dep_imported) + set(_itk_apply_namespace FALSE) + message( + DEBUG + "Linking ${itk-module} to external imported target: ${dep}" + ) + endif() + unset(_itk_dep_imported) + endif() + if(NOT _itk_apply_namespace) + list(APPEND _libraries "${dep}") + unset(_itk_apply_namespace) + continue() + endif() + unset(_itk_apply_namespace) + list( APPEND _libraries From d0d071f5d480388a2030ddbb8f2a9944f14f8bf4 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Thu, 16 Apr 2026 17:20:51 -0500 Subject: [PATCH 03/22] COMP: Fix hdf5_hl_cpp target-name typo in ITKHDF5_LIBRARIES Two bare-target branches of Modules/ThirdParty/HDF5/CMakeLists.txt listed hdf5_hl_cpp without the -shared / -static suffix that the sibling entries and the hdf5::hdf5_hl_cpp-* branches use. find_package does not produce a target by that name, so ITKHDF5Module's link interface referenced a non-existent ITK::hdf5_hl_cpp and CMake's generate step failed. Append the matching suffix. --- Modules/ThirdParty/HDF5/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/ThirdParty/HDF5/CMakeLists.txt b/Modules/ThirdParty/HDF5/CMakeLists.txt index 582432268ea..b6dbd0a23dc 100644 --- a/Modules/ThirdParty/HDF5/CMakeLists.txt +++ b/Modules/ThirdParty/HDF5/CMakeLists.txt @@ -40,7 +40,7 @@ endif() hdf5_cpp-shared hdf5-shared hdf5_hl-shared - hdf5_hl_cpp + hdf5_hl_cpp-shared ) elseif(TARGET hdf5::hdf5-shared) set( @@ -75,7 +75,7 @@ endif() hdf5_cpp-static hdf5-static hdf5_hl-static - hdf5_hl_cpp + hdf5_hl_cpp-static ) elseif(TARGET hdf5::hdf5-static) set( From 3014482c4ff766f88ae7ecd243d9c81a4c544046 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Thu, 16 Apr 2026 20:33:52 -0500 Subject: [PATCH 04/22] COMP: Propagate COMPILE_DEPENDS through target properties itk_module_link_dependencies() linked PUBLIC/INTERFACE/PRIVATE deps on the module's library target but not COMPILE_DEPENDS. Those deps' headers reached the module only via directory-scope include_directories in _itk_module_use_recurse(), which reads the legacy ${_INCLUDE_DIRS} variable. For third-party modules that have moved to target-based INTERFACE_INCLUDE_DIRECTORIES (e.g. Eigen3 after 4ef824bb2d), that variable is empty and compilation fails with "Eigen/Eigenvalues: No such file or directory". Also link COMPILE_DEPENDS as PRIVATE (computed as TRANSITIVE_DEPENDS minus PUBLIC_DEPENDS), letting modern CMake's target property propagation carry interface includes into the module's sources without leaking into its INTERFACE_LINK_LIBRARIES. --- CMake/ITKModuleMacros.cmake | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CMake/ITKModuleMacros.cmake b/CMake/ITKModuleMacros.cmake index df9425eb0d0..f557e61f77f 100644 --- a/CMake/ITKModuleMacros.cmake +++ b/CMake/ITKModuleMacros.cmake @@ -626,6 +626,32 @@ macro(itk_module_link_dependencies) endif() endforeach() endforeach() + + # Link COMPILE_DEPENDS as PRIVATE so this module's own compilation picks up + # transitive INTERFACE_INCLUDE_DIRECTORIES (e.g. Eigen3::Eigen via + # ITKEigen3Module) through modern CMake target properties. PRIVATE means + # the includes flow in but nothing leaks into this module's own + # INTERFACE_LINK_LIBRARIES; consumers already get these transitively via + # ${itk-module}Module's INTERFACE chain, which spans TRANSITIVE_DEPENDS + # (PUBLIC_DEPENDS + COMPILE_DEPENDS). Recover COMPILE_DEPENDS as + # TRANSITIVE_DEPENDS minus PUBLIC_DEPENDS because itk_module() unsets + # the original COMPILE_DEPENDS list. + foreach(dep IN LISTS ITK_MODULE_${itk-module}_TRANSITIVE_DEPENDS) + if(NOT dep IN_LIST ITK_MODULE_${itk-module}_PUBLIC_DEPENDS) + if( + ITK_MODULE_${dep}_LOADED + OR + TARGET + ${ITK_MODULE_${itk-module}_TARGETS_NAMESPACE}${dep}Module + ) + target_link_libraries( + ${itk-module} + PRIVATE + ${ITK_MODULE_${itk-module}_TARGETS_NAMESPACE}${dep}Module + ) + endif() + endif() + endforeach() endmacro() macro(itk_module_test) From 6f48850007d93785364d38455fc91d12873d5d73 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Thu, 16 Apr 2026 21:29:45 -0500 Subject: [PATCH 05/22] BUG: Set wrapping install COMPONENT on Python stub directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The install(DIRECTORY ${ITK_STUB_DIR}/ ...) call in Wrapping/Generators/Python/CMakeLists.txt had no COMPONENT argument, so CMake filed it under the "Unspecified" component. Conda / packaging flows that split the install across outputs (e.g. libitk runtime vs. libitk-wrapping for Python bindings) then captured all the stub-dir contents — .abi3.so Python extensions, .pyi type stubs, and the libITK*-6.0.so copies staged for Python runtime — under the runtime package instead of the wrapping package. Assign the same component the sibling install() calls use (${WRAP_ITK_INSTALL_COMPONENT_IDENTIFIER}RuntimeLibraries) so every wrapping artifact lands in one predictable component. --- Wrapping/Generators/Python/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Wrapping/Generators/Python/CMakeLists.txt b/Wrapping/Generators/Python/CMakeLists.txt index dd496c2a010..6604bb7e1b1 100644 --- a/Wrapping/Generators/Python/CMakeLists.txt +++ b/Wrapping/Generators/Python/CMakeLists.txt @@ -248,6 +248,7 @@ if(NOT EXTERNAL_WRAP_ITK_PROJECT) DIRECTORY ${ITK_STUB_DIR}/ DESTINATION ${PY_SITE_PACKAGES_PATH}/${ITK_STUB_BASENAME} + COMPONENT ${WRAP_ITK_INSTALL_COMPONENT_IDENTIFIER}RuntimeLibraries ) endif() From 5545057905cb58f7f3ea8813f35aee338aa0633a Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Thu, 16 Apr 2026 21:30:46 -0500 Subject: [PATCH 06/22] ENH: Ignore rattler-build default output/ directory rattler-build writes to ./output/ by default when --output-dir is not specified. Add it to .gitignore so developers iterating on Utilities/conda-packages/ do not accidentally stage those artifacts. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 634d6fc26c1..61545f43468 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Do not add ExternalData module staging files .ExternalData* +# condapackage uses output as the standard output directory for rattler-builds +output + # back-up files *~ *.bak From 25ff01458abb0a83b4a7f8332caaff5fb95e7b80 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Thu, 16 Apr 2026 21:52:37 -0500 Subject: [PATCH 07/22] BUG: Add ITKZLIB dep to ITKReview for macOS ARM64 link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit itkVoxBoCUBImageIO.cxx calls gzopen/gzclose/gzread/gzwrite/gztell/ gzflush directly via itk_zlib.h, but ITKReview's itk-module.cmake declares no ITKZLIB dep. Linux shared-library builds resolve the symbols transitively via undefined-at-link-time semantics; macOS ARM64 rejects that and fails the Python extension link. Add ITKZLIB under PRIVATE_DEPENDS (implementation only — no public header in ITKReview/include uses zlib). --- Modules/Nonunit/Review/itk-module.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/Nonunit/Review/itk-module.cmake b/Modules/Nonunit/Review/itk-module.cmake index d3ddf009418..ebbd24ccd16 100644 --- a/Modules/Nonunit/Review/itk-module.cmake +++ b/Modules/Nonunit/Review/itk-module.cmake @@ -10,6 +10,8 @@ itk_module( ITKReview DEPENDS ITKIOImageBase + PRIVATE_DEPENDS + ITKZLIB COMPILE_DEPENDS ITKAnisotropicSmoothing ITKAntiAlias From a046c8f65dcfee2abae7c383f10e631baf8838b7 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 04:27:04 -0500 Subject: [PATCH 08/22] WIP: TEMPORARY - cross-platform conda recipe CI (revert before merge) Temporarily add a GitHub Actions workflow (conda-recipe-test.yml) that exercises Utilities/conda-packages/recipe.yaml via rattler-build on ubuntu-22.04, windows-2022, and macos-15 matrix runners. Upload the three produced .conda files per OS as artifacts for inspection. Disable the existing ITK.Pixi and ITK.Arm64 workflows on this branch by switching their triggers to workflow_dispatch only, so CI runners focus entirely on the conda-recipe validation path. Original push / pull_request trigger blocks are preserved as comments for trivial revert. MUST BE REVERTED before this branch is opened as a ready-for-review PR to upstream. --- .github/workflows/arm.yml | 50 +++++++------ .github/workflows/conda-recipe-test.yml | 99 +++++++++++++++++++++++++ .github/workflows/pixi.yml | 50 +++++++------ 3 files changed, 153 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/conda-recipe-test.yml diff --git a/.github/workflows/arm.yml b/.github/workflows/arm.yml index c683b3c52de..f50be452692 100644 --- a/.github/workflows/arm.yml +++ b/.github/workflows/arm.yml @@ -1,29 +1,33 @@ name: ITK.Arm64 +# TEMPORARILY DISABLED on the add-conda-recipe branch — only runnable +# via manual dispatch while the conda-recipe-test workflow exercises +# the feature. Revert the trigger block below before merging. on: - push: - branches: - - main - - 'release*' - paths-ignore: - - '*.md' - - LICENSE - - NOTICE - - 'Documentation/**' - - 'Utilities/Debugger/**' - - 'Utilities/ITKv5Preparation/**' - - 'Utilities/Maintenance/**' - - 'Modules/Remote/*.remote.cmake' - pull_request: - paths-ignore: - - '*.md' - - LICENSE - - NOTICE - - 'Documentation/**' - - 'Utilities/Debugger/**' - - 'Utilities/ITKv5Preparation/**' - - 'Utilities/Maintenance/**' - - 'Modules/Remote/*.remote.cmake' + workflow_dispatch: + # push: + # branches: + # - main + # - 'release*' + # paths-ignore: + # - '*.md' + # - LICENSE + # - NOTICE + # - 'Documentation/**' + # - 'Utilities/Debugger/**' + # - 'Utilities/ITKv5Preparation/**' + # - 'Utilities/Maintenance/**' + # - 'Modules/Remote/*.remote.cmake' + # pull_request: + # paths-ignore: + # - '*.md' + # - LICENSE + # - NOTICE + # - 'Documentation/**' + # - 'Utilities/Debugger/**' + # - 'Utilities/ITKv5Preparation/**' + # - 'Utilities/Maintenance/**' + # - 'Modules/Remote/*.remote.cmake' concurrency: group: '${{ github.workflow }}@${{ github.head_ref || github.ref }}' diff --git a/.github/workflows/conda-recipe-test.yml b/.github/workflows/conda-recipe-test.yml new file mode 100644 index 00000000000..2c801f37134 --- /dev/null +++ b/.github/workflows/conda-recipe-test.yml @@ -0,0 +1,99 @@ +name: ITK.CondaRecipe.Test + +# TEMPORARY WIP workflow: validate Utilities/conda-packages/ recipe on +# Linux / Windows / macOS using rattler-build. Revert before merging the +# feature branch. + +on: + push: + branches: + - main + - 'release*' + - 'add-conda-recipe*' + pull_request: + paths: + - 'Utilities/conda-packages/**' + - 'CMake/**' + - 'Modules/ThirdParty/HDF5/**' + - 'Modules/ThirdParty/Eigen3/**' + - 'Modules/Nonunit/Review/**' + - 'Wrapping/Generators/Python/**' + - '.github/workflows/conda-recipe-test.yml' + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}@${{ github.head_ref || github.ref }}' + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + conda-recipe: + runs-on: ${{ matrix.os }} + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04, windows-2022, macos-15] + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 5 + + - name: Free disk space (Ubuntu) + if: matrix.os == 'ubuntu-22.04' + uses: BRAINSia/free-disk-space@v2 + with: + removalmode: "rmz" + swap-storage: "true" + haskell: "true" + dotnet: "true" + docker-images: "false" + tool-cache: "true" + android: "false" + large-packages: "true" + mandb: "true" + + - name: Disk space reporting (before build) + shell: bash + run: | + echo "****** df -h" + df -h + echo "****** df -h /" + df -h / + + - name: Set up Pixi + uses: prefix-dev/setup-pixi@v0.8.1 + + - name: Build conda packages (rattler-build) + shell: bash + env: + ITK_CONDA_USE_CCACHE: "0" + run: | + pixi run --environment dev rattler-build build --experimental \ + --recipe Utilities/conda-packages/recipe.yaml \ + --output-dir "${{ runner.temp }}/itk-conda-channel/" + + - name: List produced packages + if: always() + shell: bash + run: | + echo "****** channel contents" + ls -lh "${{ runner.temp }}/itk-conda-channel/"*/ 2>/dev/null \ + || ls -lh "${{ runner.temp }}/itk-conda-channel/" 2>/dev/null \ + || echo "(no channel contents found)" + + - name: Disk space reporting (after build) + if: always() + shell: bash + run: | + echo "****** df -h" + df -h + + - name: Upload conda packages + if: always() + uses: actions/upload-artifact@v4 + with: + name: itk-conda-packages-${{ matrix.os }} + path: ${{ runner.temp }}/itk-conda-channel/**/*.conda + if-no-files-found: warn + retention-days: 7 diff --git a/.github/workflows/pixi.yml b/.github/workflows/pixi.yml index 1734f5a5eb4..bfa539fad80 100644 --- a/.github/workflows/pixi.yml +++ b/.github/workflows/pixi.yml @@ -1,29 +1,33 @@ name: ITK.Pixi +# TEMPORARILY DISABLED on the add-conda-recipe branch — only runnable +# via manual dispatch while the conda-recipe-test workflow exercises +# the feature. Revert the trigger block below before merging. on: - push: - branches: - - main - - 'release*' - paths-ignore: - - '*.md' - - LICENSE - - NOTICE - - 'Documentation/**' - - 'Utilities/Debugger/**' - - 'Utilities/ITKv5Preparation/**' - - 'Utilities/Maintenance/**' - - 'Modules/Remote/*.remote.cmake' - pull_request: - paths-ignore: - - '*.md' - - LICENSE - - NOTICE - - 'Documentation/**' - - 'Utilities/Debugger/**' - - 'Utilities/ITKv5Preparation/**' - - 'Utilities/Maintenance/**' - - 'Modules/Remote/*.remote.cmake' + workflow_dispatch: + # push: + # branches: + # - main + # - 'release*' + # paths-ignore: + # - '*.md' + # - LICENSE + # - NOTICE + # - 'Documentation/**' + # - 'Utilities/Debugger/**' + # - 'Utilities/ITKv5Preparation/**' + # - 'Utilities/Maintenance/**' + # - 'Modules/Remote/*.remote.cmake' + # pull_request: + # paths-ignore: + # - '*.md' + # - LICENSE + # - NOTICE + # - 'Documentation/**' + # - 'Utilities/Debugger/**' + # - 'Utilities/ITKv5Preparation/**' + # - 'Utilities/Maintenance/**' + # - 'Modules/Remote/*.remote.cmake' concurrency: group: '${{ github.workflow }}@${{ github.head_ref || github.ref }}' From 58990a179dd2d477eb82acddb96eef77bf03ed1f Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Fri, 17 Apr 2026 04:32:22 -0500 Subject: [PATCH 09/22] COMP: Pin libcxx <22 in conda recipe for CastXML 0.7.0 compatibility CastXML 0.7.0 on conda-forge bundles Clang 20 internally, which cannot parse the __acquire_capability__ pack-expansion syntax that libc++ 22 introduced in :422. Without this pin, every wrapped ITK class fails during Python XML generation with: attribute '__acquire_capability__' does not support argument pack expansion Remove this pin when CastXML ships against Clang 22+ (or when conda-forge tightens castxml-feedstock's own libcxx upper bound). --- Utilities/conda-packages/recipe.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Utilities/conda-packages/recipe.yaml b/Utilities/conda-packages/recipe.yaml index 6f0a93dae7b..70613613865 100644 --- a/Utilities/conda-packages/recipe.yaml +++ b/Utilities/conda-packages/recipe.yaml @@ -36,6 +36,12 @@ outputs: - ${{ compiler('c') }} - ${{ compiler('cxx') }} - castxml >=0.7.0 + # castxml 0.7.0 bundles Clang 20, which cannot parse the + # __acquire_capability__ pack-expansion syntax introduced in + # libc++ 22's . Pin libc++ to <22 until a castxml release + # builds against Clang 22+. Without this, every wrapped header + # fails at mutex:422. + - libcxx <22 - if: env.get("ITK_CONDA_USE_CCACHE", default="0") == "1" then: ccache - if: build_platform != target_platform From 2db0e724bd71fce44bb04f711a3166d5a25a4e89 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 06:53:23 -0500 Subject: [PATCH 10/22] COMP: Skip ITK source-path-length check on Windows conda builds conda-forge's Windows sandbox path places CMAKE_CURRENT_SOURCE_DIR at D:\a\_temp\\bld\rattler-build_libitk_\work, which exceeds ITK's default 50-character limit on CMAKE_CURRENT_SOURCE_DIR. Configure fails with: CMake Error at CMakeLists.txt:131 (message): ITK source code directory path length is too long (69 > 50) Pass -DITK_SKIP_PATH_LENGTH_CHECKS:BOOL=ON in build.bat. The check is a fossil from MSBuild on pre-Win10 systems; modern Windows runners (GitHub Actions, conda-forge) have LongPathsEnabled=1. --- Utilities/conda-packages/build.bat | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Utilities/conda-packages/build.bat b/Utilities/conda-packages/build.bat index a589594b74c..8c2ce0b80fd 100755 --- a/Utilities/conda-packages/build.bat +++ b/Utilities/conda-packages/build.bat @@ -18,7 +18,12 @@ if "%ITK_CONDA_USE_CCACHE%"=="1" ( ) ) +REM conda-forge's Windows sandbox path (%SRC_DIR%) exceeds ITK's default +REM 50-char source-path limit. Modern Windows (including the GitHub +REM Actions and conda-forge runners) has LongPathsEnabled=1, so the +REM check is safely skipped. cmake -GNinja %CCACHE_CMAKE_ARGS% ^ + -DITK_SKIP_PATH_LENGTH_CHECKS:BOOL=ON ^ -DCMAKE_BUILD_TYPE:STRING=Release ^ -DCMAKE_INSTALL_PREFIX:PATH="%LIBRARY_PREFIX%" ^ -DCMAKE_PREFIX_PATH:PATH="%LIBRARY_PREFIX%" ^ From f49cdd39d14f3a5344207f240ef4bd22a2ba45de Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 06:54:08 -0500 Subject: [PATCH 11/22] WIP: Bump conda-recipe-test timeout to 240 min for slower runners GitHub Actions ubuntu-22.04 and macos-15 runners have 2-4 cores vs. a typical dev workstation's 16+; the first CI run reached build step 4322/4701 (~92%) before hitting the 120-min job timeout. Bump to 240 min (well within GitHub's 6-hour per-job ceiling) so Linux and macOS can finish the full build + 3-package install pipeline. Part of the temporary CI platform test; reverts with the rest of the WIP CI commit before merge. --- .github/workflows/conda-recipe-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/conda-recipe-test.yml b/.github/workflows/conda-recipe-test.yml index 2c801f37134..c39f683efc4 100644 --- a/.github/workflows/conda-recipe-test.yml +++ b/.github/workflows/conda-recipe-test.yml @@ -28,7 +28,7 @@ concurrency: jobs: conda-recipe: runs-on: ${{ matrix.os }} - timeout-minutes: 120 + timeout-minutes: 240 strategy: fail-fast: false matrix: From 35d5c4dc08603c6cdcf3cdc1764eaf868b3bb99b Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 08:35:41 -0500 Subject: [PATCH 12/22] COMP: Use imported target for system double-conversion link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modules/ThirdParty/DoubleConversion/CMakeLists.txt populated ITKDoubleConversion_LIBRARIES with get_target_property(LOCATION), which returns the library file path. On Linux that resolves to a .so which ld accepts; on Windows it resolves to the .dll, which link.exe cannot consume and which fails with: %PREFIX%\Library\bin\double-conversion.dll : fatal error LNK1107: invalid or corrupt file: cannot read at 0x300 Use the double-conversion::double-conversion imported target directly — CMake resolves it per-platform (Linux .so, Windows .lib import library) through normal target-property resolution. Also emit ITKDoubleConversion_EXPORT_CODE_INSTALL/_BUILD so that downstream consumers of the installed ITKConfig run find_package (double-conversion) before include(ITKTargets.cmake); otherwise ITKTargets.cmake's INTERFACE_LINK_LIBRARIES references a target that does not yet exist in the downstream session. --- .../DoubleConversion/CMakeLists.txt | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Modules/ThirdParty/DoubleConversion/CMakeLists.txt b/Modules/ThirdParty/DoubleConversion/CMakeLists.txt index 900829e4944..00b27435c92 100644 --- a/Modules/ThirdParty/DoubleConversion/CMakeLists.txt +++ b/Modules/ThirdParty/DoubleConversion/CMakeLists.txt @@ -11,15 +11,28 @@ mark_as_advanced(ITK_USE_SYSTEM_DOUBLECONVERSION) if(ITK_USE_SYSTEM_DOUBLECONVERSION) find_package(double-conversion 3.1.6 REQUIRED) - get_target_property( - ITKDoubleConversion_INCLUDE_DIRS - double-conversion::double-conversion - INTERFACE_INCLUDE_DIRECTORIES + # Use the imported target directly; CMake resolves it per-platform + # (Linux: .so, Windows: .lib import library). Do not use + # get_target_property(... LOCATION), which yields the .dll on Windows + # and produces LNK1107 ("invalid or corrupt file") at link time. + # INTERFACE_INCLUDE_DIRECTORIES propagates through the target, so no + # explicit ITKDoubleConversion_INCLUDE_DIRS assignment is required. + set(ITKDoubleConversion_LIBRARIES double-conversion::double-conversion) + + # Ensure downstream consumers of the installed ITKConfig have the + # double-conversion:: imported target before ITKTargets.cmake is + # processed (which references it in INTERFACE_LINK_LIBRARIES). + set( + ITKDoubleConversion_EXPORT_CODE_INSTALL + "find_package(double-conversion 3.1.6 REQUIRED)" ) - get_target_property( - ITKDoubleConversion_LIBRARIES - double-conversion::double-conversion - LOCATION + set( + ITKDoubleConversion_EXPORT_CODE_BUILD + " + if(NOT ITK_BINARY_DIR) + find_package(double-conversion 3.1.6 REQUIRED) + endif() + " ) else() set( From 88056fdfbbef227be7fba630c6bcf3dfa21750ed Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 08:37:31 -0500 Subject: [PATCH 13/22] WIP: Drop paths filter on conda-recipe-test so every push validates Core/Modules edits outside the small initial path list (e.g. a Modules/ThirdParty/DoubleConversion change) were silently skipping the cross-platform CI matrix. For a WIP platform-validation workflow we want every push to retrigger all three runners. Reverts with the rest of the WIP CI commit before merge. --- .github/workflows/conda-recipe-test.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/conda-recipe-test.yml b/.github/workflows/conda-recipe-test.yml index c39f683efc4..346f170c36b 100644 --- a/.github/workflows/conda-recipe-test.yml +++ b/.github/workflows/conda-recipe-test.yml @@ -11,14 +11,9 @@ on: - 'release*' - 'add-conda-recipe*' pull_request: - paths: - - 'Utilities/conda-packages/**' - - 'CMake/**' - - 'Modules/ThirdParty/HDF5/**' - - 'Modules/ThirdParty/Eigen3/**' - - 'Modules/Nonunit/Review/**' - - 'Wrapping/Generators/Python/**' - - '.github/workflows/conda-recipe-test.yml' + # Intentionally no paths filter — this is the temporary CI + # platform matrix; we want every push to rerun Linux/Windows/ + # macOS so any core or Modules/ change is validated. workflow_dispatch: concurrency: From 35b74167ac0f48b110a79117e4f3fcee74e5530a Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 11:22:30 -0500 Subject: [PATCH 14/22] COMP: Normalize path separators in Python wrapping install warning Wrapping/Generators/Python/CMakeLists.txt interpolated ${CMAKE_INSTALL_PREFIX} and ${py_prefix} directly into an install(CODE "message(WARNING \"...\")") string. On Windows those variables contain backslash paths; CMake re-parses the generated cmake_install.cmake with escape-sequence interpretation active and rejects "\a" inside the path as an invalid C escape: CMake Error at build/Wrapping/Generators/Python/cmake_install.cmake:36 (message): Invalid character escape '\\a'. Run the two paths through file(TO_CMAKE_PATH ...) before interpolation so the embedded strings use forward slashes. No-op on Unix; required on Windows / conda-forge / rattler-build sandboxes where CMAKE_INSTALL_PREFIX lives under paths like D:\\a\\_temp\\.... --- Wrapping/Generators/Python/CMakeLists.txt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Wrapping/Generators/Python/CMakeLists.txt b/Wrapping/Generators/Python/CMakeLists.txt index 6604bb7e1b1..db4ff6defa2 100644 --- a/Wrapping/Generators/Python/CMakeLists.txt +++ b/Wrapping/Generators/Python/CMakeLists.txt @@ -61,12 +61,23 @@ if(Python3_EXECUTABLE AND NOT PY_SITE_PACKAGES_PATH) ERROR_VARIABLE py_prefix OUTPUT_STRIP_TRAILING_WHITESPACE ) - if(NOT "${CMAKE_INSTALL_PREFIX}" STREQUAL "${py_prefix}") + # Normalize both paths to forward slashes before embedding them into + # the generated install script. On Windows, CMAKE_INSTALL_PREFIX is a + # backslash path; the generated cmake_install.cmake re-parses the + # message() call with escape-sequence interpretation active and + # treats e.g. "\a" inside the path as an invalid C escape, failing + # install with "Invalid character escape '\\a'". file(TO_CMAKE_PATH) + # is a no-op on Unix where paths are already forward-slash. + file(TO_CMAKE_PATH "${CMAKE_INSTALL_PREFIX}" _cip_fwd) + file(TO_CMAKE_PATH "${py_prefix}" _pyp_fwd) + if(NOT "${_cip_fwd}" STREQUAL "${_pyp_fwd}") install( CODE - "message(WARNING \"CMAKE_INSTALL_PREFIX, ${CMAKE_INSTALL_PREFIX}, does not match Python's prefix, ${py_prefix}\")" + "message(WARNING \"CMAKE_INSTALL_PREFIX, ${_cip_fwd}, does not match Python's prefix, ${_pyp_fwd}\")" ) endif() + unset(_cip_fwd) + unset(_pyp_fwd) string(REGEX REPLACE "\n" "" py_spp_no_newline "${py_spp}") file(TO_CMAKE_PATH "${py_spp_no_newline}" py_spp_nobackslashes) From 13b374e190ef18a4c17f8e197337213bf68af2c0 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 11:22:46 -0500 Subject: [PATCH 15/22] ENH: Pin ITK_USE_SYSTEM_* conda-forge deps to tested ranges conda-forge packages advance at roughly monthly cadence while ITK's vendored third-party copies advance roughly annually. An unpinned recipe picks up the newest conda-forge version at build time, so an HDF5 2.0 / TBB 2023 / Eigen 3.5 upload silently breaks the next build. Pin each ITK_USE_SYSTEM_*-eligible dep in the build-cache host: block with floor = ITK's vendored version and ceiling = next expected ABI break. Apply matching pins to libitk.run: and libitk-devel.run:; tbb ceiling reflects the oneTBB 2022 stable line pre-dating the 2023 rewrite. Python floor raised to 3.10. Keep the conda-forge zlib / expat metapackages (not the bare libzlib / libexpat); the metapackages ship CMake find_package compatibility glue that the bare libs lack, and dropping them caused find_package(ZLIB) to fall back to the host system libz (1.2.11). Add numpy to host: explicitly. FindPython3 COMPONENTS NumPy needs its headers at configure time; earlier builds picked numpy up transitively, but make the dep explicit so a solver change cannot drop it. --- Utilities/conda-packages/recipe.yaml | 76 ++++++++++++++++++---------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/Utilities/conda-packages/recipe.yaml b/Utilities/conda-packages/recipe.yaml index 70613613865..767fe0fbc23 100644 --- a/Utilities/conda-packages/recipe.yaml +++ b/Utilities/conda-packages/recipe.yaml @@ -30,12 +30,18 @@ outputs: then: bash Utilities/conda-packages/build.sh else: call Utilities\conda-packages\build.bat requirements: + # Version pin policy: floors match ITK's vendored third-party + # versions (Modules/ThirdParty/*/src/); ceilings stop at the + # next expected ABI / API break (major version). conda-forge + # packages move faster than ITK's vendored copies, so explicit + # ranges are required to keep builds deterministic as upstream + # packages advance. Revisit at each ITK release. build: - - cmake >=3.16.3 + - cmake >=3.22.1,<5 - ninja - ${{ compiler('c') }} - ${{ compiler('cxx') }} - - castxml >=0.7.0 + - castxml >=0.7.0,<0.8 # castxml 0.7.0 bundles Clang 20, which cannot parse the # __acquire_capability__ pack-expansion syntax introduced in # libc++ 22's . Pin libc++ to <22 until a castxml release @@ -45,21 +51,32 @@ outputs: - if: env.get("ITK_CONDA_USE_CCACHE", default="0") == "1" then: ccache - if: build_platform != target_platform - then: python + then: python >=3.10,<3.15 host: + # Use conda-forge's "compat" metapackages expat/zlib (not the + # concrete libexpat/libzlib). The metapackages pull in the + # lib* AND ship the CMake compat integration that find_package + # (ZLIB) and find_package(EXPAT) use; switching to the bare lib* + # was tried and caused CMake to fall back to the host system + # zlib (1.2.11) and break the configure. - if: unix then: - - expat - - libpng - - zlib - - fftw - - hdf5 - - libjpeg-turbo - - libtiff - - eigen - - double-conversion - - tbb-devel - - python + - expat >=2.5,<3 + - libpng >=1.6.43,<1.7 + - zlib >=1.2.13,<2 + - fftw >=3.3.10,<3.4 + - hdf5 >=1.14.5,<2 + - libjpeg-turbo >=3.0,<4 + - libtiff >=4.6,<5 + - eigen >=3.4,<3.5 + - double-conversion >=3.3,<4 + - tbb-devel >=2022.2.0,<2023 + - python >=3.10,<3.15 + # numpy headers are required by FindPython3 COMPONENTS NumPy + # during the Python-wrapping configure. Earlier builds picked it + # up transitively; make the dep explicit so a solver change + # cannot drop it. + - numpy source: path: ../.. @@ -75,17 +92,20 @@ outputs: else: call Utilities\conda-packages\libitk_install.bat requirements: run: + # Most of these are already carried by run_exports from the + # host pins above, but being explicit keeps the recipe + # self-documenting and independent of feedstock behavior. - if: unix then: - - expat - - libpng - - zlib - - fftw - - hdf5 - - libjpeg-turbo - - libtiff - - double-conversion - - tbb + - expat >=2.5,<3 + - libpng >=1.6.43,<1.7 + - zlib >=1.2.13,<2 + - fftw >=3.3.10,<3.4 + - hdf5 >=1.14.5,<2 + - libjpeg-turbo >=3.0,<4 + - libtiff >=4.6,<5 + - double-conversion >=3.3,<4 + - tbb >=2022.2.0,<2023 tests: - script: - if: osx @@ -112,9 +132,9 @@ outputs: - ${{ pin_subpackage('libitk', exact=True) }} run: - ${{ pin_subpackage('libitk', exact=True) }} - - tbb-devel - - cmake - - eigen + - tbb-devel >=2022.2.0,<2023 + - cmake >=3.22.1,<5 + - eigen >=3.4,<3.5 tests: - script: - if: unix @@ -152,10 +172,10 @@ outputs: requirements: host: - ${{ pin_subpackage('libitk', exact=True) }} - - python >=3.9 + - python >=3.10,<3.15 run: - ${{ pin_subpackage('libitk', exact=True) }} - - python >=3.9 + - python >=3.10,<3.15 - numpy tests: - python: From 1be14b65b36b7dd5fd235e8c0063a2a0ccc58ce5 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 14:14:30 -0500 Subject: [PATCH 16/22] COMP: Drop Module_ITKDeprecated from conda build Windows cl /DLL produces no .lib import library for a shared library with no exported symbols. ITKDeprecated ships only a placeholder .cxx without an ITK_EXPORT-decorated symbol, so the Windows build links ITKDeprecated-6.0.dll but no ITKDeprecated-6.0.lib, leaving ITKTargets.cmake referencing a file that does not exist. The libitk-devel rattler-build test then fails find_package(ITK) with: The imported target "ITK::ITKDeprecated" references the file ".../Library/lib/ITKDeprecated-6.0.lib" but this file does not exist Module_ITKDeprecated is optional and default-off; downstream consumers that need the deprecated compatibility surface can enable it in their own builds. Dropping it from the conda recipe avoids shipping a broken import target on Windows while keeping Linux/macOS parity. --- Utilities/conda-packages/build.bat | 1 - Utilities/conda-packages/build.sh | 1 - 2 files changed, 2 deletions(-) diff --git a/Utilities/conda-packages/build.bat b/Utilities/conda-packages/build.bat index 8c2ce0b80fd..d935b5b9219 100755 --- a/Utilities/conda-packages/build.bat +++ b/Utilities/conda-packages/build.bat @@ -37,7 +37,6 @@ cmake -GNinja %CCACHE_CMAKE_ARGS% ^ -DModule_ITKTBB:BOOL=ON ^ -DModule_MGHIO:BOOL=ON ^ -DModule_ITKIOTransformInsightLegacy:BOOL=ON ^ - -DModule_ITKDeprecated:BOOL=ON ^ -DITK_USE_SYSTEM_FFTW:BOOL=ON ^ -DITK_USE_SYSTEM_HDF5:BOOL=ON ^ -DITK_USE_SYSTEM_JPEG:BOOL=ON ^ diff --git a/Utilities/conda-packages/build.sh b/Utilities/conda-packages/build.sh index 52b0222c695..26d0cc0bc7e 100755 --- a/Utilities/conda-packages/build.sh +++ b/Utilities/conda-packages/build.sh @@ -94,7 +94,6 @@ cmake ${CROSS_CMAKE_ARGS} ${CCACHE_CMAKE_ARGS} ${ITK_CONDA_EXTRA_CMAKE_ARGS:-} \ -DModule_ITKTBB:BOOL=${use_tbb} \ -DModule_MGHIO:BOOL=ON \ -DModule_ITKIOTransformInsightLegacy:BOOL=ON \ - -DModule_ITKDeprecated:BOOL=ON \ -DITK_USE_SYSTEM_EXPAT:BOOL=ON \ -DITK_USE_SYSTEM_FFTW:BOOL=ON \ -DITK_USE_SYSTEM_HDF5:BOOL=ON \ From 13eb5d4004d9f21f3c96ed29cece087d7923a596 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 15:29:50 -0500 Subject: [PATCH 17/22] DOC: Document local rattler-build invocation and stale package cache fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two sections to README_Advanced.md: 1. "Local build invocation" — shows the correct way to call rattler-build locally (the dev pixi env does not bundle it; use the rattler cache path or pixi global install). 2. "Stale rattler package cache" — explains the hash mismatch error that occurs on repeated local builds and gives the one-line workaround: rm -rf ~/.cache/rattler/cache/pkgs/libitk-* Root cause documented: the staging cache key includes build-environment package hashes, which change as conda-forge updates packages between runs, forcing a new libitk build with a different hash each time. --- Utilities/conda-packages/README_Advanced.md | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Utilities/conda-packages/README_Advanced.md b/Utilities/conda-packages/README_Advanced.md index 6f1da1ec561..0a72f8e5114 100644 --- a/Utilities/conda-packages/README_Advanced.md +++ b/Utilities/conda-packages/README_Advanced.md @@ -3,6 +3,57 @@ Advanced / opt-in features of the `Utilities/conda-packages/` build. The baseline workflow is in [`README.md`](./README.md); read that first. +## Local build invocation + +The `dev` pixi environment does not bundle `rattler-build`. Use the copy +that rattler itself caches under `~/.cache/rattler/pkgs/`: + +```bash +# Find the rattler-build binary (version may differ) +RATTLER=$(ls ~/.cache/rattler/pkgs/rattler-build-*/bin/rattler-build | sort -V | tail -1) + +# Verify +$RATTLER --version + +# Build (add ccache vars if desired — see below) +ITK_CONDA_USE_CCACHE=1 CCACHE_DIR=$HOME/.ccache \ +$RATTLER build --experimental \ + --recipe Utilities/conda-packages/recipe.yaml \ + --output-dir ~/my-itk-channel/ +``` + +Alternatively, install rattler-build as a pixi global tool: + +```bash +pixi global install rattler-build +rattler-build --version +``` + +## Stale rattler package cache — hash mismatch errors + +Each `rattler-build` run may produce a `libitk` package with a different +build hash (timestamps, environment hash). If a previous `libitk` is +cached in `~/.cache/rattler/cache/pkgs/`, subsequent builds fail with: + +``` +hash mismatch, wanted … hash A … but the cached package has hash B +``` + +**Workaround — clear the stale cache before re-running:** + +```bash +rm -rf ~/.cache/rattler/cache/pkgs/libitk-* +``` + +This forces rattler to re-fetch `libitk` from the local channel on the +next run. The compiler cache (ccache) is not affected. + +**Root cause:** rattler-build's staging cache key includes the resolved +build-environment package hashes. When conda-forge updates a build +dependency between runs, the staging cache misses and a new `libitk` +is produced with a different hash. The rattler package cache then holds +a stale copy that no longer matches the channel's repodata.json. + ## ccache — compiler caching across rattler-build runs Rattler-build creates a fresh work directory (`rattler-build_libitk_/`) From b0dea5a44acbef9c3583345f7ec6f772b8b70436 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Apr 2026 16:57:34 -0500 Subject: [PATCH 18/22] COMP: Install GDCM DebugDevel component in libitk-devel package On Windows, the GDCM-bundled jpeg libraries (gdcmjpeg8/12/16) install their static .lib archives under the "DebugDevel" CMake component, not "Development". ITKTargets.cmake references these .lib files, so without this component the downstream find_package(ITK) test fails: CMake Error at ITKTargets.cmake:2172: The imported target "ITK::gdcmjpeg8" references the file "%PREFIX%/Library/lib/itkgdcmjpeg8-6.0.lib" but this file does not exist. Add DebugDevel to both install scripts for cross-platform consistency. On Linux/macOS the component produces no output (gdcmjpeg static archives are not exported there), so it is a safe no-op on those platforms. --- Utilities/conda-packages/libitk-devel_install.bat | 6 ++++++ Utilities/conda-packages/libitk-devel_install.sh | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/Utilities/conda-packages/libitk-devel_install.bat b/Utilities/conda-packages/libitk-devel_install.bat index 528d1b909f0..fd34eea1405 100755 --- a/Utilities/conda-packages/libitk-devel_install.bat +++ b/Utilities/conda-packages/libitk-devel_install.bat @@ -4,3 +4,9 @@ cmake -DCOMPONENT=Development -P "%BUILD_DIR%\cmake_install.cmake" if errorlevel 1 exit 1 cmake -DCOMPONENT=Headers -P "%BUILD_DIR%\cmake_install.cmake" if errorlevel 1 exit 1 +REM GDCM bundles static jpeg libs (gdcmjpeg8/12/16) whose ARCHIVE rules +REM install under the "DebugDevel" component. ITKTargets.cmake references +REM these .lib files, so they must be present for downstream find_package +REM to succeed on Windows. +cmake -DCOMPONENT=DebugDevel -P "%BUILD_DIR%\cmake_install.cmake" +if errorlevel 1 exit 1 diff --git a/Utilities/conda-packages/libitk-devel_install.sh b/Utilities/conda-packages/libitk-devel_install.sh index b4957361a89..ab8ed3f4a8e 100755 --- a/Utilities/conda-packages/libitk-devel_install.sh +++ b/Utilities/conda-packages/libitk-devel_install.sh @@ -3,3 +3,8 @@ set -ex BUILD_DIR="${SRC_DIR}/build" cmake -DCOMPONENT=Development -P "${BUILD_DIR}/cmake_install.cmake" cmake -DCOMPONENT=Headers -P "${BUILD_DIR}/cmake_install.cmake" +# GDCM bundles static jpeg libs (gdcmjpeg8/12/16) whose ARCHIVE rules +# install under the "DebugDevel" component. ITKTargets.cmake references +# these .lib files on Windows, so include DebugDevel for cross-platform +# consistency (no-op on Linux/macOS where no static .a is installed here). +cmake -DCOMPONENT=DebugDevel -P "${BUILD_DIR}/cmake_install.cmake" From 72d1664491974c8396f25176079bc87ae9742ae8 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Sat, 18 Apr 2026 05:54:01 -0500 Subject: [PATCH 19/22] COMP: Fix Windows libitk-devel test Release/Debug CRT mismatch The libitk-devel test cmake configure used -GNinja without -DCMAKE_BUILD_TYPE=Release. For single-config generators, the cmake --build --config Release flag is ignored, so MSVC compiled main.cxx with /MDd (Debug CRT / _ITERATOR_DEBUG_LEVEL=2) while the installed VNL static libraries were built with /MD (Release). This caused LNK2038 CRT mismatch errors on all 42 link units. Fix: pass -DCMAKE_BUILD_TYPE=Release to cmake configure on Windows so the test links against the same Release libraries that are installed. --- Utilities/conda-packages/recipe.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Utilities/conda-packages/recipe.yaml b/Utilities/conda-packages/recipe.yaml index 767fe0fbc23..7eafebb7804 100644 --- a/Utilities/conda-packages/recipe.yaml +++ b/Utilities/conda-packages/recipe.yaml @@ -156,8 +156,8 @@ outputs: cmake --build . else: | mkdir build-test && cd build-test - cmake -GNinja -DCMAKE_PREFIX_PATH:PATH="%PREFIX%" ..\Utilities\conda-packages\tests\example - cmake --build . --config Release + cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH:PATH="%PREFIX%" ..\Utilities\conda-packages\tests\example + cmake --build . - package: name: libitk-wrapping From dd551393f565d2489f072f146b683f12b6d75e4b Mon Sep 17 00:00:00 2001 From: Cavan Riley Date: Sun, 19 Apr 2026 14:49:22 -0500 Subject: [PATCH 20/22] ENH: Install ThirdParty component in conda libitk-devel package Add a cmake_install.cmake invocation for the ThirdParty component to both the Unix shell and Windows batch install scripts for libitk-devel. Third-party headers and config files vendored into the ITK build tree are part of the development package's interface and are required for downstream projects that include ITK headers directly. --- Utilities/conda-packages/libitk-devel_install.bat | 2 ++ Utilities/conda-packages/libitk-devel_install.sh | 1 + 2 files changed, 3 insertions(+) diff --git a/Utilities/conda-packages/libitk-devel_install.bat b/Utilities/conda-packages/libitk-devel_install.bat index fd34eea1405..d90ea76038e 100755 --- a/Utilities/conda-packages/libitk-devel_install.bat +++ b/Utilities/conda-packages/libitk-devel_install.bat @@ -10,3 +10,5 @@ REM these .lib files, so they must be present for downstream find_package REM to succeed on Windows. cmake -DCOMPONENT=DebugDevel -P "%BUILD_DIR%\cmake_install.cmake" if errorlevel 1 exit 1 +cmake -DCOMPONENT=ThirdParty -P "%BUILD_DIR%\cmake_install.cmake" +if errorlevel 1 exit 1 diff --git a/Utilities/conda-packages/libitk-devel_install.sh b/Utilities/conda-packages/libitk-devel_install.sh index ab8ed3f4a8e..faba11363cd 100755 --- a/Utilities/conda-packages/libitk-devel_install.sh +++ b/Utilities/conda-packages/libitk-devel_install.sh @@ -8,3 +8,4 @@ cmake -DCOMPONENT=Headers -P "${BUILD_DIR}/cmake_install.cmake" # these .lib files on Windows, so include DebugDevel for cross-platform # consistency (no-op on Linux/macOS where no static .a is installed here). cmake -DCOMPONENT=DebugDevel -P "${BUILD_DIR}/cmake_install.cmake" +cmake -DCOMPONENT=ThirdParty -P "${BUILD_DIR}/cmake_install.cmake" From 120d2fc9f6325c07c73327f91185b542f84327b1 Mon Sep 17 00:00:00 2001 From: Cavan Riley Date: Sun, 19 Apr 2026 14:49:43 -0500 Subject: [PATCH 21/22] ENH: Generate relocatable cmake_install.cmake in conda libitk-wrapping ITKPythonPackage drives installation using PythonWheelRuntimeLibraries component names, but the conda libitk-wrapping package builds with WRAP_ITK_INSTALL_COMPONENT_IDENTIFIER=PythonWrapping, producing a different component suffix. Generate a shim cmake_install.cmake in the ITK cmake config directory at conda package install time. The shim maps each PythonWheel* component request to files already installed under the conda prefix and derives the prefix from CMAKE_CURRENT_LIST_FILE so it is fully relocatable. On Windows, the bat script uses a temporary PowerShell file to write the shim, adapting for .pyd extensions and the conda Lib/site-packages layout. --- .../libitk-wrapping_install.bat | 111 ++++++++++++++++++ .../conda-packages/libitk-wrapping_install.sh | 103 ++++++++++++++++ 2 files changed, 214 insertions(+) diff --git a/Utilities/conda-packages/libitk-wrapping_install.bat b/Utilities/conda-packages/libitk-wrapping_install.bat index 62fdebcaea0..fcb9ebf0bd6 100755 --- a/Utilities/conda-packages/libitk-wrapping_install.bat +++ b/Utilities/conda-packages/libitk-wrapping_install.bat @@ -7,3 +7,114 @@ rem runtime by building with -DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER=PythonWrapp rem which directs all wrapping install() calls to this dedicated component. cmake -DCOMPONENT=PythonWrappingRuntimeLibraries -P "%BUILD_DIR%\cmake_install.cmake" if errorlevel 1 exit 1 + +rem Generate a relocatable cmake_install.cmake in the ITK cmake config directory. +rem ITKPythonPackage calls include("${ITK_BINARY_DIR}/cmake_install.cmake") with +rem COMPONENT set to e.g. "ITKCommonPythonWheelRuntimeLibraries". This shim maps +rem those PythonWheel* names to files already installed under %PREFIX%. +rem On Windows, conda Python packages live at %PREFIX%\Lib\site-packages (one +rem level above %LIBRARY_PREFIX%) and extension modules use .pyd instead of .so. + +for /d %%i in ("%LIBRARY_PREFIX%\lib\cmake\ITK-*") do set ITK_CMAKE_DIR=%%i +if "%ITK_CMAKE_DIR%"=="" ( + echo ERROR: could not find ITK cmake config dir under %LIBRARY_PREFIX%\lib\cmake 1>&2 + exit 1 +) + +rem Write the cmake_install.cmake via a temporary PowerShell script. +rem Using a PS1 temp file avoids cmd escaping issues with parentheses and +rem double quotes that appear throughout the cmake script content. +set GEN_PS1=%TEMP%\gen_itk_cmake_install_%RANDOM%.ps1 + +echo $dest = '%ITK_CMAKE_DIR%\cmake_install.cmake' > "%GEN_PS1%" +echo $content = @' >> "%GEN_PS1%" +echo # Relocatable cmake_install.cmake for conda-installed ITK (Windows). >> "%GEN_PS1%" +echo # Generated by ITKPythonPackage conda recipe (libitk-wrapping_install.bat). >> "%GEN_PS1%" +echo # Maps ITKPythonPackage's PythonWheelRuntimeLibraries component names to the >> "%GEN_PS1%" +echo # pre-installed files in the conda prefix. >> "%GEN_PS1%" +echo. >> "%GEN_PS1%" +echo # On Windows the layout is: >> "%GEN_PS1%" +echo # ^/lib/cmake/ITK-x.y/cmake_install.cmake >> "%GEN_PS1%" +echo # where LIBRARY_PREFIX = PREFIX/Library, so walk up 4 levels to reach PREFIX. >> "%GEN_PS1%" +echo get_filename_component(_ipp_itk_cmake_dir "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) >> "%GEN_PS1%" +echo get_filename_component(_ipp_cmake_dir "${_ipp_itk_cmake_dir}" DIRECTORY) >> "%GEN_PS1%" +echo get_filename_component(_ipp_lib_dir "${_ipp_cmake_dir}" DIRECTORY) >> "%GEN_PS1%" +echo get_filename_component(_ipp_library_prefix "${_ipp_lib_dir}" DIRECTORY) >> "%GEN_PS1%" +echo get_filename_component(_ipp_conda_prefix "${_ipp_library_prefix}" DIRECTORY) >> "%GEN_PS1%" +echo. >> "%GEN_PS1%" +echo if(DEFINED CMAKE_INSTALL_COMPONENT AND NOT CMAKE_INSTALL_COMPONENT STREQUAL "") >> "%GEN_PS1%" +echo set(_ipp_component "${CMAKE_INSTALL_COMPONENT}") >> "%GEN_PS1%" +echo elseif(DEFINED COMPONENT AND NOT COMPONENT STREQUAL "") >> "%GEN_PS1%" +echo set(_ipp_component "${COMPONENT}") >> "%GEN_PS1%" +echo else() >> "%GEN_PS1%" +echo return() >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo. >> "%GEN_PS1%" +echo if(NOT _ipp_component MATCHES "PythonWheelRuntimeLibraries$") >> "%GEN_PS1%" +echo return() >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo. >> "%GEN_PS1%" +echo # "ITKCommonPythonWheelRuntimeLibraries" -^> "ITKCommon" >> "%GEN_PS1%" +echo string(REGEX REPLACE "PythonWheelRuntimeLibraries$" "" _ipp_module "${_ipp_component}") >> "%GEN_PS1%" +echo. >> "%GEN_PS1%" +echo # Global package files (installed once via sentinel) >> "%GEN_PS1%" +echo set(_ipp_sentinel "${CMAKE_INSTALL_PREFIX}/itk/.conda_global_done") >> "%GEN_PS1%" +echo if(NOT EXISTS "${_ipp_sentinel}") >> "%GEN_PS1%" +echo file(GLOB _g "${_ipp_conda_prefix}/Lib/site-packages/itkConfig.py") >> "%GEN_PS1%" +echo if(_g) >> "%GEN_PS1%" +echo file(INSTALL ${_g} DESTINATION "${CMAKE_INSTALL_PREFIX}" USE_SOURCE_PERMISSIONS) >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo file(GLOB _g "${_ipp_conda_prefix}/Lib/site-packages/itk/__init__.py") >> "%GEN_PS1%" +echo if(_g) >> "%GEN_PS1%" +echo file(INSTALL ${_g} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk" USE_SOURCE_PERMISSIONS) >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo # itk/support/ -- pure-Python API layer (lazy loading, template dispatch, etc.) >> "%GEN_PS1%" +echo file(GLOB _support_dir LIST_DIRECTORIES true >> "%GEN_PS1%" +echo "${_ipp_conda_prefix}/Lib/site-packages/itk/support" >> "%GEN_PS1%" +echo ) >> "%GEN_PS1%" +echo if(_support_dir) >> "%GEN_PS1%" +echo file(INSTALL ${_support_dir} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk" USE_SOURCE_PERMISSIONS) >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo file(GLOB _g "${_ipp_conda_prefix}/Lib/site-packages/itk/Configuration/__init__.py") >> "%GEN_PS1%" +echo if(_g) >> "%GEN_PS1%" +echo file(INSTALL ${_g} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk/Configuration" USE_SOURCE_PERMISSIONS) >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo file(WRITE "${_ipp_sentinel}" "done\n") >> "%GEN_PS1%" +echo message(STATUS "conda cmake_install: installed global itk package files") >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo. >> "%GEN_PS1%" +echo # C extension (.pyd) >> "%GEN_PS1%" +echo file(GLOB _ext >> "%GEN_PS1%" +echo "${_ipp_conda_prefix}/Lib/site-packages/itk/_${_ipp_module}Python.pyd" >> "%GEN_PS1%" +echo ) >> "%GEN_PS1%" +echo if(_ext) >> "%GEN_PS1%" +echo file(INSTALL ${_ext} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk" USE_SOURCE_PERMISSIONS) >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo. >> "%GEN_PS1%" +echo # Python wrapper .py >> "%GEN_PS1%" +echo file(GLOB _py >> "%GEN_PS1%" +echo "${_ipp_conda_prefix}/Lib/site-packages/itk/${_ipp_module}Python.py" >> "%GEN_PS1%" +echo ) >> "%GEN_PS1%" +echo if(_py) >> "%GEN_PS1%" +echo file(INSTALL ${_py} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk" USE_SOURCE_PERMISSIONS) >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo. >> "%GEN_PS1%" +echo # Configuration files >> "%GEN_PS1%" +echo file(GLOB _cfg >> "%GEN_PS1%" +echo "${_ipp_conda_prefix}/Lib/site-packages/itk/Configuration/${_ipp_module}Config.py" >> "%GEN_PS1%" +echo "${_ipp_conda_prefix}/Lib/site-packages/itk/Configuration/${_ipp_module}_snake_case.py" >> "%GEN_PS1%" +echo ) >> "%GEN_PS1%" +echo if(_cfg) >> "%GEN_PS1%" +echo file(INSTALL ${_cfg} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk/Configuration" USE_SOURCE_PERMISSIONS) >> "%GEN_PS1%" +echo endif() >> "%GEN_PS1%" +echo. >> "%GEN_PS1%" +echo list(LENGTH _ext _ipp_n) >> "%GEN_PS1%" +echo message(STATUS "conda cmake_install: ${_ipp_component}: ${_ipp_n} .pyd + python files installed") >> "%GEN_PS1%" +echo '@ >> "%GEN_PS1%" +echo [System.IO.File]::WriteAllText($dest, $content) >> "%GEN_PS1%" + +powershell -NoProfile -ExecutionPolicy Bypass -File "%GEN_PS1%" +if errorlevel 1 exit 1 +del "%GEN_PS1%" + +echo Generated %ITK_CMAKE_DIR%\cmake_install.cmake diff --git a/Utilities/conda-packages/libitk-wrapping_install.sh b/Utilities/conda-packages/libitk-wrapping_install.sh index b0384befa59..3c165b9e789 100755 --- a/Utilities/conda-packages/libitk-wrapping_install.sh +++ b/Utilities/conda-packages/libitk-wrapping_install.sh @@ -7,3 +7,106 @@ BUILD_DIR="${SRC_DIR}/build" # runtime by building with -DWRAP_ITK_INSTALL_COMPONENT_IDENTIFIER=PythonWrapping, # which directs all wrapping install() calls to this dedicated component. cmake -DCOMPONENT=PythonWrappingRuntimeLibraries -P "${BUILD_DIR}/cmake_install.cmake" + +# Generate a relocatable cmake_install.cmake and place it in the ITK cmake +# config directory. ITKPythonPackage calls: +# include("${ITK_BINARY_DIR}/cmake_install.cmake") +# with COMPONENT set to e.g. "ITKCommonPythonWheelRuntimeLibraries". +# Since this conda package uses WRAP_ITK_INSTALL_COMPONENT_IDENTIFIER=PythonWrapping +# (different suffix), the build's cmake_install.cmake won't match. This +# generated file bridges the gap: it maps the PythonWheel* component names that +# ITKPythonPackage expects to the files already installed under ${PREFIX}. +# The file uses CMAKE_CURRENT_LIST_DIR to derive the prefix at cmake time, +# making it fully relocatable across machines and prefix paths. + +ITK_CMAKE_DIR=$(find "${PREFIX}/lib/cmake" -maxdepth 1 -name "ITK-*" -type d | head -1) +if [ -z "${ITK_CMAKE_DIR}" ]; then + echo "ERROR: could not find ITK cmake config dir under ${PREFIX}/lib/cmake" >&2 + exit 1 +fi + +cat > "${ITK_CMAKE_DIR}/cmake_install.cmake" <<'CMAKE_EOF' +# Relocatable cmake_install.cmake for conda-installed ITK. +# Generated by ITKPythonPackage conda recipe (libitk-wrapping_install.sh). +# Maps ITKPythonPackage's PythonWheelRuntimeLibraries component names to the +# pre-installed files in the conda prefix. + +# Derive the conda prefix from this file's own location: +# /lib/cmake/ITK-x.y/cmake_install.cmake +get_filename_component(_ipp_itk_cmake_dir "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) +get_filename_component(_ipp_cmake_dir "${_ipp_itk_cmake_dir}" DIRECTORY) +get_filename_component(_ipp_lib_dir "${_ipp_cmake_dir}" DIRECTORY) +get_filename_component(_ipp_conda_prefix "${_ipp_lib_dir}" DIRECTORY) + +if(DEFINED CMAKE_INSTALL_COMPONENT AND NOT CMAKE_INSTALL_COMPONENT STREQUAL "") + set(_ipp_component "${CMAKE_INSTALL_COMPONENT}") +elseif(DEFINED COMPONENT AND NOT COMPONENT STREQUAL "") + set(_ipp_component "${COMPONENT}") +else() + return() +endif() + +if(NOT _ipp_component MATCHES "PythonWheelRuntimeLibraries$") + return() +endif() + +# "ITKCommonPythonWheelRuntimeLibraries" -> "ITKCommon" +string(REGEX REPLACE "PythonWheelRuntimeLibraries$" "" _ipp_module "${_ipp_component}") + +# Global package files (installed once via sentinel) +set(_ipp_sentinel "${CMAKE_INSTALL_PREFIX}/itk/.conda_global_done") +if(NOT EXISTS "${_ipp_sentinel}") + file(GLOB _g "${_ipp_conda_prefix}/lib/python*/site-packages/itkConfig.py") + if(_g) + file(INSTALL ${_g} DESTINATION "${CMAKE_INSTALL_PREFIX}" USE_SOURCE_PERMISSIONS) + endif() + file(GLOB _g "${_ipp_conda_prefix}/lib/python*/site-packages/itk/__init__.py") + if(_g) + file(INSTALL ${_g} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk" USE_SOURCE_PERMISSIONS) + endif() + # itk/support/ — pure-Python API layer (lazy loading, template dispatch, etc.) + file(GLOB _support_dir LIST_DIRECTORIES true + "${_ipp_conda_prefix}/lib/python*/site-packages/itk/support" + ) + if(_support_dir) + file(INSTALL ${_support_dir} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk" USE_SOURCE_PERMISSIONS) + endif() + file(GLOB _g "${_ipp_conda_prefix}/lib/python*/site-packages/itk/Configuration/__init__.py") + if(_g) + file(INSTALL ${_g} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk/Configuration" USE_SOURCE_PERMISSIONS) + endif() + file(WRITE "${_ipp_sentinel}" "done\n") + message(STATUS "conda cmake_install: installed global itk package files") +endif() + +# C extension (.abi3.so) +file(GLOB _so + "${_ipp_conda_prefix}/lib/python*/site-packages/itk/_${_ipp_module}Python.abi3.so" + "${_ipp_conda_prefix}/lib/python*/site-packages/itk/_${_ipp_module}Python*.so" +) +if(_so) + file(INSTALL ${_so} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk" USE_SOURCE_PERMISSIONS) +endif() + +# Python wrapper .py +file(GLOB _py + "${_ipp_conda_prefix}/lib/python*/site-packages/itk/${_ipp_module}Python.py" +) +if(_py) + file(INSTALL ${_py} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk" USE_SOURCE_PERMISSIONS) +endif() + +# Configuration files +file(GLOB _cfg + "${_ipp_conda_prefix}/lib/python*/site-packages/itk/Configuration/${_ipp_module}Config.py" + "${_ipp_conda_prefix}/lib/python*/site-packages/itk/Configuration/${_ipp_module}_snake_case.py" +) +if(_cfg) + file(INSTALL ${_cfg} DESTINATION "${CMAKE_INSTALL_PREFIX}/itk/Configuration" USE_SOURCE_PERMISSIONS) +endif() + +list(LENGTH _so _ipp_n) +message(STATUS "conda cmake_install: ${_ipp_component}: ${_ipp_n} .so + python files installed") +CMAKE_EOF + +echo "Generated ${ITK_CMAKE_DIR}/cmake_install.cmake" From 057e98731a91a7a0ef656a01e155645dda206667 Mon Sep 17 00:00:00 2001 From: Cavan Riley Date: Sun, 19 Apr 2026 17:38:58 -0500 Subject: [PATCH 22/22] BUG: Fix cmake config dir lookup in conda libitk-wrapping install The libitk-wrapping install script searched $PREFIX/lib/cmake/ITK-*/ for the cmake config directory, but that directory is owned by libitk-devel and does not exist during the libitk-wrapping packaging step. The find returned empty and the script aborted. Fix: look for ITKConfig.cmake in the build tree (where it is always present) to determine the ITK version string, then mkdir -p the installed cmake config dir before writing the shim cmake_install.cmake. libitk-devel contributes the remaining cmake config files to the same directory at user-install time; conda permits multiple packages to share a directory. --- .../conda-packages/libitk-wrapping_install.bat | 12 +++++++++--- Utilities/conda-packages/libitk-wrapping_install.sh | 13 ++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Utilities/conda-packages/libitk-wrapping_install.bat b/Utilities/conda-packages/libitk-wrapping_install.bat index fcb9ebf0bd6..943f713e926 100755 --- a/Utilities/conda-packages/libitk-wrapping_install.bat +++ b/Utilities/conda-packages/libitk-wrapping_install.bat @@ -15,11 +15,17 @@ rem those PythonWheel* names to files already installed under %PREFIX%. rem On Windows, conda Python packages live at %PREFIX%\Lib\site-packages (one rem level above %LIBRARY_PREFIX%) and extension modules use .pyd instead of .so. -for /d %%i in ("%LIBRARY_PREFIX%\lib\cmake\ITK-*") do set ITK_CMAKE_DIR=%%i -if "%ITK_CMAKE_DIR%"=="" ( - echo ERROR: could not find ITK cmake config dir under %LIBRARY_PREFIX%\lib\cmake 1>&2 +rem Derive the ITK version from the build tree and create the installed cmake +rem config dir if needed. libitk-devel populates the same directory; conda +rem allows multiple packages to contribute files to a shared directory. +set ITK_CMAKE_DIRNAME= +for /f "delims=" %%d in ('powershell -NoProfile -Command "Get-ChildItem -Path '%BUILD_DIR%' -Recurse -Filter ITKConfig.cmake | Where-Object { $_.FullName -notmatch 'CMakeFiles' } | Select-Object -First 1 -ExpandProperty DirectoryName | Split-Path -Leaf"') do set ITK_CMAKE_DIRNAME=%%d +if "%ITK_CMAKE_DIRNAME%"=="" ( + echo ERROR: could not find ITKConfig.cmake in build tree %BUILD_DIR% 1>&2 exit 1 ) +set ITK_CMAKE_DIR=%LIBRARY_PREFIX%\lib\cmake\%ITK_CMAKE_DIRNAME% +if not exist "%ITK_CMAKE_DIR%" mkdir "%ITK_CMAKE_DIR%" rem Write the cmake_install.cmake via a temporary PowerShell script. rem Using a PS1 temp file avoids cmd escaping issues with parentheses and diff --git a/Utilities/conda-packages/libitk-wrapping_install.sh b/Utilities/conda-packages/libitk-wrapping_install.sh index 3c165b9e789..185410f423f 100755 --- a/Utilities/conda-packages/libitk-wrapping_install.sh +++ b/Utilities/conda-packages/libitk-wrapping_install.sh @@ -19,11 +19,18 @@ cmake -DCOMPONENT=PythonWrappingRuntimeLibraries -P "${BUILD_DIR}/cmake_install. # The file uses CMAKE_CURRENT_LIST_DIR to derive the prefix at cmake time, # making it fully relocatable across machines and prefix paths. -ITK_CMAKE_DIR=$(find "${PREFIX}/lib/cmake" -maxdepth 1 -name "ITK-*" -type d | head -1) -if [ -z "${ITK_CMAKE_DIR}" ]; then - echo "ERROR: could not find ITK cmake config dir under ${PREFIX}/lib/cmake" >&2 +# Derive the ITK version from the build tree (ITKConfig.cmake is always +# generated there) and create the installed cmake config dir if needed. +# libitk-devel populates the same directory; conda allows multiple packages +# to contribute files to a shared directory. +BUILD_ITK_CMAKE=$(find "${BUILD_DIR}" -maxdepth 5 -name "ITKConfig.cmake" -not -path "*/CMakeFiles/*" | head -1) +if [ -z "${BUILD_ITK_CMAKE}" ]; then + echo "ERROR: could not find ITKConfig.cmake in build tree ${BUILD_DIR}" >&2 exit 1 fi +ITK_CMAKE_DIRNAME=$(basename "$(dirname "${BUILD_ITK_CMAKE}")") +ITK_CMAKE_DIR="${PREFIX}/lib/cmake/${ITK_CMAKE_DIRNAME}" +mkdir -p "${ITK_CMAKE_DIR}" cat > "${ITK_CMAKE_DIR}/cmake_install.cmake" <<'CMAKE_EOF' # Relocatable cmake_install.cmake for conda-installed ITK.