From 5456d9c5c7578e2537a55b0f6d6830ee4d91ee74 Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Fri, 23 Oct 2026 21:14:18 +0000 Subject: [PATCH 1/2] ENH: Build FFTW as an ITKFFTW third-party module with FetchContent Replace the itkExternal_FFTW ExternalProject, which built the library twice outside the module system, with a standard third-party module. FFTW is fetched once and each precision is added as a subdirectory. FindFFTW now provides FFTW::Double and FFTW::Float imported targets, and the module re-runs it for downstream projects through the usual EXPORT_CODE hooks. Remove the FFTW paths from ITKConfig.cmake. Enabling ITKFFTW without ITK_USE_FFTWD or ITK_USE_FFTWF is an error. --- CMake/CTestCustom.cmake.in | 3 - CMake/FindFFTW.cmake | 99 ++++- CMake/ITKConfig.cmake.in | 8 - CMake/ITKModuleMacros.cmake | 8 + .../FFTWTargetArchitecture/CMakeLists.txt | 75 ++-- CMake/itkExternal_FFTW.cmake | 398 ------------------ CMakeLists.txt | 19 +- .../docs/contributing/build_test_itk.md | 19 + Modules/Filtering/FFT/CMakeLists.txt | 12 +- Modules/Filtering/FFT/itk-module.cmake | 3 + Modules/Filtering/FFT/src/CMakeLists.txt | 23 - Modules/ThirdParty/FFTW/CMakeLists.txt | 145 +++++++ Modules/ThirdParty/FFTW/itk-module.cmake | 7 + Modules/ThirdParty/FFTW/itkFFTWOptions.cmake | 60 +++ Modules/ThirdParty/FFTW/src/CMakeLists.txt | 60 +++ 15 files changed, 439 insertions(+), 500 deletions(-) delete mode 100644 CMake/itkExternal_FFTW.cmake create mode 100644 Modules/ThirdParty/FFTW/CMakeLists.txt create mode 100644 Modules/ThirdParty/FFTW/itk-module.cmake create mode 100644 Modules/ThirdParty/FFTW/itkFFTWOptions.cmake create mode 100644 Modules/ThirdParty/FFTW/src/CMakeLists.txt diff --git a/CMake/CTestCustom.cmake.in b/CMake/CTestCustom.cmake.in index 67571017527..addb207d9ef 100644 --- a/CMake/CTestCustom.cmake.in +++ b/CMake/CTestCustom.cmake.in @@ -101,9 +101,6 @@ set(CTEST_CUSTOM_WARNING_EXCEPTION "Cloning into.*" "Note: checking out.*" "HEAD is now at.*" - # FFTW ExternalProject - ".*fftw[fd]/src/fftw[fd]./*" - "libtool: install: warning: relinking .libfftw3f?_threads.la." # Ignore clang's summary warning, assuming prior text has matched some # other warning expression: diff --git a/CMake/FindFFTW.cmake b/CMake/FindFFTW.cmake index b92b418fcb0..07dfd995307 100644 --- a/CMake/FindFFTW.cmake +++ b/CMake/FindFFTW.cmake @@ -2,6 +2,15 @@ ## various data types. ## There is a single set of include files, and then multiple libraries, ## One for each type. I.e. libfftw.a-->double, libfftwf.a-->float +## +## Usage: +## find_package(FFTW REQUIRED COMPONENTS Double Float) +## Without COMPONENTS, the precisions enabled by `ITK_USE_FFTWD` and +## `ITK_USE_FFTWF` are searched for. +## +## Imported targets (carrying include directories and link dependencies): +## FFTW::Double -- double precision FFTW (with threads support if found) +## FFTW::Float -- single precision FFTW (with threads support if found) ## The following logic belongs in the individual package ## mark_as_advanced(ITK_USE_FFTWD) @@ -30,7 +39,23 @@ ## to set the initial value of the CMake variable `MKLROOT` (see ## https://software.intel.com/en-us/mkl-linux-developer-guide-scripts-to-set-environment-variables). -if(ITK_USE_FFTWD OR ITK_USE_FFTWF) +set(_fftw_double ${ITK_USE_FFTWD}) +set(_fftw_float ${ITK_USE_FFTWF}) +if(FFTW_FIND_COMPONENTS) + set(_fftw_double OFF) + set(_fftw_float OFF) + foreach(_fftw_component IN LISTS FFTW_FIND_COMPONENTS) + if(_fftw_component STREQUAL "Double") + set(_fftw_double ON) + elseif(_fftw_component STREQUAL "Float") + set(_fftw_float ON) + else() + message(FATAL_ERROR "Unknown FFTW component: ${_fftw_component}") + endif() + endforeach() +endif() + +if(_fftw_double OR _fftw_float) if(ITK_USE_MKL) # If the user has provided the MKL include path then search nearby for library files if(FFTW_INCLUDE_PATH) @@ -53,8 +78,6 @@ if(ITK_USE_FFTWD OR ITK_USE_FFTWF) else() set( FFTW_INC_SEARCHPATH - ${FFTW3f_INCLUDE_DIRS} # Defined in fftwf local build - ${FFTW3_INCLUDE_DIRS} # Defined in fftwd local build /sw/include /usr/include /usr/local/include @@ -194,20 +217,17 @@ if(ITK_USE_FFTWD OR ITK_USE_FFTWF) endif() endforeach() fftwd_libraries_end() - add_compile_options(${MKL_OPTIONS}) else() get_filename_component(FFTW_INSTALL_BASE_PATH ${FFTW_INCLUDE_PATH} PATH) set( FFTW_LIB_SEARCHPATH - ${FFTW3f_LIBRARY_DIRS} # Defined in fftwf local build - ${FFTW3_LIBRARY_DIRS} # Defined in fftwd local build ${FFTW_INSTALL_BASE_PATH}/lib ${FFTW_INSTALL_BASE_PATH}/lib64 /usr/lib/fftw /usr/local/lib/fftw ) unset(FFTWD_FOUND) - if(ITK_USE_FFTWD) + if(_fftw_double) mark_as_advanced( FFTWD_BASE_LIB FFTWD_THREADS_LIB @@ -219,8 +239,6 @@ if(ITK_USE_FFTWD OR ITK_USE_FFTWF) if(FFTWD_BASE_LIB) set(FFTWD_FOUND 1) get_filename_component(FFTW_LIBDIR ${FFTWD_BASE_LIB} PATH) #NOTE FFTWD_BASE_LIB and FFTWF_BASE_LIB must be installed in the same location - get_filename_component(FFTWD_BASE_LIB ${FFTWD_BASE_LIB} NAME) - get_filename_component(FFTWD_THREADS_LIB ${FFTWD_THREADS_LIB} NAME) set(FFTWD_LIBRARIES ${FFTWD_BASE_LIB}) if(FFTWD_THREADS_LIB) set( @@ -235,7 +253,7 @@ if(ITK_USE_FFTWD OR ITK_USE_FFTWF) endif() unset(FFTWF_FOUND) - if(ITK_USE_FFTWF) + if(_fftw_float) mark_as_advanced( FFTWF_BASE_LIB FFTWF_THREADS_LIB @@ -247,8 +265,6 @@ if(ITK_USE_FFTWD OR ITK_USE_FFTWF) if(FFTWF_BASE_LIB) set(FFTWF_FOUND 1) get_filename_component(FFTW_LIBDIR ${FFTWF_BASE_LIB} PATH) #NOTE FFTWD_BASE_LIB and FFTWF_BASE_LIB must be installed in the same location - get_filename_component(FFTWF_BASE_LIB ${FFTWF_BASE_LIB} NAME) - get_filename_component(FFTWF_THREADS_LIB ${FFTWF_THREADS_LIB} NAME) set(FFTWF_LIBRARIES ${FFTWF_BASE_LIB}) if(FFTWF_THREADS_LIB) set( @@ -262,6 +278,61 @@ if(ITK_USE_FFTWD OR ITK_USE_FFTWF) endif() endif() endif() - unset(FFTWD_THREADS_LIB) - unset(FFTWF_THREADS_LIB) + if(ITK_USE_MKL) + set(FFTWF_LIBRARIES ${FFTWD_LIBRARIES}) + endif() + set(_fftw_required_vars FFTW_INCLUDE) + if(_fftw_double) + list(APPEND _fftw_required_vars FFTWD_LIBRARIES) + endif() + if(_fftw_float) + list(APPEND _fftw_required_vars FFTWF_LIBRARIES) + endif() + if(ITK_USE_CUFFTW) + list( + APPEND + _fftw_required_vars + CUFFT_LIB + CUFFTW_LIB + ) + endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(FFTW REQUIRED_VARS ${_fftw_required_vars}) + + if(FFTW_FOUND) + set(_fftw_extra_libraries "") + if(NOT ITK_USE_MKL AND NOT ITK_USE_CUFFTW) + find_package(Threads REQUIRED) + list(APPEND _fftw_extra_libraries Threads::Threads) + if(UNIX) + list(APPEND _fftw_extra_libraries m) + endif() + endif() + foreach(_fftw_precision IN ITEMS Double Float) + string(SUBSTRING ${_fftw_precision} 0 1 _fftw_suffix) + string(TOLOWER ${_fftw_precision} _fftw_selected) + if(_fftw_${_fftw_selected} AND NOT TARGET FFTW::${_fftw_precision}) + add_library(FFTW::${_fftw_precision} INTERFACE IMPORTED) + target_include_directories( + FFTW::${_fftw_precision} + SYSTEM + INTERFACE + "${FFTW_INCLUDE}" + ) + target_link_libraries( + FFTW::${_fftw_precision} + INTERFACE + ${FFTW${_fftw_suffix}_LIBRARIES} + ${_fftw_extra_libraries} + ) + if(ITK_USE_MKL) + target_compile_options( + FFTW::${_fftw_precision} + INTERFACE + ${MKL_OPTIONS} + ) + endif() + endif() + endforeach() + endif() endif() diff --git a/CMake/ITKConfig.cmake.in b/CMake/ITKConfig.cmake.in index 5c45e698cfa..778b4625e23 100644 --- a/CMake/ITKConfig.cmake.in +++ b/CMake/ITKConfig.cmake.in @@ -109,14 +109,6 @@ include( "${ITK_CMAKE_DIR}/ITKFactoryRegistration.cmake" ) set(ITK_USE_FFTWD "@ITK_USE_FFTWD@") set(ITK_USE_FFTWF "@ITK_USE_FFTWF@") set(ITK_USE_SYSTEM_FFTW "@ITK_USE_SYSTEM_FFTW@") -set(ITK_FFTW_INCLUDE_PATH "@FFTW_INCLUDE_PATH@") -set(ITK_FFTW_LIBDIR "@FFTW_LIBDIR@") - -# Add FFTW include and library directories -if (ITK_USE_FFTWF OR ITK_USE_FFTWD) - set(ITK_INCLUDE_DIRS ${ITK_INCLUDE_DIRS} "${ITK_FFTW_INCLUDE_PATH}") - set(ITK_LIBRARY_DIRS ${ITK_LIBRARY_DIRS} "${ITK_FFTW_LIBDIR}") -endif() # Add configuration with GPU set(ITK_USE_GPU "@ITK_USE_GPU@") diff --git a/CMake/ITKModuleMacros.cmake b/CMake/ITKModuleMacros.cmake index 81421133f4f..007ae410f74 100644 --- a/CMake/ITKModuleMacros.cmake +++ b/CMake/ITKModuleMacros.cmake @@ -476,6 +476,14 @@ endif() # Export and install the interface library itk_module_target_export(${itk-module}Module) itk_module_target_install(${itk-module}Module) + # Source-less modules still export their interface library to ITKTargets. + set_property( + GLOBAL + APPEND + PROPERTY + ITKTargets_MODULES + ${itk-module} + ) #### # End ITK Modules interface library creation #### diff --git a/CMake/Tests/FFTWTargetArchitecture/CMakeLists.txt b/CMake/Tests/FFTWTargetArchitecture/CMakeLists.txt index bb53c2db4d8..59e13268509 100644 --- a/CMake/Tests/FFTWTargetArchitecture/CMakeLists.txt +++ b/CMake/Tests/FFTWTargetArchitecture/CMakeLists.txt @@ -1,30 +1,23 @@ cmake_minimum_required(VERSION 3.22.1) project(FFTWTargetArchitectureCheck LANGUAGES NONE) -file(READ "${CMAKE_CURRENT_LIST_DIR}/../../itkExternal_FFTW.cmake" source) -string(FIND "${source}" "set(_fftw_default_neon OFF)" selection_start) -string(FIND "${source}" "# Macro to generate library filename" selection_end) -math(EXPR selection_length "${selection_end} - ${selection_start}") -string(SUBSTRING "${source}" ${selection_start} ${selection_length} selection) -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/selection.cmake" "${selection}") +include(CheckCSourceCompiles) function(check_c_source_compiles source result) - set(${result} OFF PARENT_SCOPE) + set(${result} "${compiler_simd}" PARENT_SCOPE) endfunction() -set(APPLE TRUE) -set(failures "") -foreach(host IN ITEMS arm64 x86_64) - set(CMAKE_SYSTEM_PROCESSOR "${host}") - if(host STREQUAL "arm64") - set(CMAKE_OSX_ARCHITECTURES x86_64) - set(expected_neon OFF) - set(expected_sse ON) - else() - set(CMAKE_OSX_ARCHITECTURES arm64) - set(expected_neon ON) - set(expected_sse OFF) - endif() +function( + check_target + processor + architectures + expected_neon + expected_sse + expected_avx +) + set(APPLE TRUE) + set(CMAKE_SYSTEM_PROCESSOR "${processor}") + set(CMAKE_OSX_ARCHITECTURES "${architectures}") foreach( isa IN @@ -38,7 +31,9 @@ foreach(host IN ITEMS arm64 x86_64) unset(FFTW_ENABLE_${isa} CACHE) unset(FFTW_ENABLE_${isa}) endforeach() - include("${CMAKE_CURRENT_BINARY_DIR}/selection.cmake") + include( + "${CMAKE_CURRENT_LIST_DIR}/../../../Modules/ThirdParty/FFTW/itkFFTWOptions.cmake" + ) if( NOT "${FFTW_ENABLE_NEON}" @@ -54,10 +49,40 @@ foreach(host IN ITEMS arm64 x86_64) "${FFTW_ENABLE_SSE2}" STREQUAL "${expected_sse}" + OR + NOT + "${FFTW_ENABLE_AVX}" + STREQUAL + "${expected_avx}" + OR + NOT + "${FFTW_ENABLE_AVX2}" + STREQUAL + "${expected_avx}" ) - list(APPEND failures "${host}->${CMAKE_OSX_ARCHITECTURES}") + message( + FATAL_ERROR + "FFTW defaults disagree with target: ${processor}->${architectures}" + ) endif() -endforeach() -if(failures) - message(FATAL_ERROR "FFTW defaults disagree with target: ${failures}") +endfunction() + +set(compiler_simd OFF) +check_target(arm64 x86_64 OFF ON OFF) +check_target(x86_64 arm64 ON OFF OFF) +check_target(x86_64 "" OFF ON OFF) +check_target(aarch64 "" ON OFF OFF) +check_target(i686 "" OFF OFF OFF) +check_target(arm64 "arm64;x86_64" OFF OFF OFF) + +set(compiler_simd ON) +check_target(x86_64 "" OFF ON ON) + +set(FFTW_ENABLE_AVX OFF CACHE BOOL "" FORCE) +set(CMAKE_SYSTEM_PROCESSOR x86_64) +include( + "${CMAKE_CURRENT_LIST_DIR}/../../../Modules/ThirdParty/FFTW/itkFFTWOptions.cmake" +) +if(FFTW_ENABLE_AVX) + message(FATAL_ERROR "FFTW SIMD defaults override an explicit user option") endif() diff --git a/CMake/itkExternal_FFTW.cmake b/CMake/itkExternal_FFTW.cmake deleted file mode 100644 index ed6da6373d3..00000000000 --- a/CMake/itkExternal_FFTW.cmake +++ /dev/null @@ -1,398 +0,0 @@ -# -# Encapsulates building FFTW as an External Project. -# -# SIMD codelet selection and binary redistribution policy -# ------------------------------------------------------- -# FFTW SIMD codelets are hand-written assembly routines compiled INTO the -# library at build time. Unlike -march=native on the ITK side, FFTW codelets -# must be requested explicitly via FFTW's own CMake options -# (ENABLE_NEON, ENABLE_SSE, ENABLE_SSE2, ENABLE_AVX, ENABLE_AVX2). -# -# For redistributable binary packages (conda, pip/PyPI, manylinux Docker -# images, etc.) SIMD codelets must only be enabled when the resulting binary -# will run correctly on ALL machines in the target distribution. The ISA -# baseline mandated by each architecture ABI is universally safe: -# -# x86_64 / AMD64 : SSE and SSE2 are required by the AMD64 ABI. Every -# 64-bit x86 CPU (including all manylinux2014 / -# manylinux_2_28 targets) supports them. DEFAULT ON. -# -# aarch64 / arm64 : NEON is required by the AArch64 ABI. Every 64-bit ARM -# CPU (Apple Silicon, all Linux aarch64 targets) supports -# it. DEFAULT ON. -# -# AVX / AVX2 : Not part of the baseline ABI; present only on Sandy -# Bridge (2011) and Haswell (2013) and newer CPUs -# respectively. Enabling them by default would produce -# binaries that SIGILL on older (but spec-compliant) -# x86_64 CPUs. DEFAULT OFF unless the compiler is already -# targeting a micro-architecture that includes them. -# -# Opt-in to AVX / AVX2 -# --------------------- -# If the user's toolchain is already generating AVX/AVX2 instructions -# (because they passed -march=native, -mavx2, -march=haswell, or an -# equivalent MSVC /arch: flag) the compiler pre-defines __AVX__ / __AVX2__. -# This file detects those macros at cmake configure time via -# check_c_source_compiles (compile-time, NOT runtime — no build-host CPU -# probe is performed) and auto-enables the matching FFTW codelets so that -# FFTW's generated code aligns with the rest of the ITK build. -# Users who want AVX2 in a redistributed package can set: -# cmake -DFFTW_ENABLE_AVX2=ON ... -# -# macOS universal binary -# ---------------------- -# When CMAKE_OSX_ARCHITECTURES lists more than one value (e.g. "arm64;x86_64") -# a single FFTW configure/build pass cannot correctly serve both slices. -# SIMD defaults are set to OFF in this case; use ITK_USE_SYSTEM_FFTW with a -# proper universal FFTW installation (e.g., built with lipo) if SIMD -# performance is required in a macOS universal build. -# -# Every flag remains individually overridable, e.g.: -# cmake -DFFTW_ENABLE_AVX2=ON # opt in to AVX2 for a non-redistributed build -# cmake -DFFTW_ENABLE_SSE2=OFF # opt out of SSE2 (unusual) -# Note: option() defaults are only applied on the first configure. -# To re-detect after a toolchain change, delete the CMake cache or use -# cmake --fresh, or pass explicit -DFFTW_ENABLE_*= overrides. -# -# ENABLE_SSE (SSE1) is float-only and is not forwarded to the -# double-precision fftwd build. -# -# These instructions follow the guidance provided for modern cmake usage as described: -# https://github.com/dev-cafe/cmake-cookbook/blob/master/chapter-08/recipe-03/c-example/external/upstream/fftw3/CMakeLists.txt -# -include(ITK_CheckCCompilerFlag) - -if(NOT ITK_USE_MKL AND NOT ITK_USE_CUFFTW) - set(msg "ATTENTION: You have enabled the use of FFTW.") - set(msg "${msg} This library is distributed under a GPL license.") - set(msg "${msg} By enabling this option, the ITK libraries binary") - set(msg "${msg} that is built will be covered by a GPL license") - set( - msg - "${msg} and so will any executable that is linked against these libraries." - ) - message("${msg}") -endif() - -if(NOT ITK_USE_SYSTEM_FFTW) - # - # fftw limitation -- can't be built in - # a directory with whitespace in its name. - if(${CMAKE_CURRENT_BINARY_DIR} MATCHES ".*[ \t].*") - message( - FATAL_ERROR - "Can't build fftw in a directory with whitespace in its name" - ) - endif() - - if(WIN32) - # on windows the build of FFTW must match that of ITK - set(FFTW_BUILD_TYPE ${CMAKE_BUILD_TYPE}) - else() - set(FFTW_BUILD_TYPE Release) - endif() - - include(GNUInstallDirs) - - set(_fftw_target_version 3.3.10) - set( - _fftw_url_hash - "2d34b5ccac7b08740dbdacc6ebe451d8a34cf9d9bfec85a5e776e87adf94abfd803c222412d8e10fbaa4ed46f504aa87180396af1b108666cde4314a55610b40" - ) - set( - _fftw_url - "https://data.kitware.com/api/v1/file/hashsum/sha512/${_fftw_url_hash}/download" - ) - - set(FFTW_STAGED_INSTALL_PREFIX "${ITK_BINARY_DIR}/fftw") - - # Detect SIMD defaults (see file header for full policy description). - # - # Architecture-guaranteed ISA baselines (no runtime probe needed): - # - x86_64 mandates SSE + SSE2 in the AMD64 ABI. - # - arm64/aarch64 mandates NEON in the AArch64 ABI. - # - # AVX/AVX2 opt-in via compiler predefined macros: - # check_c_source_compiles (not _runs) reflects what the compiler is - # generating for the TARGET architecture, not what the BUILD HOST's CPU - # can execute. This is safe for cross-compilation and redistribution. - include(CheckCSourceCompiles) - - set(_fftw_default_neon OFF) - set(_fftw_default_sse OFF) - set(_fftw_default_sse2 OFF) - set(_fftw_default_avx OFF) - set(_fftw_default_avx2 OFF) - - # Detect macOS universal binary build: a single configure+build pass cannot - # simultaneously produce correct SIMD for both arm64 and x86_64 slices. - set(_fftw_is_universal FALSE) - if(APPLE AND CMAKE_OSX_ARCHITECTURES) - list(LENGTH CMAKE_OSX_ARCHITECTURES _fftw_arch_count) - if(_fftw_arch_count GREATER 1) - set(_fftw_is_universal TRUE) - message( - STATUS - "FFTW: macOS universal binary (${CMAKE_OSX_ARCHITECTURES}): " - "per-architecture SIMD defaults disabled. " - "Use ITK_USE_SYSTEM_FFTW with a universal FFTW to enable SIMD." - ) - endif() - endif() - - if(NOT _fftw_is_universal) - set(_fftw_target_processor "${CMAKE_SYSTEM_PROCESSOR}") - if(APPLE AND CMAKE_OSX_ARCHITECTURES) - set(_fftw_target_processor "${CMAKE_OSX_ARCHITECTURES}") - endif() - if(_fftw_target_processor MATCHES "aarch64|arm64|ARM64") - # NEON is mandatory in the AArch64 ABI — every arm64 CPU has it. - # Safe for all conda/pip arm64 packages and manylinux aarch64. - set(_fftw_default_neon ON) - elseif(_fftw_target_processor MATCHES "x86_64|AMD64") - # SSE and SSE2 are required by the AMD64 ABI — universally present on - # every 64-bit x86 CPU, including the oldest manylinux build targets. - # Safe for all conda/pip x86_64 packages. - set(_fftw_default_sse ON) - set(_fftw_default_sse2 ON) - # AVX and AVX2 are NOT part of the AMD64 baseline. Auto-enable them - # only when the compiler is already producing those instructions — i.e. - # when the user explicitly asked for a specific micro-architecture via - # -march=native, -mavx2, /arch:AVX2, etc. This compile-time check - # mirrors the approach recommended by seanm in ITK PR #6006: - # "the compiler knows what CPU it's compiling for." - # - # check_c_source_compiles caches its result by variable name. Unset - # the cache entry first so the probe always re-runs against the current - # CMAKE_C_FLAGS; this ensures that adding -march=native on a subsequent - # configure is correctly reflected in the auto-detected default. - # Note: FFTW_ENABLE_AVX / FFTW_ENABLE_AVX2 follow standard option() - # caching — they are only auto-set from the detected default when not - # already present in the cache. To force re-evaluation of the option - # after a FLAGS change, delete those entries from the CMake cache or - # pass -DFFTW_ENABLE_AVX2=ON explicitly. - unset(_fftw_compiler_targets_avx CACHE) - check_c_source_compiles( - "#ifndef __AVX__\n#error AVX not enabled\n#endif\nint main(void){return 0;}" - _fftw_compiler_targets_avx - ) - if(_fftw_compiler_targets_avx) - set(_fftw_default_avx ON) - endif() - unset(_fftw_compiler_targets_avx2 CACHE) - check_c_source_compiles( - "#ifndef __AVX2__\n#error AVX2 not enabled\n#endif\nint main(void){return 0;}" - _fftw_compiler_targets_avx2 - ) - if(_fftw_compiler_targets_avx2) - set(_fftw_default_avx2 ON) - endif() - elseif(_fftw_target_processor MATCHES "i686|i386") - # 32-bit x86 ABI does not mandate SSE/SSE2. Leave defaults OFF; - # users may opt in explicitly if their minimum target CPU supports them. - endif() - endif() - - option( - FFTW_ENABLE_NEON - "Enable FFTW NEON SIMD codelets (ARM64; ON by default on aarch64/arm64)" - ${_fftw_default_neon} - ) - option( - FFTW_ENABLE_SSE - "Enable FFTW SSE SIMD codelets (x86; ON by default on x86_64 — required by AMD64 ABI)" - ${_fftw_default_sse} - ) - option( - FFTW_ENABLE_SSE2 - "Enable FFTW SSE2 SIMD codelets (x86; ON by default on x86_64 — required by AMD64 ABI)" - ${_fftw_default_sse2} - ) - option( - FFTW_ENABLE_AVX - "Enable FFTW AVX SIMD codelets (Sandy Bridge+; OFF by default for redistribution safety)" - ${_fftw_default_avx} - ) - option( - FFTW_ENABLE_AVX2 - "Enable FFTW AVX2 SIMD codelets (Haswell+; OFF by default for redistribution safety)" - ${_fftw_default_avx2} - ) - - message( - STATUS - "FFTW SIMD: NEON=${FFTW_ENABLE_NEON} SSE=${FFTW_ENABLE_SSE} SSE2=${FFTW_ENABLE_SSE2} AVX=${FFTW_ENABLE_AVX} AVX2=${FFTW_ENABLE_AVX2}" - ) - - # Macro to generate library filename with appropriate prefix/suffix - # Args: output_var library_base_name - macro(_library_name_to_filename output_var library_base_name) - if(BUILD_SHARED_LIBS) - set( - ${output_var} - ${CMAKE_SHARED_LIBRARY_PREFIX}${library_base_name}${CMAKE_SHARED_LIBRARY_SUFFIX} - ) - else() - set( - ${output_var} - ${CMAKE_STATIC_LIBRARY_PREFIX}${library_base_name}${CMAKE_STATIC_LIBRARY_SUFFIX} - ) - endif() - endmacro() - - _library_name_to_filename(_fftw3f_threads_lib fftw3f_threads) - _library_name_to_filename(_fftw3f_lib fftw3f) - set( - ITK_FFTWF_LIBRARIES_NAMES - $$/${CMAKE_INSTALL_LIBDIR}/ITK-${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}/${_fftw3f_threads_lib}> - $$/${CMAKE_INSTALL_LIBDIR}/ITK-${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}/${_fftw3f_lib}> - ) - - set(PROJ_FFTWD_DEPENDS "") - if(ITK_USE_FFTWF) - itk_download_attempt_check(FFTW) - ExternalProject_Add( - fftwf - PREFIX fftwf-${_fftw_target_version} - INSTALL_DIR ${FFTW_STAGED_INSTALL_PREFIX} - URL - ${_fftw_url} - URL_HASH SHA512=${_fftw_url_hash} - DOWNLOAD_NAME "fftw-${_fftw_target_version}.tar.gz" - DOWNLOAD_NO_PROGRESS 1 - UPDATE_COMMAND - "" - LOG_CONFIGURE 1 - LOG_BUILD 1 - LOG_INSTALL 1 - CMAKE_CACHE_ARGS - -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS} -DBUILD_TESTS:BOOL=OFF - -DCMAKE_BUILD_TYPE:STRING=${FFTW_BUILD_TYPE} - -DCMAKE_INSTALL_PREFIX:PATH=${FFTW_STAGED_INSTALL_PREFIX} - -DCMAKE_INSTALL_LIBDIR:STRING=${CMAKE_INSTALL_LIBDIR} - -DCMAKE_INSTALL_BINDIR:STRING=${CMAKE_INSTALL_BINDIR} - -DDISABLE_FORTRAN:BOOL=ON -DENABLE_AVX:BOOL=${FFTW_ENABLE_AVX} - -DENABLE_AVX2:BOOL=${FFTW_ENABLE_AVX2} -DENABLE_FLOAT:BOOL=ON - -DENABLE_LONG_DOUBLE:BOOL=OFF -DENABLE_NEON:BOOL=${FFTW_ENABLE_NEON} - -DENABLE_OPENMP:BOOL=OFF -DENABLE_QUAD_PRECISION:BOOL=OFF - -DENABLE_SSE:BOOL=${FFTW_ENABLE_SSE} - -DENABLE_SSE2:BOOL=${FFTW_ENABLE_SSE2} -DENABLE_THREADS:BOOL=ON - -DCMAKE_APPLE_SILICON_PROCESSOR:STRING=${CMAKE_APPLE_SILICON_PROCESSOR} - -DCMAKE_C_COMPILER_LAUNCHER:PATH=${CMAKE_C_COMPILER_LAUNCHER} - -DCMAKE_C_COMPILER:PATH=${CMAKE_C_COMPILER} - -DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} - -DCMAKE_OSX_SYSROOT:PATH=${CMAKE_OSX_SYSROOT} - -DCMAKE_OSX_DEPLOYMENT_TARGET:PATH=${CMAKE_OSX_DEPLOYMENT_TARGET} - -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES} - -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=${CMAKE_POSITION_INDEPENDENT_CODE} - -DCMAKE_POLICY_VERSION_MINIMUM:STRING=${ITK_OLDEST_VALIDATED_POLICIES_VERSION} - INSTALL_BYPRODUCTS - ${FFTW_STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${_fftw3f_lib} - ${FFTW_STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${_fftw3f_threads_lib} - ) - # set( - # FFTW3f_DIR ${FFTW_STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/cmake/fftw3f - # CACHE PATH "Path to internally built single precision FFTW3Config.cmake" - # FORCE - # ) - # Can not find package, it does not yet exist find_package(FFTW3f CONFIG REQUIRED) - # but we know where it will eventually be! - set(FFTW3f_INCLUDE_DIRS ${FFTW_STAGED_INSTALL_PREFIX}/include) - set( - FFTW3f_LIBRARY_DIRS - ${FFTW_STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} - ) - set(FFTW_INCLUDE ${FFTW3f_INCLUDE_DIRS}) - - set(PROJ_FFTWD_DEPENDS "fftwf") - endif() - - if(ITK_USE_FFTWD) - _library_name_to_filename(_fftw3_threads_lib fftw3_threads) - _library_name_to_filename(_fftw3_lib fftw3) - set( - ITK_FFTWD_LIBRARIES_NAMES - $$/${CMAKE_INSTALL_LIBDIR}/ITK-${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}/${_fftw3_threads_lib}> - $$/${CMAKE_INSTALL_LIBDIR}/ITK-${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}/${_fftw3_lib}> - ) - - itk_download_attempt_check(FFTW) - ExternalProject_Add( - fftwd - PREFIX fftwd-${_fftw_target_version} - INSTALL_DIR ${FFTW_STAGED_INSTALL_PREFIX} - URL - ${_fftw_url} - URL_HASH SHA512=${_fftw_url_hash} - DOWNLOAD_NAME "fftw-${_fftw_target_version}.tar.gz" - DOWNLOAD_NO_PROGRESS 1 - UPDATE_COMMAND - "" - LOG_CONFIGURE 1 - LOG_BUILD 1 - LOG_INSTALL 1 - CMAKE_CACHE_ARGS - -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS} -DBUILD_TESTS:BOOL=OFF - -DCMAKE_BUILD_TYPE:STRING=${FFTW_BUILD_TYPE} - -DCMAKE_INSTALL_PREFIX:PATH=${FFTW_STAGED_INSTALL_PREFIX} - -DCMAKE_INSTALL_LIBDIR:STRING=${CMAKE_INSTALL_LIBDIR} - -DCMAKE_INSTALL_BINDIR:STRING=${CMAKE_INSTALL_BINDIR} - -DDISABLE_FORTRAN:BOOL=ON -DENABLE_AVX:BOOL=${FFTW_ENABLE_AVX} - -DENABLE_AVX2:BOOL=${FFTW_ENABLE_AVX2} -DENABLE_FLOAT:BOOL=OFF - -DENABLE_LONG_DOUBLE:BOOL=OFF -DENABLE_NEON:BOOL=${FFTW_ENABLE_NEON} - -DENABLE_OPENMP:BOOL=OFF -DENABLE_QUAD_PRECISION:BOOL=OFF - -DENABLE_SSE:BOOL=OFF # SSE1 codelets are 32-bit float only; no effect on double-precision - -DENABLE_SSE2:BOOL=${FFTW_ENABLE_SSE2} -DENABLE_THREADS:BOOL=ON - -DCMAKE_APPLE_SILICON_PROCESSOR:STRING=${CMAKE_APPLE_SILICON_PROCESSOR} - -DCMAKE_C_COMPILER_LAUNCHER:PATH=${CMAKE_C_COMPILER_LAUNCHER} - -DCMAKE_C_COMPILER:PATH=${CMAKE_C_COMPILER} - -DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} - -DCMAKE_OSX_SYSROOT:PATH=${CMAKE_OSX_SYSROOT} - -DCMAKE_OSX_DEPLOYMENT_TARGET:PATH=${CMAKE_OSX_DEPLOYMENT_TARGET} - -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES} - -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=${CMAKE_POSITION_INDEPENDENT_CODE} - -DCMAKE_POLICY_VERSION_MINIMUM:STRING=${ITK_OLDEST_VALIDATED_POLICIES_VERSION} - DEPENDS - ${PROJ_FFTWD_DEPENDS} # Avoid potential collisions on install - INSTALL_BYPRODUCTS - ${FFTW_STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${_fftw3_lib} - ${FFTW_STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${_fftw3_threads_lib} - ) - # set( - # FFTW3_DIR ${FFTW_STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/cmake/fftw3 - # CACHE PATH "Path to internally built double precision FFTW3Config.cmake" - # FORCE - # ) - # Can not find package, it does not yet exist find_package(FFTW3 CONFIG REQUIRED) - set(FFTW3_INCLUDE_DIRS ${FFTW_STAGED_INSTALL_PREFIX}/include) - set( - FFTW3_LIBRARY_DIRS - ${FFTW_STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} - ) - - set(FFTW_INCLUDE ${FFTW3_INCLUDE_DIRS}) - endif() - - # - # copy libraries into install tree, NOTE: DESTINATION MUST EXACTLY MATCH values from main CMakeLists.txt for FFTW_LIBDIR - install( - CODE - "file(GLOB FFTW_LIBS ${FFTW_STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/*fftw3*) - file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/ITK-${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}\" -TYPE FILE FILES \${FFTW_LIBS})" - COMPONENT Development - ) - # - # copy headers into install tree - install( - CODE - "file(GLOB FFTW_INC ${FFTW_STAGED_INSTALL_PREFIX}/include/*fftw3*) -file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/include/ITK-${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}\" -TYPE FILE FILES \${FFTW_INC})" - COMPONENT Development - ) -else() - #Search the filesystem for compatible versions - find_package(FFTW) # Use local itk FindFFTW.config to set variables consistently both with/without USE_SYSTEM_FFTW -endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 28646ff77e9..4d714eada9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -704,13 +704,6 @@ option( ) mark_as_advanced(ITK_USE_SYSTEM_FFTW) -if(ITK_USE_FFTWD OR ITK_USE_FFTWF) - include(itkExternal_FFTW) - # This pollutes the global namespace, but is needed for backward compatibility - include_directories(${FFTW_INCLUDE}) - link_directories(${FFTW_LIBDIR}) -endif() - set(ITK_USE_64BITS_IDS_DEFAULT "OFF") # Note WIN32 is true when targeting any windows system if(CMAKE_SIZEOF_VOID_P EQUAL "8" AND WIN32) @@ -883,17 +876,6 @@ set( ITK_CONFIG_MODULE_API_FILE "\${ITK_INSTALL_PREFIX}/${ITK_INSTALL_PACKAGE_DIR}/ITKModuleAPI.cmake" ) -if(NOT ITK_USE_SYSTEM_FFTW) - # Location installed with the FFTW ExternalProject. - set( - FFTW_LIBDIR - "\${ITK_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/ITK-${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}" - ) - set( - FFTW_INCLUDE_PATH - "\${ITK_INSTALL_PREFIX}/include/ITK-${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}/Algorithms" - ) -endif() configure_file(CMake/ITKConfig.cmake.in CMakeFiles/ITKConfig.cmake @ONLY) #----------------------------------------------------------------------------- @@ -921,6 +903,7 @@ install( CMake/Windows-utf8-codepage.manifest CMake/ITKExternalData.cmake CMake/ITKDownloadSetup.cmake + CMake/FindFFTW.cmake CMake/ExternalData.cmake CMake/ExternalData_config.cmake.in CMake/WrappingConfigCommon.cmake diff --git a/Documentation/docs/contributing/build_test_itk.md b/Documentation/docs/contributing/build_test_itk.md index 704799c84ff..cccf8f3dc36 100644 --- a/Documentation/docs/contributing/build_test_itk.md +++ b/Documentation/docs/contributing/build_test_itk.md @@ -4,6 +4,25 @@ The [CMake] build system was initially created to support development of ITK; IT ITK's tests are defined with CTest, and the tests can be executed via `ctest`. +## FFTW + +Enable `ITK_USE_FFTWF` for single precision or `ITK_USE_FFTWD` for double +precision. Either option adds the `ITKFFTW` third-party module to `ITKFFT`. +Enabling `Module_ITKFFTW` with neither option is a configuration error. +FFTW is GPL licensed; linking it changes the licensing requirements of +the resulting binaries. + +By default, `ITKFFTW` downloads FFTW 3.3.10 at configuration time with CMake +`FetchContent`. Both precisions share one source download and use separate +build directories. Set `FETCHCONTENT_SOURCE_DIR_FFTW` to an unpacked FFTW +source directory to build offline, or set `ITK_USE_SYSTEM_FFTW=ON` to use an +installed library instead. + +`FFTW_ENABLE_NEON`, `FFTW_ENABLE_SSE`, `FFTW_ENABLE_SSE2`, +`FFTW_ENABLE_AVX`, and `FFTW_ENABLE_AVX2` control SIMD codelets. Defaults +follow the target architecture and compiler flags, not the build host. +Link consumers to `ITK::ITKFFTModule` to inherit FFTW dependencies. + ## Pixi An easy and reproducible way to build and test ITK for development is via [Pixi]. Pixi will provide locally-scoped build tools such as toolchains, `cmake`, `ninja`, and `python`. diff --git a/Modules/Filtering/FFT/CMakeLists.txt b/Modules/Filtering/FFT/CMakeLists.txt index ac75b264d64..eb4351afc51 100644 --- a/Modules/Filtering/FFT/CMakeLists.txt +++ b/Modules/Filtering/FFT/CMakeLists.txt @@ -1,15 +1,5 @@ project(ITKFFT) -if(ITK_USE_CUFFTW) - set( - ITKFFT_LIBRARIES - ITKFFT - ${FFTWF_LIBRARIES} - ) -else() - set(ITKFFT_LIBRARIES ITKFFT) -endif() -set(ITKFFT_SYSTEM_LIBRARY_DIRS ${FFTW_LIBDIR}) -set(ITKFFT_INCLUDE_DIRS "${FFTW_INCLUDE}") +set(ITKFFT_LIBRARIES ITKFFT) itk_module_impl() diff --git a/Modules/Filtering/FFT/itk-module.cmake b/Modules/Filtering/FFT/itk-module.cmake index ac9a5987c77..fd2c25606d0 100644 --- a/Modules/Filtering/FFT/itk-module.cmake +++ b/Modules/Filtering/FFT/itk-module.cmake @@ -9,7 +9,9 @@ implementation you must comply with the GPL license." ) set(_fft_backends "FFTImageFilterInit::Pocket") +set(_fft_dependencies "") if(ITK_USE_FFTWF OR ITK_USE_FFTWD) + list(APPEND _fft_dependencies ITKFFTW) # Prepend so that FFTW constructor is preferred list(PREPEND _fft_backends "FFTImageFilterInit::FFTW") endif() @@ -20,6 +22,7 @@ itk_module( DEPENDS ITKCommon ITKPocketFFT + ${_fft_dependencies} COMPILE_DEPENDS ITKImageGrid TEST_DEPENDS diff --git a/Modules/Filtering/FFT/src/CMakeLists.txt b/Modules/Filtering/FFT/src/CMakeLists.txt index de12d8a9366..40127c4b517 100644 --- a/Modules/Filtering/FFT/src/CMakeLists.txt +++ b/Modules/Filtering/FFT/src/CMakeLists.txt @@ -13,26 +13,3 @@ if(ITK_USE_FFTWF OR ITK_USE_FFTWD) endif() itk_module_add_library(ITKFFT ${ITKFFT_SRCS}) - -# this library is only needed if FFTW is used -if(ITK_USE_FFTWF OR ITK_USE_FFTWD AND NOT ITK_USE_CUFFTW) - if(ITK_USE_FFTWD) - if(NOT ITK_USE_SYSTEM_FFTW) - add_dependencies(ITKFFT fftwd) # Ensure that local FFTW is built before using - target_link_libraries(ITKFFT PUBLIC ${ITK_FFTWD_LIBRARIES_NAMES}) - else() - target_include_directories(ITKFFT PUBLIC ${FFTW_INCLUDE}) - target_link_libraries(ITKFFT PUBLIC ${FFTWD_LIBRARIES}) - endif() - endif() - - if(ITK_USE_FFTWF) - if(NOT ITK_USE_SYSTEM_FFTW) - add_dependencies(ITKFFT fftwf) # Esnsure that local FFTW is build before using - target_link_libraries(ITKFFT PUBLIC ${ITK_FFTWF_LIBRARIES_NAMES}) - else() - target_include_directories(ITKFFT PUBLIC ${FFTW_INCLUDE}) - target_link_libraries(ITKFFT PUBLIC ${FFTWF_LIBRARIES}) - endif() - endif() -endif() diff --git a/Modules/ThirdParty/FFTW/CMakeLists.txt b/Modules/ThirdParty/FFTW/CMakeLists.txt new file mode 100644 index 00000000000..bc44c24fd24 --- /dev/null +++ b/Modules/ThirdParty/FFTW/CMakeLists.txt @@ -0,0 +1,145 @@ +project(ITKFFTW) +set(ITKFFTW_THIRD_PARTY 1) +set(ITKFFTW_LIBRARIES "") + +set(_fftw_precisions "") +set(_fftw_components "") +set(_fftw_library_names "") +if(ITK_USE_FFTWD) + list(APPEND _fftw_precisions double) + list(APPEND _fftw_components Double) + list(APPEND _fftw_library_names fftw3) +endif() +if(ITK_USE_FFTWF) + list(APPEND _fftw_precisions float) + list(APPEND _fftw_components Float) + list(APPEND _fftw_library_names fftw3f) +endif() + +if(NOT _fftw_precisions) + message( + FATAL_ERROR + "Module_ITKFFTW requires ITK_USE_FFTWD or ITK_USE_FFTWF to be ON." + ) +endif() + +if(NOT ITK_USE_MKL AND NOT ITK_USE_CUFFTW) + message( + STATUS + "FFTW is GPL licensed. Linking ITK with FFTW subjects the resulting binaries to the GPL." + ) +endif() + +if(ITK_USE_SYSTEM_FFTW) + list( + TRANSFORM _fftw_components + PREPEND + "FFTW::" + OUTPUT_VARIABLE ITKFFTW_LIBRARIES + ) + find_package(FFTW REQUIRED COMPONENTS ${_fftw_components}) + set(ITKFFTW_NO_SRC 1) + + # Seed the app's cache with the files ITK found, unless the app provides FFTW_ROOT. + set(_fftw_hint_code "") + if(NOT ITK_USE_MKL AND NOT ITK_USE_CUFFTW) + foreach( + _fftw_var + IN + ITEMS + FFTW_INCLUDE_PATH + FFTWD_BASE_LIB + FFTWD_THREADS_LIB + FFTWF_BASE_LIB + FFTWF_THREADS_LIB + ) + if(${_fftw_var}) + set(_fftw_type FILEPATH) + if(_fftw_var STREQUAL "FFTW_INCLUDE_PATH") + set(_fftw_type PATH) + endif() + string( + APPEND + _fftw_hint_code + " + set(${_fftw_var} \"${${_fftw_var}}\" CACHE ${_fftw_type} \"\")" + ) + endif() + endforeach() + set( + _fftw_hint_code + " +if(NOT DEFINED FFTW_ROOT AND NOT DEFINED ENV{FFTW_ROOT})${_fftw_hint_code} +endif()" + ) + endif() + + # When this module is loaded by an app, find FFTW with ITK's finder too. + set( + _fftw_find_code + " +set(ITK_USE_MKL ${ITK_USE_MKL}) +set(ITK_USE_CUFFTW ${ITK_USE_CUFFTW})${_fftw_hint_code} +set(_itk_fftw_module_path \"\${CMAKE_MODULE_PATH}\") +list(PREPEND CMAKE_MODULE_PATH \"\${ITK_CMAKE_DIR}\") +find_package(FFTW REQUIRED COMPONENTS ${_fftw_components}) +set(CMAKE_MODULE_PATH \"\${_itk_fftw_module_path}\") +unset(_itk_fftw_module_path) +" + ) + set(ITKFFTW_EXPORT_CODE_INSTALL "${_fftw_find_code}") + set( + ITKFFTW_EXPORT_CODE_BUILD + " +if(NOT ITK_BINARY_DIR)${_fftw_find_code}endif() +" + ) +else() + include(FetchContent) + include(itkFFTWOptions.cmake) + set(_fftw_version 3.3.10) + set( + _fftw_url_hash + "2d34b5ccac7b08740dbdacc6ebe451d8a34cf9d9bfec85a5e776e87adf94abfd803c222412d8e10fbaa4ed46f504aa87180396af1b108666cde4314a55610b40" + ) + # FFTW configures one precision per directory; populate without adding it. + FetchContent_Declare( + FFTW + URL + "https://data.kitware.com/api/v1/file/hashsum/sha512/${_fftw_url_hash}/download" + URL_HASH SHA512=${_fftw_url_hash} + DOWNLOAD_NAME fftw-${_fftw_version}.tar.gz + SOURCE_SUBDIR + itk-fetch-only + ) + if(NOT FETCHCONTENT_SOURCE_DIR_FFTW AND NOT FETCHCONTENT_FULLY_DISCONNECTED) + itk_download_attempt_check(FFTW) + endif() + FetchContent_MakeAvailable(FFTW) + # Expose only the public header, not FFTW's internal api/ headers. + configure_file( + "${fftw_SOURCE_DIR}/api/fftw3.h" + "${ITKFFTW_BINARY_DIR}/include/fftw3.h" + COPYONLY + ) + set(ITKFFTW_INCLUDE_DIRS "${ITKFFTW_BINARY_DIR}/include") + # Namespaced names keep ITK from exporting bare fftw3* compatibility targets. + set(_fftw_ns "${ITK_MODULE_ITKFFTW_TARGETS_NAMESPACE}") + foreach(_fftw_library IN LISTS _fftw_library_names) + list( + APPEND + ITKFFTW_LIBRARIES + ${_fftw_ns}${_fftw_library}_threads + ${_fftw_ns}${_fftw_library} + ) + endforeach() +endif() + +itk_module_impl() + +# The exported module target references this directory even without headers. +install( + DIRECTORY + DESTINATION ${ITKFFTW_INSTALL_INCLUDE_DIR} + COMPONENT Development +) diff --git a/Modules/ThirdParty/FFTW/itk-module.cmake b/Modules/ThirdParty/FFTW/itk-module.cmake new file mode 100644 index 00000000000..dad5e2490c8 --- /dev/null +++ b/Modules/ThirdParty/FFTW/itk-module.cmake @@ -0,0 +1,7 @@ +set( + DOCUMENTATION + "This module provides FFTW, a GPL-licensed fast Fourier transform library, +with optional single and double precision support." +) + +itk_module(ITKFFTW EXCLUDE_FROM_DEFAULT DESCRIPTION "${DOCUMENTATION}") diff --git a/Modules/ThirdParty/FFTW/itkFFTWOptions.cmake b/Modules/ThirdParty/FFTW/itkFFTWOptions.cmake new file mode 100644 index 00000000000..3aba7b60837 --- /dev/null +++ b/Modules/ThirdParty/FFTW/itkFFTWOptions.cmake @@ -0,0 +1,60 @@ +include(CheckCSourceCompiles) + +set(_fftw_default_neon OFF) +set(_fftw_default_sse OFF) +set(_fftw_default_sse2 OFF) +set(_fftw_default_avx OFF) +set(_fftw_default_avx2 OFF) + +set(_fftw_is_universal FALSE) +if(APPLE AND CMAKE_OSX_ARCHITECTURES) + list(LENGTH CMAKE_OSX_ARCHITECTURES _fftw_arch_count) + if(_fftw_arch_count GREATER 1) + set(_fftw_is_universal TRUE) + message( + STATUS + "FFTW: macOS universal binary (${CMAKE_OSX_ARCHITECTURES}): SIMD defaults disabled." + ) + endif() +endif() + +if(NOT _fftw_is_universal) + set(_fftw_target_processor "${CMAKE_SYSTEM_PROCESSOR}") + if(APPLE AND CMAKE_OSX_ARCHITECTURES) + set(_fftw_target_processor "${CMAKE_OSX_ARCHITECTURES}") + endif() + if(_fftw_target_processor MATCHES "aarch64|arm64|ARM64") + set(_fftw_default_neon ON) + elseif(_fftw_target_processor MATCHES "x86_64|AMD64") + # SSE2 is an ABI baseline; AVX requires an explicit compiler target. + set(_fftw_default_sse ON) + set(_fftw_default_sse2 ON) + unset(_fftw_compiler_targets_avx CACHE) + check_c_source_compiles( + "#ifndef __AVX__\n#error AVX not enabled\n#endif\nint main(void){return 0;}" + _fftw_compiler_targets_avx + ) + set(_fftw_default_avx ${_fftw_compiler_targets_avx}) + unset(_fftw_compiler_targets_avx2 CACHE) + check_c_source_compiles( + "#ifndef __AVX2__\n#error AVX2 not enabled\n#endif\nint main(void){return 0;}" + _fftw_compiler_targets_avx2 + ) + set(_fftw_default_avx2 ${_fftw_compiler_targets_avx2}) + endif() +endif() + +option(FFTW_ENABLE_NEON "Enable FFTW NEON SIMD codelets" ${_fftw_default_neon}) +option( + FFTW_ENABLE_SSE + "Enable FFTW SSE SIMD codelets (float only)" + ${_fftw_default_sse} +) +option(FFTW_ENABLE_SSE2 "Enable FFTW SSE2 SIMD codelets" ${_fftw_default_sse2}) +option(FFTW_ENABLE_AVX "Enable FFTW AVX SIMD codelets" ${_fftw_default_avx}) +option(FFTW_ENABLE_AVX2 "Enable FFTW AVX2 SIMD codelets" ${_fftw_default_avx2}) + +message( + STATUS + "FFTW SIMD: NEON=${FFTW_ENABLE_NEON} SSE=${FFTW_ENABLE_SSE} SSE2=${FFTW_ENABLE_SSE2} AVX=${FFTW_ENABLE_AVX} AVX2=${FFTW_ENABLE_AVX2}" +) diff --git a/Modules/ThirdParty/FFTW/src/CMakeLists.txt b/Modules/ThirdParty/FFTW/src/CMakeLists.txt new file mode 100644 index 00000000000..e604399fbb1 --- /dev/null +++ b/Modules/ThirdParty/FFTW/src/CMakeLists.txt @@ -0,0 +1,60 @@ +function(itk_fftw_add_precision precision) + set(CMAKE_POLICY_VERSION_MINIMUM ${ITK_OLDEST_VALIDATED_POLICIES_VERSION}) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + set(BUILD_TESTS OFF) + set(DISABLE_FORTRAN ON) + set(ENABLE_THREADS ON) + set(WITH_COMBINED_THREADS OFF) + set(ENABLE_OPENMP OFF) + set(ENABLE_LONG_DOUBLE OFF) + set(ENABLE_QUAD_PRECISION OFF) + set(ENABLE_FLOAT OFF) + set(ENABLE_SSE OFF) + if(precision STREQUAL "float") + set(ENABLE_FLOAT ON) + set(ENABLE_SSE ${FFTW_ENABLE_SSE}) + set(_fftw_library fftw3f) + else() + set(_fftw_library fftw3) + endif() + foreach(isa IN ITEMS NEON SSE2 AVX AVX2) + set(ENABLE_${isa} ${FFTW_ENABLE_${isa}}) + endforeach() + # Mask cached SIMD probes when a codelet option is disabled. + foreach( + isa + IN + ITEMS + SSE + SSE2 + AVX + AVX2 + FMA + ) + set(HAVE_${isa} 0) + endforeach() + + add_subdirectory("${fftw_SOURCE_DIR}" "${precision}" EXCLUDE_FROM_ALL) + + foreach(_target IN ITEMS ${_fftw_library} ${_fftw_library}_threads) + set_property( + TARGET + ${_target} + PROPERTY + INTERFACE_INCLUDE_DIRECTORIES + "${ITKFFTW_GENEX_INCLUDE_DIRS}" + ) + itk_module_target(${_target}) + endforeach() +endfunction() + +foreach(_precision IN LISTS _fftw_precisions) + itk_fftw_add_precision(${_precision}) +endforeach() + +install( + FILES + "${ITKFFTW_BINARY_DIR}/include/fftw3.h" + DESTINATION ${ITKFFTW_INSTALL_INCLUDE_DIR} + COMPONENT Development +) From b5c5da0dea9609ec53b5e21165157361b0efc69b Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Mon, 26 Oct 2026 13:45:17 +0000 Subject: [PATCH 2/2] DOC: Add ITK 6 migration note for linking FFTW through ITK targets ITKConfig.cmake no longer defines ITK_FFTW_INCLUDE_PATH or ITK_FFTW_LIBDIR, and the bundled libraries are renamed libitkfftw3*. Describe linking ITK::ITKFFTModule instead of naming FFTW paths and libraries. --- .../migration_guides/itk_6_migration_guide.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Documentation/docs/migration_guides/itk_6_migration_guide.md b/Documentation/docs/migration_guides/itk_6_migration_guide.md index ad8aaaa3afc..a50df7d3f7f 100644 --- a/Documentation/docs/migration_guides/itk_6_migration_guide.md +++ b/Documentation/docs/migration_guides/itk_6_migration_guide.md @@ -587,6 +587,44 @@ Use the `--link` option to generate `target_link_libraries()` commands with the This outputs the namespaced interface libraries (e.g., `ITK::ITKCommonModule`) suitable for direct use in `target_link_libraries()` commands. +#### FFTW + +FFTW is built or found by the `ITKFFTW` third-party module, and its include directories, libraries, and compile +options are carried by the `ITK::ITKFFTModule` interface library. Link that target, rather than naming FFTW +directories or libraries. Code that includes `fftw3.h` directly is covered too, because `ITK::ITKFFTModule` provides +the FFTW include directory. + +**Before:** +```cmake +find_package(ITK REQUIRED COMPONENTS ITKFFT) +include(${ITK_USE_FILE}) +include_directories(${ITK_FFTW_INCLUDE_PATH}) +link_directories(${ITK_FFTW_LIBDIR}) +add_executable(Example Example.cxx) +target_link_libraries(Example ${ITK_LIBRARIES} fftw3 fftw3f) +``` + +**After:** +```cmake +find_package(ITK REQUIRED COMPONENTS ITKFFT) +add_executable(Example Example.cxx) +target_link_libraries(Example PRIVATE ITK::ITKFFTModule) +``` + +To use FFTW without the FFT filters, request the module directly with `find_package(ITK COMPONENTS ITKFFTW)` and link +`ITK::ITKFFTWModule`. + +`ITK_FFTW_INCLUDE_PATH` and `ITK_FFTW_LIBDIR` are no longer defined by `ITKConfig.cmake`, and FFTW directories are no +longer appended to `ITK_INCLUDE_DIRS` and `ITK_LIBRARY_DIRS`. The bundled libraries are installed as `libitkfftw3*` +instead of `libfftw3*`, so linking `fftw3` or `-lfftw3` by name fails to find the library. Only the precisions ITK was +built with (`ITK_USE_FFTWF` and `ITK_USE_FFTWD`) are provided. + +When ITK is built against a system FFTW (`ITK_USE_SYSTEM_FFTW=ON`), downstream `find_package(ITK)` runs ITK's +installed `FindFFTW.cmake`, seeded with the include directory and library files ITK found. Set `FFTW_ROOT` to search +for a different installation instead. The ITK build options `FFTW_INCLUDE_PATH`, `FFTWD_BASE_LIB`, `FFTWF_BASE_LIB`, +`FFTWD_THREADS_LIB`, and `FFTWF_THREADS_LIB` are still honored; the library results are now full paths rather than +file names. A separate FFTW threads library is optional. + ### Migration for ITK Modules