diff --git a/CMakeLists.txt b/CMakeLists.txt index d314643763..e3b390abe2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1437 +1,1438 @@ set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.18) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND CMAKE_VERSION VERSION_LESS 3.19.2) message(FATAL_ERROR "\ CMake v${CMAKE_VERSION} is defective [1]. \ Please either downgrade to v3.18 or upgrade to at least v3.19.2.\n\ [1] https://gitlab.kitware.com/cmake/cmake/-/issues/21529") endif() #----------------------------------------------------------------------------- # Policies #----------------------------------------------------------------------------- #[[ T28060 https://cmake.org/cmake/help/v3.18/policy/CMP0091.html https://cmake.org/cmake/help/v3.18/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html We pass CMP0091 to all external projects as command-line argument: -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=OLD ]] cmake_policy(SET CMP0091 OLD) #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK VERSION 2018.04.99) include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # MITK Extension Feature #----------------------------------------------------------------------------- set(MITK_EXTENSION_DIRS "" CACHE STRING "") unset(MITK_ABSOLUTE_EXTENSION_DIRS) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) get_filename_component(MITK_ABSOLUTE_EXTENSION_DIR "${MITK_EXTENSION_DIR}" ABSOLUTE) list(APPEND MITK_ABSOLUTE_EXTENSION_DIRS "${MITK_ABSOLUTE_EXTENSION_DIR}") endforeach() set(MITK_DIR_PLUS_EXTENSION_DIRS "${MITK_SOURCE_DIR}" ${MITK_ABSOLUTE_EXTENSION_DIRS}) #----------------------------------------------------------------------------- # Update CMake module path #----------------------------------------------------------------------------- set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake) set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake") if(EXISTS "${MITK_CMAKE_EXTENSION_DIR}") list(APPEND CMAKE_MODULE_PATH "${MITK_CMAKE_EXTENSION_DIR}") endif() endforeach() #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- # Standard CMake macros include(FeatureSummary) include(CTest) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionEnableBuildConfiguration) include(mitkFunctionWhitelists) include(mitkFunctionAddExternalProject) include(mitkFunctionAddLibrarySearchPaths) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # Check miminum macOS version #----------------------------------------------------------------------------- # The minimum supported macOS version is 10.13. If you use a version less than 10.13, there is no guarantee that the build still works. if(APPLE) exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE macos_version) if (macos_version VERSION_LESS "10.13") message(WARNING "Detected macOS version \"${macos_version}\" is not supported anymore. Minimum required macOS version is at least 10.13.") endif() if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.13) message(WARNING "Detected macOS deployment target \"${CMAKE_OSX_DEPLOYMENT_TARGET}\" is not supported anymore. Minimum required macOS version is at least 10.13.") endif() endif() #----------------------------------------------------------------------------- # Check miminum compiler versions #----------------------------------------------------------------------------- if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # require at least gcc 4.9 as provided by ppa:ubuntu-toolchain-r/test for Ubuntu 14.04 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) message(FATAL_ERROR "GCC version must be at least 4.9 If you are using Ubuntu 14.04, you can easily install gcc and g++ 4.9 (or any later version available) in addition to your version ${CMAKE_CXX_COMPILER_VERSION}: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install gcc-4.9 g++-4.9 Make sure to explicitly specify these compilers when configuring MITK: CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-4.9 CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-4.9 For more information on the proposed PPA see the Toolchain Updates section of https://wiki.ubuntu.com/ToolChain.") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # require at least clang 3.4 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4) message(FATAL_ERROR "Clang version must be at least 3.4") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") # require at least clang 5.0 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) message(FATAL_ERROR "Apple Clang version must be at least 5.0") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # require at least Visual Studio 2017 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) message(FATAL_ERROR "Microsoft Visual Studio 2017 or newer required") endif() else() message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang (Linux or Apple), GCC and MSVC.") endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) else() set(GCC_VERSION 0) endif() set(MITK_CXX_STANDARD 14) set(CMAKE_CXX_EXTENSIONS 0) set(CMAKE_CXX_STANDARD ${MITK_CXX_STANDARD}) set(CMAKE_CXX_STANDARD_REQUIRED 1) # This is necessary to avoid problems with compile feature checks. # CMAKE_CXX_STANDARD seems to only set the -std=c++14 flag for targets. # However, compile flag checks also need to be done with -std=c++14. # The MITK_CXX14_FLAG variable is also used for external projects # build during the MITK super-build. mitkFunctionCheckCompilerFlags("-std=c++14" MITK_CXX14_FLAG) #----------------------------------------------------------------------------- # Warn if source or build path is too long #----------------------------------------------------------------------------- if(WIN32) set(_src_dir_length_max 50) set(_bin_dir_length_max 50) if(MITK_USE_SUPERBUILD) set(_src_dir_length_max 34) # _src_dir_length_max - strlen(ep/src/ITK-build) set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build) endif() string(LENGTH "${MITK_SOURCE_DIR}" _src_n) string(LENGTH "${MITK_BINARY_DIR}" _bin_n) # The warnings should be converted to errors if(_src_n GREATER _src_dir_length_max) message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- # ----------------------------------------- # General build options option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) option(MITK_FAST_TESTING "Disable long-running tests like packaging" OFF) option(MITK_XVFB_TESTING "Execute test drivers through xvfb-run" OFF) option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) option(MITK_BUILD_EXAMPLES "Build the MITK Examples" OFF) option(MITK_ENABLE_PIC_READER "Enable support for reading the DKFZ pic file format." ON) mark_as_advanced( MITK_XVFB_TESTING MITK_FAST_TESTING MITK_BUILD_ALL_APPS MITK_ENABLE_PIC_READER ) #----------------------------------------------------------------------------- # Set UI testing flags #----------------------------------------------------------------------------- if(MITK_XVFB_TESTING) set(MITK_XVFB_TESTING_COMMAND "xvfb-run" "--auto-servernum" CACHE STRING "Command and options to test through Xvfb") mark_as_advanced(MITK_XVFB_TESTING_COMMAND) endif(MITK_XVFB_TESTING) # ----------------------------------------- # Other options set(MITK_CUSTOM_REVISION_DESC "" CACHE STRING "Override MITK revision description") mark_as_advanced(MITK_CUSTOM_REVISION_DESC) set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "") include(CMakeExternals/ExternalProjectList.cmake) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMakeExternals") if(EXISTS "${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") include("${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") endif() endforeach() # ----------------------------------------- # Other MITK_USE_* options not related to # external projects build via the # MITK superbuild option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) option(MITK_USE_OpenMP "Use OpenMP" OFF) option(MITK_USE_Python3 "Use Python 3" OFF) #----------------------------------------------------------------------------- # Build configurations #----------------------------------------------------------------------------- set(_buildConfigs "Custom") file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake) foreach(_buildConfigFile ${_buildConfigFiles}) get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_buildConfigFile}) endforeach() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) file(GLOB _extBuildConfigFiles "${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake") foreach(_extBuildConfigFile ${_extBuildConfigFiles}) get_filename_component(_extBuildConfigFile "${_extBuildConfigFile}" NAME_WE) list(APPEND _buildConfigs "${_extBuildConfigFile}") endforeach() list(REMOVE_DUPLICATES _buildConfigs) endforeach() set(MITK_BUILD_CONFIGURATION "Custom" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs}) mitkFunctionEnableBuildConfiguration() mitkFunctionCreateWhitelistPaths(MITK) mitkFunctionFindWhitelists(MITK) # ----------------------------------------- # Qt version related variables option(MITK_USE_Qt5 "Use Qt 5 library" ON) if(MITK_USE_Qt5) if(WIN32) set(MITK_QT5_MINIMUM_VERSION 5.12.9) else() set(MITK_QT5_MINIMUM_VERSION 5.12) endif() set(MITK_QT5_COMPONENTS Concurrent OpenGL PrintSupport Script Sql Svg Widgets Xml XmlPatterns WebEngineWidgets UiTools Help LinguistTools) if(APPLE) list(APPEND MITK_QT5_COMPONENTS DBus) elseif(UNIX) list(APPEND MITK_QT5_COMPONENTS X11Extras) endif() # Hint at default install locations of Qt if(NOT Qt5_DIR) if(MSVC) set(_dir_candidates "C:/Qt") if(CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") elseif(CMAKE_GENERATOR MATCHES "Ninja") include(mitkFunctionGetMSVCVersion) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") endif() endif() if(_compilers MATCHES "[0-9]+") if (CMAKE_MATCH_0 EQUAL 2019) list(APPEND _compilers "msvc2017") # Binary compatible to 2019 endif() endif() else() set(_dir_candidates ~/Qt) if(APPLE) set(_compilers clang) else() list(APPEND _dir_candidates /opt/Qt) set(_compilers gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) foreach(_compiler ${_compilers}) list(APPEND _compilers64 "${_compiler}_64") endforeach() set(_compilers ${_compilers64}) endif() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) foreach(_compiler ${_compilers}) set(_glob_expression "${_dir_candidate}/5.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT5_HINTS ${_hints}) endforeach() endforeach() endif() find_package(Qt5 ${MITK_QT5_MINIMUM_VERSION} COMPONENTS ${MITK_QT5_COMPONENTS} REQUIRED HINTS ${MITK_QT5_HINTS}) endif() # ----------------------------------------- # Custom dependency logic option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") if(MITK_USE_cpprestsdk) if(WIN32 AND Qt5_DIR) set(_dir_candidate "${Qt5_DIR}/../../../../../Tools/OpenSSL/Win_x64") get_filename_component(_dir_candidate ${_dir_candidate} ABSOLUTE) if(EXISTS "${_dir_candidate}") set(OPENSSL_ROOT_DIR "${_dir_candidate}") endif() endif() find_package(OpenSSL QUIET) if(NOT OpenSSL_FOUND) set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n") if(UNIX) if(APPLE) set(openssl_message "${openssl_message}Please install it using your favorite package management " "system (i.e. Homebrew or MacPorts).\n") else() set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n") endif() else() set(openssl_message "${openssl_message}Please either install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\n" "Or use the Qt Maintenance tool to install:\n" " Developer and Designer Tools > OpenSSL Toolkit > OpenSSL 64-bit binaries\n") endif() set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by " "adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an " "OpenSSL installation. Make sure to clear variables of partly found " "versions of OpenSSL before, or they will be mixed up.") message(FATAL_ERROR ${openssl_message}) endif() list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system) if(UNIX) list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread) endif() list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES) set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE) endif() if(MITK_USE_Python3) set(MITK_USE_ZLIB ON CACHE BOOL "" FORCE) if(APPLE AND CMAKE_FRAMEWORK_PATH AND CMAKE_FRAMEWORK_PATH MATCHES "python3\\.?([0-9]+)") find_package(Python3 3.${CMAKE_MATCH_1} EXACT REQUIRED COMPONENTS Interpreter Development NumPy) else() find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy) endif() if(WIN32) string(REPLACE "\\" "/" Python3_STDARCH "${Python3_STDARCH}") string(REPLACE "\\" "/" Python3_STDLIB "${Python3_STDLIB}") string(REPLACE "\\" "/" Python3_SITELIB "${Python3_SITELIB}") endif() + link_directories(${Python3_LIBRARY_DIRS}) endif() if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() #----------------------------------------------------------------------------- # Pixel type multiplexing #----------------------------------------------------------------------------- # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}) string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}) foreach(_scalar_type ${_integral_types} ${_floating_types}) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,") endforeach() string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length) math(EXPR _length "${_length} - 1") string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() find_package(Git REQUIRED) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") # Print configuration summary message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL) return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # Organize MITK targets in folders #----------------------------------------------------------------------------- set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(MITK_ROOT_FOLDER "MITK" CACHE STRING "") mark_as_advanced(MITK_ROOT_FOLDER) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(GenerateExportHeader) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionUseModules) if( ${MITK_USE_MatchPoint} ) include(mitkFunctionCreateMatchPointDeployedAlgorithm) endif() include(mitkMacroConfigureItkPixelTypes) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) # Deprecated include(mitkMacroCreateCTKPlugin) #----------------------------------------------------------------------------- # Global CMake variables #----------------------------------------------------------------------------- # Required and enabled C++14 features for all MITK code. # These are added as PUBLIC compile features to all MITK modules. set(MITK_CXX_FEATURES cxx_auto_type cxx_decltype cxx_enum_forward_declarations cxx_extended_friend_declarations cxx_extern_templates cxx_final cxx_lambdas cxx_local_type_template_args cxx_long_long_type cxx_nullptr cxx_override cxx_range_for cxx_right_angle_brackets cxx_rvalue_references cxx_static_assert cxx_strong_enums cxx_template_template_parameters cxx_trailing_return_types cxx_variadic_macros ) if(NOT DEFINED CMAKE_DEBUG_POSTFIX) # We can't do this yet because the CTK Plugin Framework # cannot cope with a postfix yet. #set(CMAKE_DEBUG_POSTFIX d) endif() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- set(_default_LIBRARY_output_dir lib) set(_default_RUNTIME_output_dir bin) set(_default_ARCHIVE_output_dir lib) foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) else() set(CMAKE_${type}_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${_default_${type}_output_dir}) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY}) endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY} CACHE INTERNAL "Output directory for ${type} files.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- # Look for optional Doxygen package find_package(Doxygen) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) # Ask the user to show the console window for applications option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) if(NOT MITK_FAST_TESTING) if(MITK_CTEST_SCRIPT_MODE STREQUAL "Continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "Experimental") set(MITK_FAST_TESTING ON) endif() endif() if(NOT UNIX) set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms") endif() if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() # Configure pixel types used for ITK image access multiplexing mitkMacroConfigureItkPixelTypes() # Configure module naming conventions set(MITK_MODULE_NAME_REGEX_MATCH "^[A-Z].*$") set(MITK_MODULE_NAME_REGEX_NOT_MATCH "^[Mm][Ii][Tt][Kk].*$") set(MITK_DEFAULT_MODULE_NAME_PREFIX "Mitk") set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) set(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME 1) #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) # MITK_VERSION set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") if(MITK_VERSION_PATCH STREQUAL "99") set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}") endif() #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on macOS all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() endforeach() endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_CXX14_FLAG}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) if(WIN32) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN -DNOMINMAX") mitkFunctionCheckCompilerFlags("/wd4005" MITK_CXX_FLAGS) # warning C4005: macro redefinition mitkFunctionCheckCompilerFlags("/wd4231" MITK_CXX_FLAGS) # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation # the following line should be removed after fixing bug 17637 mitkFunctionCheckCompilerFlags("/wd4316" MITK_CXX_FLAGS) # warning C4316: object alignment on heap mitkFunctionCheckCompilerFlags("/wd4180" MITK_CXX_FLAGS) # warning C4180: qualifier applied to function type has no meaning mitkFunctionCheckCompilerFlags("/wd4251" MITK_CXX_FLAGS) # warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' endif() if(APPLE) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DGL_SILENCE_DEPRECATION") # Apple deprecated OpenGL in macOS 10.14 endif() if(NOT MSVC_VERSION) foreach(_flag -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Wno-error=gnu -Wno-error=unknown-pragmas # The strict-overflow warning is generated by ITK template code -Wno-error=strict-overflow -Woverloaded-virtual -Wstrict-null-sentinel #-Wold-style-cast #-Wsign-promo -Wno-error=deprecated-copy -Wno-array-bounds -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) set(MITK_CXX_FLAGS_RELEASE "-U_FORTIFY_SOURCES -D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake/PackageDepends") if(EXISTS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") list(APPEND MODULES_PACKAGE_DEPENDS_DIRS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") endif() endforeach() if(NOT MITK_USE_SYSTEM_Boost) set(Boost_NO_SYSTEM_PATHS 1) endif() set(Boost_USE_MULTITHREADED 1) set(Boost_USE_STATIC_LIBS 0) set(Boost_USE_STATIC_RUNTIME 0) set(Boost_ADDITIONAL_VERSIONS 1.74 1.74.0) # We need this later for a DCMTK workaround set(_dcmtk_dir_orig ${DCMTK_DIR}) # This property is populated at the top half of this file get_property(MITK_EXTERNAL_PROJECTS GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(MITK_USE_${ep} AND _package) if(_components) find_package(${_package} COMPONENTS ${_components} REQUIRED CONFIG) else() # Prefer config mode first because it finds external # Config.cmake files pointed at by _DIR variables. # Otherwise, existing Find.cmake files could fail. if(DEFINED ${_package}_DIR) #we store the information because it will be overwritten by find_package #and would get lost for all EPs that use on Find.cmake instead of config #files. set(_temp_EP_${_package}_dir ${${_package}_DIR}) endif(DEFINED ${_package}_DIR) find_package(${_package} QUIET CONFIG) string(TOUPPER "${_package}" _package_uc) if(NOT (${_package}_FOUND OR ${_package_uc}_FOUND)) if(DEFINED _temp_EP_${_package}_dir) set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externaly set dir of the package ${_package}" FORCE) endif(DEFINED _temp_EP_${_package}_dir) find_package(${_package} REQUIRED) endif() endif() endif() endforeach() # Ensure that the MITK CMake module path comes first set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) if(MITK_USE_DCMTK) # Due to the preferred CONFIG mode in find_package calls above, # the DCMTKConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMTK. if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*") # Help our FindDCMTK.cmake script find our super-build DCMTK set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) else() # Use the original value set(DCMTK_DIR ${_dcmtk_dir_orig}) endif() find_package(DCMTK REQUIRED MODULE) endif() if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() if(MITK_USE_OpenIGTLink) link_directories(${OpenIGTLink_LIBRARY_DIRS}) endif() if(MITK_USE_OpenCL) find_package(OpenCL REQUIRED) endif() if(MITK_USE_OpenMP) find_package(OpenMP REQUIRED COMPONENTS CXX) else() find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) elseif(APPLE AND OpenMP_libomp_LIBRARY AND NOT OpenMP_CXX_LIB_NAMES) set(OpenMP_CXX_LIB_NAMES libomp CACHE STRING "" FORCE) get_filename_component(openmp_lib_dir "${OpenMP_libomp_LIBRARY}" DIRECTORY) set(openmp_include_dir "${openmp_lib_dir}/../include") if(EXISTS "${openmp_include_dir}") get_filename_component(openmp_include_dir "${openmp_include_dir}" REALPATH) set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${openmp_include_dir}" CACHE STRING "" FORCE) find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) endif() endif() endif() endif() # Qt support if(MITK_USE_Qt5) find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) # at least Core required get_target_property(_qmake_exec Qt5::qmake LOCATION) execute_process(COMMAND ${_qmake_exec} -query QT_INSTALL_BINS RESULT_VARIABLE _result OUTPUT_VARIABLE QT_BINARY_DIR ERROR_VARIABLE _error ) string(STRIP "${QT_BINARY_DIR}" QT_BINARY_DIR) if(_result OR NOT EXISTS "${QT_BINARY_DIR}") message(FATAL_ERROR "Could not determine Qt binary directory: ${_result} ${QT_BINARY_DIR} ${_error}") endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt5 qhelpgenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt5 qcollectiongenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant assistant-qt5 assistant5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) mark_as_advanced(QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE ) if(MITK_USE_BLUEBERRY) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP) # Sanity checks for in-application BlueBerry plug-in help generation if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT MITK_USE_Qt5) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt5 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE) endif() endif() if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP) message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON") endif() endif() endif() #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) #[[ See T27701 # Initial cache for ProjectTemplate and PluginGenerator tests configure_file( CMake/mitkTestInitialCache.txt.in ${MITK_BINARY_DIR}/mitkTestInitialCache.txt @ONLY )]] # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch (const std::exception& e) { fprintf(stderr, \"%s\\n\", e.what()); return EXIT_FAILURE; } catch (...) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; }") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Utilities) set(MITK_WRAP_PYTHON_ENABLED OFF) if(MITK_USE_SWIG AND MITK_USE_Python3) find_package(SWIG 4.0.2 COMPONENTS python) if(SWIG_FOUND) option(MITK_WRAP_PYTHON "" ON) if(MITK_WRAP_PYTHON) set(MITK_WRAP_PYTHON_ENABLED ON) endif() else() message(SEND_ERROR "SWIG not found!") endif() endif() add_subdirectory(Modules) include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake") mitkFunctionWhitelistModules(MITK MITK_MODULES) set(MITK_ROOT_FOLDER_BACKUP "${MITK_ROOT_FOLDER}") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) get_filename_component(MITK_ROOT_FOLDER "${MITK_EXTENSION_DIR}" NAME) set(MITK_MODULES_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Modules") if(EXISTS "${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") set(MITK_MODULES "") include("${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") foreach(mitk_module ${MITK_MODULES}) add_subdirectory("${MITK_MODULES_EXTENSION_DIR}/${mitk_module}" "Modules/${mitk_module}") endforeach() endif() set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) endforeach() set(MITK_ROOT_FOLDER "${MITK_ROOT_FOLDER_BACKUP}") add_subdirectory(Wrapping) set(MITK_DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/Documentation/Doxygen" CACHE PATH "Output directory for doxygen generated documentation.") if(MITK_USE_BLUEBERRY) include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") mitkFunctionWhitelistPlugins(MITK MITK_PLUGINS) set(mitk_plugins_fullpath "") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() set(MITK_PLUGIN_REGEX_LIST "") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Plugins") if(EXISTS "${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") set(MITK_PLUGINS "") include("${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath "${MITK_PLUGINS_EXTENSION_DIR}/${mitk_plugin}") endforeach() endif() endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() if(MITK_BUILD_EXAMPLES) include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake") set(mitk_example_plugins_fullpath ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb MITK_PLUGIN_REGEX_LIST OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options set(mitk_apps_fullpath "") foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) if(${option_name}) list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}") endif() endforeach() endif() endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- if(DOXYGEN_FOUND) add_subdirectory(Documentation) endif() #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) set(ALL_MITK_APPS "") set(activated_apps_no 0) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) list(APPEND ALL_MITK_APPS "${MITK_EXTENSION_DIR}/Applications/${directory_name}^^${option_name}^^${executable_name}") if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() endif() endforeach() list(LENGTH ALL_MITK_APPS app_count) if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${ALL_MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${target_dir}/CPackOptions.cmake") include("${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${target_dir}/CPackConfig.cmake") configure_file(${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${executable_name}") endif() endforeach() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- # ---------------- Export targets ----------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() get_target_property(deprecated_module ${target_to_export} MITK_MODULE_DEPRECATED_SINCE) if(deprecated_module) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_MODULE_DEPRECATED_SINCE \"${deprecated_module}\")") endif() endforeach() # ---------------- External projects ----------------- get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) set(MITK_CONFIG_EXTERNAL_PROJECTS ) #string(REPLACE "^^" ";" _mitk_external_projects ${MITK_EXTERNAL_PROJECTS}) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} set(MITK_USE_${ep} ${MITK_USE_${ep}}) set(MITK_${ep}_DIR \"${${ep}_DIR}\") set(MITK_${ep}_COMPONENTS ${_components}) ") endforeach() foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(_components) set(_components_arg COMPONENTS \${_components}) else() set(_components_arg) endif() if(_package) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} if(MITK_USE_${ep}) set(${ep}_DIR \${MITK_${ep}_DIR}) if(MITK_${ep}_COMPONENTS) mitkMacroFindDependency(${_package} COMPONENTS \${MITK_${ep}_COMPONENTS}) else() mitkMacroFindDependency(${_package}) endif() endif()") endif() endforeach() # ---------------- Tools ----------------- configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) # ---------------- Configure files ----------------- configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) if(MSVC AND TARGET MitkWorkbench) set_directory_properties(PROPERTIES VS_STARTUP_PROJECT MitkWorkbench) endif() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt") add_subdirectory("${MITK_APPLICATIONS_EXTENSION_DIR}" "Applications") endif() endforeach() #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Modules/PythonService/PythonPath.h.in b/Modules/PythonService/PythonPath.h.in index 84ac58f10a..ae0a8942f8 100644 --- a/Modules/PythonService/PythonPath.h.in +++ b/Modules/PythonService/PythonPath.h.in @@ -1,28 +1,16 @@ -#ifdef _DEBUG -#define PYTHON_PATH_BUILD_TYPE "/Debug" -#define SWIG_MITK_WRAPPING "@MITK_BINARY_DIR@/lib/Debug" -#else -#define PYTHON_PATH_BUILD_TYPE "/Release" -#define SWIG_MITK_WRAPPING "@MITK_BINARY_DIR@/lib/Release" -#endif - -#define SWIG_PYTHON_BUILD_OUTPUT "@MITK_BINARY_DIR@/Wrapping/Python/build" - -#define PYTHON_LIBRARY "@PYTHON_LIBRARY@" - -#define WRAPPING_BINARY_DIR "@MITK_BINARY_DIR@/Wrapping" +#define EP_DLL_DIR "@MITK_EXTERNAL_PROJECT_PREFIX@/bin" -#define LIB_DIR "@MITK_BINARY_DIR@/lib" +#define BIN_DIR "@MITK_BINARY_DIR@/bin" #ifdef WIN32 //Todo: windows system python #define EXTERNAL_SITE_PACKAGES "@MITK_EXTERNAL_PROJECT_PREFIX@/lib/python@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@/site-packages" #define EXTERNAL_DIST_PACKAGES "@MITK_EXTERNAL_PROJECT_PREFIX@/lib/python@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@/dist-packages" #else #define EXTERNAL_SITE_PACKAGES "@MITK_EXTERNAL_PROJECT_PREFIX@/lib/python@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@/site-packages" #define EXTERNAL_DIST_PACKAGES "@MITK_EXTERNAL_PROJECT_PREFIX@/lib/python@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@/dist-packages" #endif #ifndef WIN32 #define PYTHON_INCLUDE "@Python3_INCLUDE_DIR@" #endif diff --git a/Modules/PythonService/mitkPythonService.cpp b/Modules/PythonService/mitkPythonService.cpp index 2cd83252d3..f153c44b4c 100644 --- a/Modules/PythonService/mitkPythonService.cpp +++ b/Modules/PythonService/mitkPythonService.cpp @@ -1,639 +1,633 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPythonService.h" #include #ifdef _DEBUG #undef _DEBUG #include #define _DEBUG #else #include #endif #include "PythonPath.h" #include #include #include #include #include #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include #ifndef WIN32 #include #endif #include "swigpyrun.h" #include typedef itksys::SystemTools ist; mitk::PythonService::PythonService() : m_ItkWrappingAvailable(true), m_ErrorOccured(false) { // for Linux, libpython needs to be opened before initializing the Python Interpreter to enable imports #ifndef WIN32 std::vector pythonIncludeVector; // get libpython file to open (taken from "Python3_INCLUDE_DIR" variable from CMake to dynamically do it for different python versions) boost::split(pythonIncludeVector, PYTHON_INCLUDE, boost::is_any_of( "/" ) ); std::string libPython = "lib"+pythonIncludeVector[pythonIncludeVector.size()-1]+".so"; dlopen(libPython.c_str(), RTLD_LAZY | RTLD_GLOBAL); #endif // Initialize Python interpreter and ensure global interpreter lock in order to execute python code safely if (!Py_IsInitialized()) { Py_Initialize(); } PyGILState_Ensure(); std::string programPath = mitk::IOUtil::GetProgramPath(); std::replace(programPath.begin(), programPath.end(), '\\', '/'); programPath.append("/"); MITK_INFO << programPath; std::string pythonCommand; // execute a string which imports packages that are needed and sets paths to directories pythonCommand.append("import SimpleITK as sitk\n"); pythonCommand.append("import SimpleITK._SimpleITK as _SimpleITK\n"); pythonCommand.append("import numpy\n"); pythonCommand.append("import site, sys\n"); pythonCommand.append("import os\n"); pythonCommand.append("sys.path.append('')\n"); pythonCommand.append("sys.path.append('" + programPath + "')\n"); - pythonCommand.append("sys.path.append('" + std::string(SWIG_MITK_WRAPPING) + "')\n"); - pythonCommand.append("sys.path.append('"+std::string(WRAPPING_BINARY_DIR)+"')\n"); - pythonCommand.append("sys.path.append('"+std::string(LIB_DIR)+"')\n"); + pythonCommand.append("sys.path.append('" + std::string(BIN_DIR) + "')\n"); pythonCommand.append("sys.path.append('" +std::string(EXTERNAL_DIST_PACKAGES) + "')\n"); pythonCommand.append("\nsite.addsitedir('"+std::string(EXTERNAL_SITE_PACKAGES)+"')\n"); // in python 3.8 onwards, the path system variable is not longer used to find dlls // that's why the dlls that are needed for swig wrapping need to be searched manually std::string searchForDll = "if sys.version_info[1] > 7:\n" - " for root, dirs, files in os.walk('" + std::string(SWIG_PYTHON_BUILD_OUTPUT) +"'):\n" - " for file in files:\n" - " if file.endswith('.dll'):\n" - " os.add_dll_directory(root)\n" - " break\n"; + " os.add_dll_directory('"+std::string(EP_DLL_DIR)+"')\n"; pythonCommand.append(searchForDll); if (PyRun_SimpleString(pythonCommand.c_str()) == -1) { MITK_ERROR << "Something went wrong in setting the path in Python"; } PyObject *main = PyImport_AddModule("__main__"); m_GlobalDictionary = PyModule_GetDict(main); m_LocalDictionary = m_GlobalDictionary; m_ThreadState = PyEval_SaveThread(); } mitk::PythonService::~PythonService() { } void mitk::PythonService::AddRelativeSearchDirs(std::vector< std::string > dirs) { for (const auto& dir : dirs) { try { std::string path = std::string(MITK_ROOT) + dir; // sys.path.append enables to call scripts which are located in the given path std::string pythonCommand = "import sys\nsys.path.append('" + path + "')\n"; this->Execute(pythonCommand.c_str()); } catch (const mitk::Exception&) { mitkThrow() << "An error occured setting the relative project path"; } } } void mitk::PythonService::AddAbsoluteSearchDirs(std::vector< std::string > dirs) { for (const auto dir : dirs) { try { // sys.path.append enables to call scripts which are located in the given path std::string pythonCommand = "import sys\nsys.path.append('" + dir + "')\n"; this->Execute(pythonCommand.c_str()); } catch (const mitk::Exception&) { mitkThrow() << "An error occured setting the absolute project path"; } } } std::string mitk::PythonService::Execute(const std::string &stdpythonCommand, int commandType) { if (!Py_IsInitialized()) { Py_Initialize(); } std::string result = ""; PyGILState_Ensure(); try { // command type is start symbol for python interpreter switch (commandType) { case IPythonService::SINGLE_LINE_COMMAND: commandType = Py_single_input; break; case IPythonService::MULTI_LINE_COMMAND: commandType = Py_file_input; break; case IPythonService::EVAL_COMMAND: commandType = Py_eval_input; break; default: commandType = Py_file_input; } // executes python code given as string // if result is NULL, an error occured PyObject* executionResult = PyRun_String(stdpythonCommand.c_str(), commandType, m_GlobalDictionary, m_LocalDictionary); // notifies registered observers that command has been executed this->NotifyObserver(stdpythonCommand); // if no error occured, the result is represented as string which is returned if (executionResult) { PyObject *objectsRepresentation = PyObject_Repr(executionResult); const char *resultChar = PyUnicode_AsUTF8(objectsRepresentation); result = std::string(resultChar); m_ThreadState = PyEval_SaveThread(); m_ErrorOccured = false; } else { m_ErrorOccured = true; mitkThrow() << "An error occured while running the Python code"; } } catch (const mitk::Exception& ) { PyErr_Print(); m_ThreadState = PyEval_SaveThread(); throw; } return result; } void mitk::PythonService::ExecuteScript(const std::string &pythonScript) { // read given file as string std::ifstream t(pythonScript.c_str()); std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); t.close(); // pass the file as string to the Execute() function try { this->Execute(str.c_str(), MULTI_LINE_COMMAND); } catch (const mitk::Exception&) { throw; } } std::vector mitk::PythonService::GetVariableStack() { // variables are returned as a list of type mitk::PythonVariable std::vector list; PyGILState_Ensure(); try { // vaiables are taken from the main module // get dictionary where these variables are stored PyObject *dict = PyImport_GetModuleDict(); PyObject *object = PyDict_GetItemString(dict, "__main__"); if (!object) { mitkThrow() << "An error occured getting the Dictionary"; } PyObject *dirMain = PyObject_Dir(object); PyObject *tempObject = nullptr; if (dirMain) { std::string name, attrValue, attrType; //iterate over python list of variables to get desired representation and store in returned variable list for (int i = 0; i < PyList_Size(dirMain); i++) { // get variable at current index tempObject = PyList_GetItem(dirMain, i); if (!tempObject) { mitkThrow() << "An error occured getting an item from the dictionary"; } // get variable name as string PyObject *objectsRepresentation = PyObject_Repr(tempObject); const char *objectChar = PyUnicode_AsUTF8(objectsRepresentation); std::string name = std::string(objectChar); name = name.substr(1, name.size() - 2); // get variable type as string tempObject = PyObject_GetAttrString(object, name.c_str()); if (!tempObject) { mitkThrow() << "Could not get the attribute to determine type"; } attrType = tempObject->ob_type->tp_name; // get variable value as string PyObject *valueStringRepresentation = PyObject_Repr(tempObject); const char *valueChar = PyUnicode_AsUTF8(valueStringRepresentation); std::string attrValue = std::string(valueChar); // build variable type to store mitk::PythonVariable var; var.m_Name = name; var.m_Value = attrValue; var.m_Type = attrType; list.push_back(var); } } m_ThreadState = PyEval_SaveThread(); } catch (const mitk::Exception&) { m_ThreadState = PyEval_SaveThread(); throw; } return list; } std::string mitk::PythonService::GetVariable(const std::string& name) { // get all variables from python context std::vector allVars; try { allVars = this->GetVariableStack(); } catch (const mitk::Exception&) { mitkThrow() << "Error getting the variable stack"; } // search for variable with given name for (const auto& var: allVars) { if (var.m_Name == name) { return var.m_Value; } } return ""; } bool mitk::PythonService::DoesVariableExist(const std::string& name) { bool varExists = false; // get all variables from python context std::vector allVars; try { allVars = this->GetVariableStack(); } catch (const mitk::Exception&) { mitkThrow() << "Error getting the variable stack"; } // check if variable with given name exists in context for (const auto& var: allVars) { if (var.m_Name == name) { varExists = true; break; } } return varExists; } void mitk::PythonService::AddPythonCommandObserver(mitk::PythonCommandObserver *observer) { //only add observer if it isn't already in list of observers if (!(std::find(m_Observer.begin(), m_Observer.end(), observer) != m_Observer.end())) { m_Observer.push_back(observer); } } void mitk::PythonService::RemovePythonCommandObserver(mitk::PythonCommandObserver *observer) { // iterate over all registered observers and remove the passed observer for (std::vector::iterator iter = m_Observer.begin(); iter != m_Observer.end(); ++iter) { if (*iter == observer) { m_Observer.erase(iter); break; } } } void mitk::PythonService::NotifyObserver(const std::string &command) { // call CommandExecuted() from observer interface in order to inform observers that command has been executed for (const auto& observer: this->m_Observer) { observer->CommandExecuted(command); } } int mitk::PythonService::GetNumberOfObserver() { return m_Observer.size(); } bool mitk::PythonService::IsSimpleItkPythonWrappingAvailable() { return m_ItkWrappingAvailable; } bool mitk::PythonService::CopyToPythonAsSimpleItkImage(mitk::Image::Pointer image, const std::string &stdvarName) { // this string is a python command which consists of two functions // setup() is called in order to create an image variable on python side. At this point this is a MITK image // convert_to_sitk() converts the MITK image from the previous step into a SimpleITK image. Spacing and Origin need to be set manually // as these informations get lost when converting the image to an numpy array and back (only contains pixel information) std::string transferToPython = "import pyMITK\n" "import SimpleITK as sitk\n" "mitk_image = None\n" "geometry = None\n" +stdvarName+" = None\n" "\n" "def setup(image_from_cxx):\n" " print ('setup called with', image_from_cxx)\n" " global mitk_image\n" " mitk_image = image_from_cxx\n" "\n" "def convert_to_sitk(spacing, origin):\n" " np = pyMITK.GetArrayViewFromImage(mitk_image)\n" " global "+stdvarName+"\n" " "+stdvarName+" = sitk.GetImageFromArray(np)\n" " npSpacingArray = numpy.array(spacing , dtype=float)\n" " "+stdvarName+".SetSpacing(npSpacingArray)\n" " npOriginArray = numpy.array(origin , dtype=float)\n" " " +stdvarName +".SetOrigin(npOriginArray)\n"; // execute code to make the implemented functions available in the Python context (function is not executed, only definition loaded in Python) this->Execute(transferToPython); mitk::Image::Pointer *img = ℑ PyGILState_Ensure();//PyGILState_STATE gState = PyGILState_Ensure(); //necessary for transfer array from C++ to Python import_array(); PyObject *main = PyImport_ImportModule("__main__"); if (main == NULL) { mitkThrow() << "Something went wrong getting the main module"; } // create new pyobject from given image using SWIG swig_type_info *pTypeInfo = nullptr; pTypeInfo = SWIG_TypeQuery("_p_itk__SmartPointerT_mitk__Image_t"); int owned = 0; PyObject *pInstance = SWIG_NewPointerObj(reinterpret_cast(img), pTypeInfo, owned); if (pInstance == NULL) { mitkThrow() << "Something went wrong creating the Python instance of the image"; } // get image spacing and convert it to Python array int numberOfImageDimension = image->GetDimension(); auto spacing = image->GetGeometry()->GetSpacing(); auto *spacingptr = &spacing; npy_intp dims[] = {numberOfImageDimension}; PyObject *spacingArray = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, (void *)spacingptr); // get image origin and convert it to Python array auto origin = image->GetGeometry()->GetOrigin(); auto *originptr = &origin; PyObject *originArray = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, (void *)originptr); // transfer image to python context by calling setup() function which was defined in Python string above PyObject *setup = PyObject_GetAttrString(main, "setup"); PyObject *result = PyObject_CallFunctionObjArgs(setup, pInstance, NULL); if (result == NULL) { mitkThrow() << "Something went wrong setting the MITK image in Python"; } // convert MITK image in Python context to SimpleITK image and tranfer correct origin and spacing calling convert_to_sitk() from above PyObject *convert = PyObject_GetAttrString(main, "convert_to_sitk"); result = PyObject_CallFunctionObjArgs(convert,spacingArray, originArray, NULL); if (result == NULL) { mitkThrow() << "Something went wrong converting the MITK image to a SimpleITK image"; } m_ThreadState = PyEval_SaveThread(); return true; } mitk::Image::Pointer mitk::PythonService::CopySimpleItkImageFromPython(const std::string &stdvarName) { mitk::Image::Pointer mitkImage; // Python code to convert SimpleITK image to MITK image std::string convertToMITKImage = "import SimpleITK as sitk\n" "import pyMITK\n" "numpy_array = sitk.GetArrayViewFromImage("+stdvarName+")\n" "mitk_image = pyMITK.GetImageFromArray(numpy_array)\n" "spacing = "+stdvarName+".GetSpacing()\n" "origin = "+stdvarName +".GetOrigin()\n"; // after executing this, the desired mitk image is available in the Python context with the variable name mitk_image this->Execute(convertToMITKImage); PyGILState_Ensure();//PyGILState_STATE gState = PyGILState_Ensure(); // get dictionary with variables from main context PyObject *main = PyImport_AddModule("__main__"); PyObject *globals = PyModule_GetDict(main); // get mitk_image from Python context PyObject *pyImage = PyDict_GetItemString(globals, "mitk_image"); if (pyImage==NULL) { mitkThrow() << "Could not get image from Python"; } // res is status variable to check if result when getting the image from Python context is OK int res = 0; void *voidImage; swig_type_info *pTypeInfo = nullptr; pTypeInfo = SWIG_TypeQuery("_p_itk__SmartPointerT_mitk__Image_t"); // get image from Python context as C++ void pointer res = SWIG_ConvertPtr(pyImage, &voidImage, pTypeInfo, 0); if (!SWIG_IsOK(res)) { mitkThrow() << "Could not cast image to C++ type"; } // cast C++ void pointer to mitk::Image::Pointer mitkImage = *(reinterpret_cast(voidImage)); // as spacing and origin again got lost when transferring between SimpleITK and MITK (see CopyToPythonAsSimpleItkImage()) we need to set them manually // get spacing from Python context PyObject *spacing = PyDict_GetItemString(globals, "spacing"); mitk::Vector3D spacingMITK; // convert Python Array to C++ vector for (int i = 0; i < PyTuple_GET_SIZE(spacing); i++) { PyObject *spacingPy = PyTuple_GetItem(spacing, i); double elem = PyFloat_AsDouble(spacingPy); spacingMITK[i] = elem; } // get origin from Python context PyObject *origin = PyDict_GetItemString(globals, "origin"); mitk::Point3D originMITK; // convert Python Array to Point3D for (int i = 0; i < PyTuple_GET_SIZE(origin); i++) { PyObject *originPy = PyTuple_GetItem(origin, i); double elem = PyFloat_AsDouble(originPy); originMITK[i] = elem; } m_ThreadState = PyEval_SaveThread(); // set origin and spacing correctly mitkImage->GetGeometry()->SetSpacing(spacingMITK); mitkImage->GetGeometry()->SetOrigin(originMITK); return mitkImage; } bool mitk::PythonService::CopyMITKImageToPython(mitk::Image::Pointer &image, const std::string &stdvarName) { // this string is a python command which consists one functions // setup() is called in order to create an image variable on python side. This is the MITK image which is passed std::string transferToPython = "import pyMITK\n" + stdvarName +" = None\n" "\n" "def setup(image_from_cxx):\n" " print ('setup called with', image_from_cxx)\n" " global "+stdvarName+"\n" " "+stdvarName+" = image_from_cxx\n"; // execute code to make the implemented functions available in the Python context (function is not executed, only // definition loaded in Python) this->Execute(transferToPython); mitk::Image::Pointer *img = ℑ // load main context to have access to defined function from above PyGILState_Ensure();//PyGILState_STATE gState = PyGILState_Ensure(); PyObject *main = PyImport_ImportModule("__main__"); if (main == NULL) { mitkThrow() << "Something went wrong getting the main module"; } // create new pyobject from given image using SWIG swig_type_info *pTypeInfo = nullptr; pTypeInfo = SWIG_TypeQuery("_p_itk__SmartPointerT_mitk__Image_t"); int owned = 0; PyObject *pInstance = SWIG_NewPointerObj(reinterpret_cast(img), pTypeInfo, owned); if (pInstance == NULL) { mitkThrow() << "Something went wrong creating the Python instance of the image"; } // call setup() function in Python with created PyObject of image PyObject *setup = PyObject_GetAttrString(main, "setup"); PyObject *result = PyObject_CallFunctionObjArgs(setup, pInstance, NULL); if (result == NULL) { mitkThrow() << "Something went wrong setting the MITK image in Python"; } m_ThreadState = PyEval_SaveThread(); return true; } mitk::Image::Pointer GetImageFromPyObject(PyObject *pyImage) { // helper function to get a MITK image from a PyObject mitk::Image::Pointer mitkImage; // res is status variable to check if result when getting the image from Python context is OK int res = 0; void *voidImage; swig_type_info *pTypeInfo = nullptr; pTypeInfo = SWIG_TypeQuery("_p_itk__SmartPointerT_mitk__Image_t"); // get image from Python context as C++ void pointer res = SWIG_ConvertPtr(pyImage, &voidImage, pTypeInfo, 0); if (!SWIG_IsOK(res)) { mitkThrow() << "Could not cast image to C++ type"; } // cast C++ void pointer to mitk::Image::Pointer mitkImage = *(reinterpret_cast(voidImage)); return mitkImage; } mitk::Image::Pointer mitk::PythonService::CopyMITKImageFromPython(const std::string &stdvarName) { mitk::Image::Pointer mitkImage; PyGILState_Ensure(); // get image from Python context as PyObject PyObject *main = PyImport_AddModule("__main__"); PyObject *globals = PyModule_GetDict(main); PyObject *pyImage = PyDict_GetItemString(globals, stdvarName.c_str()); if (pyImage==NULL) { mitkThrow() << "Could not get image from Python"; } // convert PyObject to mitk::Image mitkImage = GetImageFromPyObject(pyImage); m_ThreadState = PyEval_SaveThread(); return mitkImage; } std::vector mitk::PythonService::CopyListOfMITKImagesFromPython(const std::string &listVarName) { std::vector mitkImages; PyGILState_Ensure();//PyGILState_STATE gState = PyGILState_Ensure(); // get the list of the variable name as python list PyObject *main = PyImport_AddModule("__main__"); PyObject *globals = PyModule_GetDict(main); PyObject *pyImageList = PyDict_GetItemString(globals, listVarName.c_str()); if (pyImageList == NULL) { mitkThrow() << "Could not get image list from Python"; } // iterate over python list of images and convert them to C++ mitk::Image for (int i = 0; i < PyList_GET_SIZE(pyImageList);i++) { PyObject *pyImage = PyList_GetItem(pyImageList, i); mitk::Image::Pointer img = GetImageFromPyObject(pyImage); mitkImages.push_back(img); } m_ThreadState = PyEval_SaveThread(); return mitkImages; } bool mitk::PythonService::PythonErrorOccured() const { return m_ErrorOccured; } diff --git a/Wrapping/Python/CMakeLists.txt b/Wrapping/Python/CMakeLists.txt index cc3a118c0f..8891f1ba15 100644 --- a/Wrapping/Python/CMakeLists.txt +++ b/Wrapping/Python/CMakeLists.txt @@ -1,85 +1,86 @@ include(mitkTargetLinkLibrariesWithDynamicLookup) project(MITK_Python) set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERNAL "" FORCE) set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERNAL "" FORCE) mitk_check_dynamic_lookup(MODULE SHARED MITK_UNDEFINED_SYMBOLS_ALLOWED ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) option(MITK_PYTHON_THREADS "Enable threaded python usage by unlocking the GIL." ON ) option(MITK_PYTHON_EGG "Add building of python eggs to the dist target." OFF ) option(MITK_PYTHON_WHEEL "Add building of python wheels to the dist target." ON ) mark_as_advanced( MITK_PYTHON_THREADS MITK_PYTHON_EGG MITK_PYTHON_WHEEL ) set(libraries MitkCore MitkCLCore MitkCLUtilities ITKCommon MitkSegmentation MitkMultilabel MitkDICOM MitkDICOMImageIO MitkRT ) if(MITK_USE_MatchPoint) list(APPEND libraries MitkMatchPointRegistration ) endif() mitkSwigPrepareFiles(pyMITK MITK.i ${libraries}) set(CMAKE_SWIG_FLAGS ${CMAKE_SWIG_GLOBAL_FLAGS} -features autodoc=1 -keyword) if(MITK_PYTHON_THREADS) list(APPEND CMAKE_SWIG_FLAGS -threads) endif() -set(CMAKE_SWIG_OUTDIR ${MITK_BINARY_DIR}/lib/Release) - +set(CMAKE_SWIG_OUTDIR ${MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY}) set(UseSWIG_TARGET_NAME_PREFERENCE STANDARD) swig_add_library(pyMITK LANGUAGE python SOURCES MITK.i) set_property(TARGET pyMITK PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Wrapping") - target_link_libraries(pyMITK ${libraries}) mitk_target_link_libraries_with_dynamic_lookup(${SWIG_MODULE_pyMITK_REAL_NAME} ${Python3_LIBRARIES}) +set_target_properties(pyMITK PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY}" +) if(DEFINED SKBUILD) #message("SKBuild exists") install( FILES ${CMAKE_CURRENT_BINARY_DIR}/pyMITK.py ${CMAKE_CURRENT_SOURCE_DIR}/Packaging/__init__.py #${MITK_DOC_FILES} DESTINATION pyMITK COMPONENT Runtime ) install( TARGETS ${SWIG_MODULE_pyMITK_REAL_NAME} RUNTIME DESTINATION pyMITK LIBRARY DESTINATION pyMITK COMPONENT Runtime ) else() #message("SKBuild missing") include(LegacyPackaging.cmake) endif() diff --git a/Wrapping/Python/MITK.i b/Wrapping/Python/MITK.i index f7a188f7ed..14e1c0c88e 100644 --- a/Wrapping/Python/MITK.i +++ b/Wrapping/Python/MITK.i @@ -1,167 +1,172 @@ +%begin %{ +#ifdef _MSC_VER +#define SWIG_PYTHON_INTERPRETER_NO_DEBUG +#endif +%} %module pyMITK %include %{ #include "mitkNumpyArrayConversion.cxx" %} // Numpy array conversion support %native(_GetMemoryViewFromImage) PyObject *mitk_GetMemoryViewFromImage( PyObject *self, PyObject *args ); %native(_SetImageFromArray) PyObject *mitk_SetImageFromArray( PyObject *(self), PyObject *args ); %pythoncode %{ HAVE_NUMPY = True try: import numpy except ImportError: HAVE_NUMPY = False def _get_numpy_dtype( mitkImage ): """Given a MITK image, returns the numpy.dtype which describes the data""" if not HAVE_NUMPY: raise ImportError('Numpy not available.') # this is a mapping from MITK's pixel id to numpy's dtype _mitk_np = {ComponentTypeUInt8:numpy.uint8, ComponentTypeUInt16:numpy.uint16, ComponentTypeUInt32:numpy.uint32, ComponentTypeInt8:numpy.int8, ComponentTypeInt16:numpy.int16, ComponentTypeInt32:numpy.int32, ComponentTypeFloat:numpy.float32, ComponentTypeDouble:numpy.float64, } return _mitk_np[ mitkImage.GetPixelType().GetComponentType() ] def _get_mitk_pixelid(numpy_array_type): """Returns a MITK PixelID given a numpy array.""" if not HAVE_NUMPY: raise ImportError('Numpy not available.') # This is a Mapping from numpy array types to sitks pixel types. _np_mitk = {numpy.character:ComponentTypeUInt8, numpy.uint8:ComponentTypeUInt8, numpy.uint16:ComponentTypeUInt16, numpy.uint32:ComponentTypeUInt32, numpy.int8:ComponentTypeInt8, numpy.int16:ComponentTypeInt16, numpy.int32:ComponentTypeInt32, numpy.float32:ComponentTypeFloat, numpy.float64:ComponentTypeDouble, } try: return _np_mitk[numpy_array_type.dtype] except KeyError: for key in _np_mitk: if numpy.issubdtype(numpy_array_type.dtype, key): return _np_mitk[key] raise TypeError('dtype: {0} is not supported.'.format(numpy_array_type.dtype)) def _get_sitk_vector_pixelid(numpy_array_type): """Returns a MITK vecotr PixelID given a numpy array.""" if not HAVE_NUMPY: raise ImportError('Numpy not available.') # This is a Mapping from numpy array types to sitks pixel types. _np_sitk = {numpy.character:sitkVectorUInt8, numpy.uint8:sitkVectorUInt8, numpy.uint16:sitkVectorUInt16, numpy.uint32:sitkVectorUInt32, numpy.uint64:sitkVectorUInt64, numpy.int8:sitkVectorInt8, numpy.int16:sitkVectorInt16, numpy.int32:sitkVectorInt32, numpy.int64:sitkVectorInt64, numpy.float32:sitkVectorFloat32, numpy.float64:sitkVectorFloat64, } try: return _np_sitk[numpy_array_type.dtype] except KeyError: for key in _np_sitk: if numpy.issubdtype(numpy_array_type.dtype, key): return _np_sitk[key] raise TypeError('dtype: {0} is not supported.'.format(numpy_array_type.dtype)) # MITK <-> Numpy Array conversion support. #http://www.nickdarnell.com/swig-casting-revisited/ def GetArrayViewFromImage(image): """Get a NumPy ndarray view of a MITK Image. Returns a Numpy ndarray object as a "view" of the MITK's Image buffer. This reduces pixel buffer copies, but requires that the MITK image object is kept around while the buffer is being used. """ if not HAVE_NUMPY: raise ImportError('NumPy not available.') dtype = _get_numpy_dtype( image ) shape = GetImageSize(image); if image.GetPixelType().GetNumberOfComponents() > 1: shape = ( image.GetPixelType().GetNumberOfComponents(), ) + shape imageMemoryView = _pyMITK._GetMemoryViewFromImage(image) arrayView = numpy.asarray(imageMemoryView).view(dtype = dtype) arrayView.shape = shape[::-1] return arrayView def GetArrayFromImage(image): """Get a NumPy ndarray from a MITK Image. This is a deep copy of the image buffer and is completely safe and without potential side effects. """ # TODO: If the image is already not unique then a second copy may be made before the numpy copy is done. arrayView = GetArrayViewFromImage(image) # perform deep copy of the image buffer return numpy.array(arrayView, copy=True) def GetImageFromArray( arr, isVector=False): """Get a MITK Image from a numpy array. If isVector is True, then a 3D array will be treated as a 2D vector image, otherwise it will be treated as a 3D image""" if not HAVE_NUMPY: raise ImportError('Numpy not available.') z = numpy.asarray( arr ) assert z.ndim in ( 2, 3, 4 ), \ "Only arrays of 2, 3 or 4 dimensions are supported." id = _get_mitk_pixelid( z ) #img = Image_New() if ( z.ndim == 3 and isVector ) or (z.ndim == 4): pixelType=MakePixelTypeFromTypeID(id, z.shape[-1]) newShape=VectorUInt32(z.shape[-2::-1]) img = MakeImage(pixelType, newShape) #img.Initialize(pixelType, z.ndim - 1, z.shape[-2::-1]) elif z.ndim in ( 2, 3 ): pixelType=MakePixelTypeFromTypeID(id, 1) newShape=VectorUInt32(z.shape[::-1]) img = MakeImage(pixelType, newShape) #img.Initialize(pixelType, z.ndim, z.shape[::-1]) _pyMITK._SetImageFromArray( z.tostring(), img ) return img %}