diff --git a/CMakeLists.txt b/CMakeLists.txt index 68a70d4b05..6cddf18f71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1424 +1,1430 @@ set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.13) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) #----------------------------------------------------------------------------- # See https://cmake.org/cmake/help/v3.10/manual/cmake-policies.7.html for details #----------------------------------------------------------------------------- set(project_policies ) foreach(policy ${project_policies}) if(POLICY ${policy}) cmake_policy(SET ${policy} NEW) endif() endforeach() #----------------------------------------------------------------------------- # 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 "") set(MITK_DIR_PLUS_EXTENSION_DIRS ${MITK_SOURCE_DIR} ${MITK_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_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMake) get_filename_component(MITK_CMAKE_EXTENSION_DIR ${MITK_CMAKE_EXTENSION_DIR} ABSOLUTE) 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(CTestUseLaunchers) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionGenerateProjectXml) 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 2015 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) #----------------------------------------------------------------------------- macro(env_option name doc value) set(_value $ENV{${name}}) if("${_value}" STREQUAL "") set(_value ${value}) endif() option(${name} "${doc}" ${_value}) endmacro() # ----------------------------------------- # 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) env_option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) env_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_BUILD_ALL_APPS MITK_ENABLE_PIC_READER ) # ----------------------------------------- # Qt version related variables env_option(MITK_USE_Qt5 "Use Qt 5 library" ON) if(MITK_USE_Qt5) set(MITK_QT5_MINIMUM_VERSION 5.11.1) 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) 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(_compiler "msvc${CMAKE_MATCH_1}") endif() else() set(_dir_candidates ~/Qt) if(APPLE) set(_compiler clang) else() list(APPEND _dir_candidates /opt/Qt) set(_compiler gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_compiler "${_compiler}_64") endif() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) set(_glob_expression "${_dir_candidate}/5.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT5_HINTS ${_hints}) endforeach() endif() find_package(Qt5 ${MITK_QT5_MINIMUM_VERSION} COMPONENTS ${MITK_QT5_COMPONENTS} REQUIRED HINTS ${MITK_QT5_HINTS}) endif() set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "") include(CMakeExternals/ExternalProjectList.cmake) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMakeExternals) get_filename_component(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_CMAKE_EXTERNALS_EXTENSION_DIR} ABSOLUTE) 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 env_option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) env_option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) +env_option(MITK_USE_CppRestSdk "Use cpp rest sdk library" 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_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) # ----------------------------------------- # 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) 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 install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\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_Python) set(MITK_USE_ZLIB ON) find_package(PythonLibs 3 REQUIRED) find_package(PythonInterp 3 REQUIRED) if ( NOT PYTHON_VERSION_STRING VERSION_EQUAL PYTHONLIBS_VERSION_STRING) message(SEND_ERROR "Inconsistent python versions found: interpreter ${PYTHON_VERSION_STRING} vs. libs ${PYTHONLIBS_VERSION_STRING}" ) endif() find_package(Numpy REQUIRED) 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() #----------------------------------------------------------------------------- # Project.xml #----------------------------------------------------------------------------- # A list of topologically ordered targets set(CTEST_PROJECT_SUBPROJECTS) list(APPEND CTEST_PROJECT_SUBPROJECTS MITK-Core MITK-CoreUI MITK-IGT MITK-ToF MITK-DTI MITK-Modules # all modules not contained in a specific subproject MITK-Plugins # all plugins not contained in a specific subproject MITK-Examples Unlabeled # special "subproject" catching all unlabeled targets and tests ) # Configure CTestConfigSubProject.cmake that could be used by CTest scripts configure_file(${MITK_SOURCE_DIR}/CTestConfigSubProject.cmake.in ${MITK_BINARY_DIR}/CTestConfigSubProject.cmake) if(CTEST_PROJECT_ADDITIONAL_TARGETS) # those targets will be executed at the end of the ctest driver script # and they also get their own subproject label set(subproject_list "${CTEST_PROJECT_SUBPROJECTS};${CTEST_PROJECT_ADDITIONAL_TARGETS}") else() set(subproject_list "${CTEST_PROJECT_SUBPROJECTS}") endif() # Generate Project.xml file expected by the CTest driver script mitkFunctionGenerateProjectXml(${MITK_BINARY_DIR} MITK "${subproject_list}" ${MITK_USE_SUPERBUILD}) #----------------------------------------------------------------------------- # 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 **************************** #***************************************************************************** #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(GenerateExportHeader) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionConvertXPSchema) 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(mitkFunctionTestPlugin) 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 CoreApp and mitkWorkbench 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) # TODO: check if necessary option(USE_ITKZLIB "Use the ITK zlib for pic compression." ON) mark_as_advanced(USE_ITKZLIB) if(NOT MITK_FAST_TESTING) if(DEFINED MITK_CTEST_SCRIPT_MODE AND (MITK_CTEST_SCRIPT_MODE STREQUAL "continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "experimental") ) set(MITK_FAST_TESTING 1) 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) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) 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 ) find_package(OpenMP) if (OPENMP_FOUND) set(MITK_C_FLAGS "${MITK_C_FLAGS} ${OpenMP_C_FLAGS}") set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") endif() if(WIN32) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -D_WIN32_WINNT=0x0501 -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 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-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_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMake/PackageDepends) get_filename_component(MITK_PACKAGE_DEPENDS_EXTENSION_DIR ${MITK_PACKAGE_DEPENDS_EXTENSION_DIR} ABSOLUTE) 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.68 1.68.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() link_directories(${Boost_LIBRARY_DIRS}) if(MITK_USE_OpenIGTLink) link_directories(${OpenIGTLink_LIBRARY_DIRS}) endif() if(MITK_USE_OpenCL) find_package(OpenCL REQUIRED) endif() +if(MITK_USE_CppRestSdk) + message(WARNING "Be sure to install cpprestsdk via vcpkg and set cpprestsdk_DIR to /vcpkg/installed/[OS_VERSION]/share/cpprestsdk") + find_package(cpprestsdk REQUIRED) +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) enable_testing() include(CTest) mark_as_advanced(TCL_TCLSH DART_ROOT) option(MITK_ENABLE_RENDERING_TESTING OFF "Enable the MITK rendering tests. Requires x-server in Linux.") #Rendering testing does not work for Linux nightlies, thus it is disabled per default #and activated for Mac and Windows. if(WIN32 OR APPLE) set(MITK_ENABLE_RENDERING_TESTING ON) endif() mark_as_advanced( MITK_ENABLE_RENDERING_TESTING ) # Setup file for setting custom ctest vars configure_file( CMake/CTestCustom.cmake.in ${MITK_BINARY_DIR}/CTestCustom.cmake @ONLY ) # 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( std::exception & excp ) { fprintf(stderr,\"%s\\n\",excp.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 external project template if(MITK_USE_BLUEBERRY) include(mitkTestProjectTemplate) 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 custom targets representing CDash subprojects #----------------------------------------------------------------------------- foreach(subproject ${CTEST_PROJECT_SUBPROJECTS}) if(NOT TARGET ${subproject} AND NOT subproject MATCHES "Unlabeled") add_custom_target(${subproject}) endif() endforeach() #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Utilities) add_subdirectory(Modules) include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake") mitkFunctionWhitelistModules(MITK MITK_MODULES) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_MODULES_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Modules) get_filename_component(MITK_MODULES_EXTENSION_DIR ${MITK_MODULES_EXTENSION_DIR} ABSOLUTE) 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() add_subdirectory(Wrapping) if(MITK_USE_BLUEBERRY) set(BLUEBERRY_XPDOC_OUTPUT_DIR ${MITK_DOXYGEN_OUTPUT_DIR}/html/extension-points/html/) # Plug-in testing (needs some work to be enabled again) if(BUILD_TESTING) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp") if(TARGET CoreApp) get_target_property(_is_macosx_bundle CoreApp MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp.app/Contents/MacOS/CoreApp") endif() endif() set(BLUEBERRY_TEST_APP_ID "org.mitk.qt.coreapplication") endif() 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_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Plugins) get_filename_component(MITK_PLUGINS_EXTENSION_DIR ${MITK_PLUGINS_EXTENSION_DIR} ABSOLUTE) 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) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) 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) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) 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_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) 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/Chart/CMakeLists.txt b/Modules/Chart/CMakeLists.txt index c233ff4980..102806e36d 100644 --- a/Modules/Chart/CMakeLists.txt +++ b/Modules/Chart/CMakeLists.txt @@ -1,4 +1,4 @@ MITK_CREATE_MODULE( DEPENDS MitkCore PACKAGE_DEPENDS PRIVATE Qt5|WebEngineWidgets -) \ No newline at end of file +) diff --git a/Modules/Chart/resource/Chart.js b/Modules/Chart/resource/Chart.js index 1d2b7502f7..85d9ba60f9 100644 --- a/Modules/Chart/resource/Chart.js +++ b/Modules/Chart/resource/Chart.js @@ -1,405 +1,405 @@ document.body.style.backgroundColor = 'rgb(240, 240, 240)'; const minHeight = 255; var chart; var chartData; var xErrorValuesPlus=[]; var xErrorValuesMinus=[]; var yErrorValuesPlus=[]; var yErrorValuesMinus=[]; var xValues=[]; var yValues=[]; var dataLabels=[]; var xs = {}; var dataColors = {}; var chartTypes = {}; var lineStyle = {}; var backgroundColor = '#f0f0f0'; var foregroundColor = 'black'; var dataProperties = {}; // Important loading function. This will be executed at first in this whole script. // Fetching data from QWebChannel and storing them for display purposes. window.onload = function() { initHeight(); new QWebChannel(qt.webChannelTransport, function(channel) { chartData = channel.objects.chartData; let count = 0; for(let propertyName in channel.objects) { if (propertyName != 'chartData') { let xDataTemp = channel.objects[propertyName].m_XData; let yDataTemp = channel.objects[propertyName].m_YData; let xErrorsTempPlus = channel.objects[propertyName].m_XErrorDataPlus; let xErrorsTempMinus = channel.objects[propertyName].m_XErrorDataMinus; let yErrorsTempPlus = channel.objects[propertyName].m_YErrorDataPlus; let yErrorsTempMinus = channel.objects[propertyName].m_YErrorDataMinus; let dataLabel = channel.objects[propertyName].m_Label; dataLabels.push(dataLabel); console.log("loading datalabel: "+dataLabel); //add label to x array xDataTemp.unshift('x'+count.toString()) xs[dataLabel] = 'x' + count.toString() xDataTemp.push(null); //append null value, to make sure the last tick on x-axis is displayed correctly yDataTemp.unshift(dataLabel) yDataTemp.push(null); //append null value, to make sure the last tick on y-axis is displayed correctly xValues[count] = xDataTemp yValues[count] = yDataTemp xErrorValuesPlus[count] = xErrorsTempPlus; xErrorValuesMinus[count] = xErrorsTempMinus; yErrorValuesPlus[count] = yErrorsTempPlus; yErrorValuesMinus[count] = yErrorsTempMinus; var tempLineStyle = ''; if (channel.objects[propertyName].m_LineStyleName == "solid") { tempLineStyle = '' } else { tempLineStyle = "dashed" } dataProperties[dataLabel] = { "color" : channel.objects[propertyName].m_Color, "chartType": channel.objects[propertyName].m_ChartType, "style": tempLineStyle } count++; } } var theme = chartData.m_themeName; setThemeColors(theme); generateChart(chartData); }); } /** * Inits the height of the chart element to 90% of the full window height. */ function initHeight() { - var size = window.innerHeight-(window.innerHeight/100*10); //subtract 10% of height to hide vertical scrool bar + var size = window.innerHeight-(window.innerHeight/100*5); //subtract 10% of height to hide vertical scrool bar let chart = document.getElementById("chart"); chart.style.height = `${size}px`; } function getPlotlyChartType(inputType){ let plotlyType = inputType; if (inputType == "line"){ plotlyType = "scatter"; } else if (inputType == "scatter"){ plotlyType = "scatterOnly" } return plotlyType; } /** * Generate error bars object * * @param {array} errors - contains error bar values * @return error bar object */ function generateErrorBars(errors, visible){ let errorObject = { type: 'data', array: errors, visible: visible } return errorObject; } function generateErrorBarsAsymmetric(errorsPlus, errorsMinus, visible){ let errorObject = generateErrorBars(errorsPlus, visible); errorObject["arrayminus"] = errorsMinus; errorObject["symmetric"] = false; return errorObject; } function generateStackPlotData(){ let data = []; for (let index = 0; index < dataLabels.length; index++){ let inputType = dataProperties[dataLabels[index]]["chartType"]; let chartType = getPlotlyChartType(inputType); let trace = { x: xValues[index].slice(1), y: yValues[index].slice(1), stackgroup: 'one', name: dataLabels[index], type: chartType, marker:{ color: dataProperties[dataLabels[index]]["color"] } }; data.push(trace); } return data; } function generatePlotData(){ let data = []; for (let index = 0; index < dataLabels.length; index++){ let inputType = dataProperties[dataLabels[index]]["chartType"]; let chartType = getPlotlyChartType(inputType); let trace = { x: xValues[index].slice(1), y: yValues[index].slice(1), type: chartType, name: dataLabels[index], }; if(typeof xErrorValuesPlus[index] !== 'undefined'){ if(typeof xErrorValuesMinus[index] !== 'undefined' && xErrorValuesMinus[index].length > 0) { trace["error_x"] = generateErrorBarsAsymmetric(xErrorValuesPlus[index], xErrorValuesMinus[index], chartData.m_ShowErrorBars); }else{ trace["error_x"] = generateErrorBars(xErrorValuesPlus[index], chartData.m_ShowErrorBars); } } if(typeof yErrorValuesPlus[index] !== 'undefined'){ if(typeof yErrorValuesMinus[index] !== 'undefined' && yErrorValuesMinus[index].length > 0) { trace["error_y"] = generateErrorBarsAsymmetric(yErrorValuesPlus[index], yErrorValuesMinus[index], chartData.m_ShowErrorBars); }else{ trace["error_y"] = generateErrorBars(yErrorValuesPlus[index], chartData.m_ShowErrorBars); } } // ===================== CHART TYPE OPTIONS HANDLING =========== // initialize line object trace["line"] = {} trace["line"]["color"] = dataProperties[dataLabels[index]]["color"] if (chartType == "scatter"){ } else if (chartType == "area"){ trace["fill"] = 'tozeroy' } else if (chartType == "spline"){ trace["line"]["shape"] = 'spline' } else if (chartType == "scatterOnly"){ trace["mode"] = 'markers'; } else if (chartType == "area-spline"){ trace["fill"] = 'tozeroy' trace["line"]["shape"] = 'spline' } // handle marker visibility/size/color trace["marker"] = {size: chartData.m_DataPointSize, color: dataProperties[dataLabels[index]]["color"]} if (chartData.m_DataPointSize == 0){ trace["mode"] = "lines"; } if (dataProperties[dataLabels[index]]["style"] == "dashed"){ trace["line"]["dash"] = "dot" } data.push(trace) } return data; } /** * Here, the chart magic takes place. Plot.ly is called. * * @param {object} chartData - containing the options for plotting, not the actual values */ function generateChart(chartData) { console.log("generate chart"); if (chartData == undefined) { chartData = {} } if (dataLabels == undefined) { dataLabels = [] } //=============================== DATA ======================== var data = []; if (chartData.m_StackedData){ data = generateStackPlotData(); } else { data = generatePlotData(); } //=============================== STYLE ======================== let marginTop = chartData.m_chartTitle == undefined ? 10 : 50; if (chartData.m_LegendPosition == "bottomMiddle"){ var legendX = 0.5; var legendY = -0.75; } else if (chartData.m_LegendPosition == "bottomRight"){ var legendX = 1; var legendY = 0; } else if (chartData.m_LegendPosition == "topRight"){ var legendX = 1; var legendY = 1; } else if (chartData.m_LegendPosition == "topLeft"){ var legendX = 0; var legendY = 1; } else if (chartData.m_LegendPosition == "middleRight"){ var legendX = 1; var legendY = 0.5; } var layout = { paper_bgcolor : backgroundColor, plot_bgcolor : backgroundColor, title: { text:chartData.m_chartTitle, font: { color: foregroundColor } }, xaxis: { title: { text: chartData.m_xAxisLabel }, color: foregroundColor }, yaxis: { title: { text:chartData.m_yAxisLabel }, color: foregroundColor }, margin: { l: 50, r: 10, - b: 50, + b: 20, t: marginTop, pad: 4 }, showlegend: chartData.m_ShowLegend, legend: { x: legendX, y: legendY, font : { color: foregroundColor } } }; if (chartData.m_StackedData){ layout["barmode"] = 'stack'; } if (chartData.m_YAxisScale){ layout.yaxis["type"] = "log" } if (chartData.m_ShowSubchart){ layout.xaxis.rangeslider = {}; // adds range slider below x axis } Plotly.newPlot('chart', data, layout, {displayModeBar: false, responsive: true}); } /** * Change theme of chart. * * @param {string} color - dark or not dark */ function changeTheme(color) { setThemeColors(color); link = document.getElementsByTagName("link")[0]; if (color == 'dark') { link.href = "Chart_dark.css"; } else { link.href = "Chart.css"; } }; /** * Reload the chart with the given arguments. * * This method is called by C++. Changes on signature with caution. */ function Reload(){ console.log("Reload chart"); generateChart(chartData); } function SetShowSubchart(showSubchart) { chartData.m_ShowSubchart = showSubchart; } function setThemeColors(theme){ if (theme == 'dark'){ backgroundColor = '#2d2d30'; foregroundColor = 'white'; } else { backgroundColor = '#f0f0f0'; foregroundColor = 'black'; } } function SetStackDataString(stackDataString) { chartData.m_StackedData = stackDataString; } function SetShowErrorBars(showErrorBars) { chartData.m_ShowErrorBars = showErrorBars; } /** * Zooms to the given x-axis min and max values. */ function UpdateMinMaxValueXView(minValueX, maxValueX) { //y-Axis can't be adapted for now. See https://github.com/plotly/plotly.js/issues/1876 let chart = document.getElementById("chart"); let update = { xaxis:{ range:[minValueX, maxValueX] } }; Plotly.relayout(chart, update); } /** * Transforms the view to another chart type. * * This method is called by C++. Changes on signature with caution. * @param {string} transformTo - 'line' or 'bar' */ function transformView(transformTo) { console.log("transform view"); console.log(transformTo); dataProperties[dataLabels[0]]["chartType"] = transformTo; // preserve chartType for later updates let plotlyType = getPlotlyChartType(transformTo); let chart = document.getElementById("chart"); let update = {type : plotlyType}; Plotly.restyle(chart, update, 0); // updates the given plotly trace at index 0 with an update object built of a standard trace object }; diff --git a/Modules/CppRestSdk/CMakeLists.txt b/Modules/CppRestSdk/CMakeLists.txt new file mode 100644 index 0000000000..de6047745c --- /dev/null +++ b/Modules/CppRestSdk/CMakeLists.txt @@ -0,0 +1,18 @@ +if(MITK_USE_CppRestSdk) + + MITK_CREATE_MODULE( + DEPENDS MitkCore + #WARNINGS_NO_ERRORS + AUTOLOAD_WITH MitkCore + ) + + if(TARGET ${MODULE_TARGET}) + find_package(OpenSSL REQUIRED) + + target_link_libraries(${MODULE_TARGET} PRIVATE cpprestsdk::cpprest) + target_link_libraries(${MODULE_TARGET} PUBLIC OpenSSL::SSL) + endif() + + add_subdirectory(test) + +endif(MITK_USE_CppRestSdk) diff --git a/Modules/CppRestSdk/files.cmake b/Modules/CppRestSdk/files.cmake new file mode 100644 index 0000000000..aa8840e89e --- /dev/null +++ b/Modules/CppRestSdk/files.cmake @@ -0,0 +1,10 @@ +file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") + +set(CPP_FILES + mitkRESTClient.cpp + mitkRESTServer.cpp + mitkCppRestSdkActivator.cpp + mitkIRESTManager.cpp + mitkRESTManager.cpp + mitkIRESTObserver.cpp +) \ No newline at end of file diff --git a/Modules/CppRestSdk/include/mitkIRESTManager.h b/Modules/CppRestSdk/include/mitkIRESTManager.h new file mode 100644 index 0000000000..3e67c71f6b --- /dev/null +++ b/Modules/CppRestSdk/include/mitkIRESTManager.h @@ -0,0 +1,105 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkIRESTManager_h +#define mitkIRESTManager_h + +#include "cpprest/json.h" +#include "cpprest/uri.h" + +#include +#include + +#include +#include +#include + + +namespace mitk +{ + /** + * @class IRESTManager + * @brief this is a microservice interface for managing REST-requests. + * + * There are two microservices implementing this interface. + * 1. The RESTManager in the CppRestSdk Module is the service used for non-Qt applications + * 2. The RESTManagerQt in the CppRestSdkQt Module which is used for Qt-applications. + * If a Qt application is running, the RESTManagerQt is the default service which is automatically selected. + */ + + class RESTServer; + class MITKCPPRESTSDK_EXPORT IRESTManager + { + public: + virtual ~IRESTManager(); + + /** + * @brief request type for client requests by calling SendRequest + */ + enum class RequestType + { + Get, + Post, + Put + }; + + /** + * @brief Executes a HTTP request in the mitkRESTClient class + * + * @param uri defines the URI the request is send to + * @param type the RequestType of the HTTP request (optional) + * @param body the body for the request (optional) + * @return task to wait for + */ + virtual pplx::task SendRequest(const web::uri &uri, + const RequestType &type = RequestType::Get, + const web::json::value *body = nullptr, + const utility::string_t &filePath = L"") = 0; + + /** + * @brief starts listening for requests if there isn't another observer listening and the port is free + * + * @param uri defines the URI for which incoming requests should be send to the observer + * @param observer the observer which handles the incoming requests + */ + virtual void ReceiveRequest(const web::uri &uri, IRESTObserver *observer) = 0; + + /** + * @brief Handles incoming requests by notifying the observer which should receive it + * + * @param uri defines the URI of the request + * @param body the body of the request + * @return the data which is modified by the notified observer + */ + virtual web::json::value Handle(const web::uri &uri, const web::json::value &body) = 0; + + /** + * @brief Handles the deletion of an observer for all or a specific uri + * + * @param observer the observer which shouldn't receive requests anymore + * @param uri the uri for which the observer doesn't handle requests anymore (optional) + */ + virtual void HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri = L"") = 0; + + virtual const std::map& GetServerMap() = 0; + virtual const std::map, IRESTObserver *>& GetObservers() = 0; + + }; +} // namespace mitk + +MITK_DECLARE_SERVICE_INTERFACE(mitk::IRESTManager, "org.mitk.IRESTManager") + +#endif diff --git a/Modules/CppRestSdk/include/mitkIRESTObserver.h b/Modules/CppRestSdk/include/mitkIRESTObserver.h new file mode 100644 index 0000000000..52e9398ca4 --- /dev/null +++ b/Modules/CppRestSdk/include/mitkIRESTObserver.h @@ -0,0 +1,49 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkIRESTObserver_h +#define mitkIRESTObserver_h + +#include "cpprest/json.h" +#include "cpprest/uri.h" +#include + +namespace mitk +{ + class MITKCPPRESTSDK_EXPORT IRESTObserver + { + public: + /** + * @brief Deletes an observer and calls HandleDeleteObserver() in RESTManager class + * + * @see HandleDeleteObserver() + */ + virtual ~IRESTObserver(); + + /** + * @brief Called if there's an incoming request for the observer, observer implements how to handle request + * + * @param data the data of the incoming request + * @return the modified data + */ + virtual web::json::value Notify(const web::uri &uri, const web::json::value &data) = 0; + + + private: + }; +} + +#endif // !mitkIRESTObserver diff --git a/Modules/CppRestSdk/include/mitkRESTClient.h b/Modules/CppRestSdk/include/mitkRESTClient.h new file mode 100644 index 0000000000..416f431f01 --- /dev/null +++ b/Modules/CppRestSdk/include/mitkRESTClient.h @@ -0,0 +1,89 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkRESTClient_h +#define mitkRESTClient_h + +#include "cpprest/asyncrt_utils.h" +#include "cpprest/containerstream.h" +#include "cpprest/filestream.h" +#include "cpprest/http_client.h" +#include "cpprest/producerconsumerstream.h" + +#include +#include +#include +#include + +typedef web::http::client::http_client MitkClient; +typedef web::http::http_request MitkRequest; +typedef web::http::http_response MitkResponse; +typedef web::http::methods MitkRESTMethods; +typedef web::http::uri_builder MitkUriBuilder; +typedef web::http::status_codes MitkRestStatusCodes; +typedef web::json::json_exception MitkJsonException; + +namespace mitk +{ + class MITKCPPRESTSDK_EXPORT RESTClient + { + public: + RESTClient(); + ~RESTClient(); + + /** + * @brief Executes a HTTP GET request with the given uri and returns a task waiting for a json object + * + * @throw mitk::Exception if request went wrong + * @param uri the URI resulting the target of the HTTP request + * @return task to wait for with resulting json object + */ + pplx::task Get(const web::uri &uri); + + /** + * @brief Executes a HTTP GET request with the given uri and and stores the byte stream in a file given by the + * filePath + * + * @throw mitk::Exception if request went wrong + * @param uri the URI resulting the target of the HTTP request + * @return task to wait for returning an empty json object + */ + pplx::task Get(const web::uri &uri, const utility::string_t &filePath); + + /** + * @brief Executes a HTTP PUT request with given uri and the content given as json + * + * @throw mitk::Exception if request went wrong + * @param uri defines the URI resulting the target of the HTTP request + * @param content the content as json value which should be the body of the request and thus the content of the + * created resources + * @return task to wait for with resulting json object + */ + pplx::task Put(const web::uri &uri, const web::json::value *content); + + /** + * @brief Executes a HTTP POST request with given uri and the content given as json + * + * @throw mitk::Exception if request went wrong + * @param uri defines the URI resulting the target of the HTTP request + * @param content the content as json value which should be the body of the request and thus the content of the + * created resource + * @return task to wait for with resulting json object + */ + pplx::task Post(const web::uri &uri, const web::json::value *content); + }; +} // namespace mitk +#endif // !mitkRESTClient_h diff --git a/Modules/CppRestSdk/include/mitkRESTManager.h b/Modules/CppRestSdk/include/mitkRESTManager.h new file mode 100644 index 0000000000..a55987bd31 --- /dev/null +++ b/Modules/CppRestSdk/include/mitkRESTManager.h @@ -0,0 +1,132 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkRESTManager_h +#define mitkRESTManager_h + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4251) +#endif + +#include +#include + + + +namespace mitk +{ + /** + * @class RESTManager + * @brief this is a microservice for managing REST-requests, used for non-qt applications. + * + * RESTManagerQt in the CppRestSdkQt module inherits from this class and is the equivalent microservice + * used for Qt applications. + */ + class MITKCPPRESTSDK_EXPORT RESTManager : public IRESTManager + { + + public: + RESTManager(); + ~RESTManager() override; + + /** + * @brief Executes a HTTP request in the mitkRESTClient class + * + * @throw mitk::Exception if RequestType is not suported + * @param uri defines the URI the request is send to + * @param type the RequestType of the HTTP request (optional) + * @param body the body for the request (optional) + * @param filePath the file path to store the request to + * @return task to wait for + */ + pplx::task SendRequest(const web::uri &uri, + const RequestType &type = RequestType::Get, + const web::json::value *body= nullptr, + const utility::string_t &filePath = L"") override; + + /** + * @brief starts listening for requests if there isn't another observer listening and the port is free + * + * @param uri defines the URI for which incoming requests should be send to the observer + * @param observer the observer which handles the incoming requests + */ + void ReceiveRequest(const web::uri &uri, IRESTObserver *observer) override; + + /** + * @brief Handles incoming requests by notifying the observer which should receive it + * + * @param uri defines the URI of the request + * @param body the body of the request + * @return the data which is modified by the notified observer + */ + web::json::value Handle(const web::uri &uri, const web::json::value &body) override; + + /** + * @brief Handles the deletion of an observer for all or a specific uri + * + * @param observer the observer which shouldn't receive requests anymore + * @param uri the uri for which the observer doesn't handle requests anymore (optional) + */ + virtual void HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri) override; + + /** + * @brief internal use only + */ + virtual const std::map& GetServerMap() override; + virtual std::map, IRESTObserver *>& GetObservers() override; + + protected: + /** + * @brief adds an observer if a port is free, called by ReceiveRequest method + * + * @param uri the uri which builds the key for the observer map + * @param observer the observer which is added + */ + void AddObserver(const web::uri &uri, IRESTObserver *observer); + + /** + * @brief handles server management if there is already a server under a port, called by ReceiveRequest method + * + * @param uri the uri which which is requested to be added + * @param observer the observer which proceeds the request + */ + void RequestForATakenPort(const web::uri &uri, IRESTObserver *observer); + + /** + * @brief deletes an observer, called by HandleDeleteObserver method + * + * @param it the iterator comparing the observers in HandleDeleteObserver method + * @param the uri for which the observer doesn't want to receive requests anymore + * @return bool if there is another observer under the port + */ + bool DeleteObserver(std::map < std::pair, IRESTObserver *>::iterator &it, const web::uri &uri); + + void SetServerMap(const int port, RESTServer *server); + void DeleteFromServerMap(const int port); + void SetObservers(const std::pair key, IRESTObserver *observer); + + private: + std::map m_ServerMap; // Map with port server pairs + std::map, IRESTObserver *> m_Observers; // Map with all observers + }; +} // namespace mitk + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // !mitkRESTManager_h diff --git a/Modules/CppRestSdk/include/mitkRESTServer.h b/Modules/CppRestSdk/include/mitkRESTServer.h new file mode 100644 index 0000000000..4fddc3be8d --- /dev/null +++ b/Modules/CppRestSdk/include/mitkRESTServer.h @@ -0,0 +1,79 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkRESTServer_h +#define mitkRESTServer_h + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4251) +#endif + +#include "cpprest/http_listener.h" + +#include +#include +#include +#include + +typedef web::http::experimental::listener::http_listener MitkListener; +typedef web::http::http_request MitkRequest; +typedef web::http::http_response MitkResponse; +typedef web::http::methods MitkRESTMethods; +typedef web::http::status_codes MitkRestStatusCodes; +typedef web::json::json_exception MitkJsonException; + +namespace mitk +{ + class MITKCPPRESTSDK_EXPORT RESTServer + { + + public: + /** + * @brief Creates an server listening to the given URI + * + * @param uri the URI at which the server is listening for requests + */ + RESTServer(const web::uri &uri); + ~RESTServer(); + + web::uri GetUri(); + + /** + * @brief Opens the listener and starts the listening process + */ + void OpenListener(); + + /** + * @brief Closes the listener and stops the listening process + */ + void CloseListener(); + private: + /** + * @brief Handle for incoming GET requests + * + * @param MitkRequest incoming request object + */ + void HandleGet(const MitkRequest &request); + + MitkListener m_Listener; + web::uri m_Uri; + }; +} // namespace mitk +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif \ No newline at end of file diff --git a/Modules/CppRestSdk/include/mitkRESTUtil.h b/Modules/CppRestSdk/include/mitkRESTUtil.h new file mode 100644 index 0000000000..e9d8e596a0 --- /dev/null +++ b/Modules/CppRestSdk/include/mitkRESTUtil.h @@ -0,0 +1,43 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKRESTUTIL_H +#define MITKRESTUTIL_H + +#include "MitkCppRestSdkExports.h" + +#include "cpprest/asyncrt_utils.h" +#include + +namespace mitk +{ + class MITKCPPRESTSDK_EXPORT RESTUtil + { + public: + + /** + * @brief Converts the given std::wstring into a std::string representation + */ + static std::string convertToUtf8(utility::string_t stringT) { return utility::conversions::to_utf8string(stringT); } + + /** + * @brief Converts the given std::string into a std::wstring representation + */ + static utility::string_t convertToTString(std::string string) { return utility::conversions::to_string_t(string); } + }; +}; + +#endif // MITKRESTUTIL_H diff --git a/Modules/CppRestSdk/src/mitkCppRestSdkActivator.cpp b/Modules/CppRestSdk/src/mitkCppRestSdkActivator.cpp new file mode 100644 index 0000000000..d80d886885 --- /dev/null +++ b/Modules/CppRestSdk/src/mitkCppRestSdkActivator.cpp @@ -0,0 +1,25 @@ +#include "mitkCppRestSdkActivator.h" +#include +#include +#include +#include +#include +#include +#include +#include + +void MitkCppRestSdkActivator::Load(us::ModuleContext *context) +{ + //Registration of the RESTManagerMicroservice + m_RESTManager.reset(new mitk::RESTManager); + us::ServiceProperties props; + props[us::ServiceConstants::SERVICE_RANKING()] = 5; + context->RegisterService(m_RESTManager.get(),props); +} + +void MitkCppRestSdkActivator::Unload(us::ModuleContext *) +{ + +} + +US_EXPORT_MODULE_ACTIVATOR(MitkCppRestSdkActivator) diff --git a/Modules/CppRestSdk/src/mitkCppRestSdkActivator.h b/Modules/CppRestSdk/src/mitkCppRestSdkActivator.h new file mode 100644 index 0000000000..6e368eb4d3 --- /dev/null +++ b/Modules/CppRestSdk/src/mitkCppRestSdkActivator.h @@ -0,0 +1,37 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKCPPRESTSDKACTIVATOR_H_ +#define MITKCPPRESTSDKACTIVATOR_H_ + +#include + +#include +#include +#include +#include + +class MitkCppRestSdkActivator : public us::ModuleActivator +{ + +public: + void Load(us::ModuleContext *context) override; + void Unload(us::ModuleContext *) override; + +private: + std::unique_ptr m_RESTManager; +}; +#endif diff --git a/Modules/CppRestSdk/src/mitkIRESTManager.cpp b/Modules/CppRestSdk/src/mitkIRESTManager.cpp new file mode 100644 index 0000000000..b54c19f6d9 --- /dev/null +++ b/Modules/CppRestSdk/src/mitkIRESTManager.cpp @@ -0,0 +1,3 @@ +#include "mitkIRESTManager.h" + +mitk::IRESTManager::~IRESTManager() {} diff --git a/Modules/CppRestSdk/src/mitkIRESTObserver.cpp b/Modules/CppRestSdk/src/mitkIRESTObserver.cpp new file mode 100644 index 0000000000..ccbd3f099c --- /dev/null +++ b/Modules/CppRestSdk/src/mitkIRESTObserver.cpp @@ -0,0 +1,19 @@ +#include "mitkIRESTObserver.h" +#include +#include +#include +#include +#include +mitk::IRESTObserver::~IRESTObserver() +{ + us::ModuleContext *context = us::GetModuleContext(); + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + managerService->HandleDeleteObserver(this); + } + } +} diff --git a/Modules/CppRestSdk/src/mitkRESTClient.cpp b/Modules/CppRestSdk/src/mitkRESTClient.cpp new file mode 100644 index 0000000000..003d66fff3 --- /dev/null +++ b/Modules/CppRestSdk/src/mitkRESTClient.cpp @@ -0,0 +1,210 @@ +#include "mitkRESTClient.h" +#include "mitkRESTUtil.h" +#include + +mitk::RESTClient::RESTClient() {} + +mitk::RESTClient::~RESTClient() {} + +pplx::task mitk::RESTClient::Get(const web::uri &uri) +{ + //Create new HTTP client + MitkClient *client = new MitkClient(uri); + + MITK_INFO << "Calling GET with " << mitk::RESTUtil::convertToUtf8(uri.path()) << " on client " + << mitk::RESTUtil::convertToUtf8(uri.authority().to_string()); + + //create get request + MitkRequest getRequest(MitkRESTMethods::GET); + + //make request + return client->request(getRequest).then([=](pplx::task responseTask) { + try + { + //get response of the request + MitkResponse response = responseTask.get(); + auto status = response.status_code(); + MITK_INFO << " status: " << status; + + if (MitkRestStatusCodes::OK != status) + { + //throw if something went wrong (e.g. invalid uri) + //this exception can be handled by client + mitkThrow() << "response was not OK"; + } + try + { + //parse content type to application/json if it isn't already + //this is important if the content type is e.g. application/dicom+json + utility::string_t requestContentType = response.headers().content_type(); + if (L"application/json" != requestContentType) + { + response.headers().set_content_type(L"application/json"); + } + //return json answer + return response.extract_json().get(); + } + catch (...) + { + mitkThrow() << "extracting json went wrong"; + } + } + catch (...) + { + mitkThrow() << "getting response went wrong"; + } + }); +} + +pplx::task mitk::RESTClient::Get(const web::uri &uri, const utility::string_t &filePath) +{ + // Create new HTTP client + MitkClient *client = new MitkClient(uri); + + MITK_INFO << "Calling GET with " << mitk::RESTUtil::convertToUtf8(uri.path()) << " on client " + << mitk::RESTUtil::convertToUtf8(uri.authority().to_string()) << " save into " + << mitk::RESTUtil::convertToUtf8(filePath); + + //create new file buffer + auto fileBuffer = std::make_shared>(); + // create get request + MitkRequest getRequest(MitkRESTMethods::GET); + + //open file stream for the specified file path + return concurrency::streams::file_buffer::open(filePath, std::ios::out) + .then([=](concurrency::streams::streambuf outFile) -> pplx::task { + *fileBuffer = outFile; + //make the get request + return client->request(MitkRESTMethods::GET); + }) + // Write the response body into the file buffer. + .then([=](MitkResponse response) -> pplx::task { + auto status = response.status_code(); + MITK_INFO << "Status code: " << status; + + if (web::http::status_codes::OK != status) + { + // throw if something went wrong (e.g. invalid uri) + // this exception can be handled by client + mitkThrow() << "GET ended up with response " << mitk::RESTUtil::convertToUtf8(response.to_string()); + } + + return response.body().read_to_end(*fileBuffer); + }) + // Close the file buffer. + .then([=](size_t) { return fileBuffer->close(); }) + .then([=]() { + //return empty json object + web::json::value data; + return data; + }); +} + +pplx::task mitk::RESTClient::Put(const web::uri &uri, const web::json::value *content) +{ + // Create new HTTP client + MitkClient *client = new MitkClient(uri); + + MITK_INFO << "Calling PUT with " << mitk::RESTUtil::convertToUtf8(uri.path()) << " on client " + << mitk::RESTUtil::convertToUtf8(uri.authority().to_string()); + + // create put request + MitkRequest putRequest(MitkRESTMethods::PUT); + //set body of the put request with data given by client + if (nullptr != content) + { + putRequest.set_body(*content); + } + //make put request + return client->request(putRequest).then([=](pplx::task responseTask) { + try + { + // get response of the request + MitkResponse response = responseTask.get(); + auto status = response.status_code(); + MITK_INFO << " status: " << status; + if (MitkRestStatusCodes::OK != status) + { + // throw if something went wrong (e.g. invalid uri) + // this exception can be handled by client + mitkThrow() << "response was not OK"; + } + + try + { + // parse content type to application/json if it isn't already + // this is important if the content type is e.g. application/dicom+json + utility::string_t requestContentType = response.headers().content_type(); + if (L"application/json" != requestContentType) + { + response.headers().set_content_type(L"application/json"); + } + // return json answer + return response.extract_json().get(); + } + catch (...) + { + mitkThrow() << "extracting json went wrong"; + } + } + catch (...) + { + mitkThrow() << "getting response went wrong"; + } + }); +} + +pplx::task mitk::RESTClient::Post(const web::uri &uri, const web::json::value *content) +{ + // Create new HTTP client + MitkClient *client = new MitkClient(uri); + MITK_INFO << "Calling POST with " << mitk::RESTUtil::convertToUtf8(uri.path()) << " on client " + << mitk::RESTUtil::convertToUtf8(uri.authority().to_string()); + + // Create post request + MitkRequest postRequest(MitkRESTMethods::POST); + // set body of the put request with data given by client + if (nullptr != content) + { + postRequest.set_body(*content); + } + + //make post request + return client->request(postRequest).then([=](pplx::task responseTask) { + try + { + // get response of the request + MitkResponse response = responseTask.get(); + auto status = response.status_code(); + MITK_INFO << " status: " << status; + + if (MitkRestStatusCodes::Created != status) + { + // throw if something went wrong (e.g. invalid uri) + // this exception can be handled by client + mitkThrow() << "response was not Created"; + } + + try + { + // parse content type to application/json if it isn't already + // this is important if the content type is e.g. application/dicom+json + utility::string_t requestContentType = response.headers().content_type(); + if (L"application/json" != requestContentType) + { + response.headers().set_content_type(L"application/json"); + } + // return json answer + return response.extract_json().get(); + } + catch (...) + { + mitkThrow() << "extracting json went wrong"; + } + } + catch(...) + { + mitkThrow() << "getting response went wrong"; + } + }); +} diff --git a/Modules/CppRestSdk/src/mitkRESTManager.cpp b/Modules/CppRestSdk/src/mitkRESTManager.cpp new file mode 100644 index 0000000000..0ed61bda41 --- /dev/null +++ b/Modules/CppRestSdk/src/mitkRESTManager.cpp @@ -0,0 +1,218 @@ +#include "mitkRESTManager.h" +#include +#include + +mitk::RESTManager::RESTManager() {} + +mitk::RESTManager::~RESTManager() {} + +pplx::task mitk::RESTManager::SendRequest(const web::uri &uri, + const RequestType &type, + const web::json::value *content, + const utility::string_t &filePath) +{ + pplx::task answer; + auto client = new RESTClient(); + // according to the RequestType, different HTTP requests are made + switch (type) + { + case RequestType::Get: + + if (filePath.empty()) + { + // no file path specified, starts a normal get request returning the normal json result + answer = client->Get(uri); + } + else + { + // file path ist specified, the result of the get request ist stored in this file + // and an empty json object is returned + answer = client->Get(uri, filePath); + } + break; + + case RequestType::Post: + + if (nullptr == content) + { + // warning because normally you won't create an empty ressource + MITK_WARN << "Content for put is empty, this will create an empty ressource"; + } + answer = client->Post(uri, content); + break; + + case RequestType::Put: + + if (nullptr == content) + { + // warning because normally you won't empty a ressource + MITK_WARN << "Content for put is empty, this will empty the ressource"; + } + answer = client->Put(uri, content); + break; + + default: + mitkThrow() << "Request Type not supported"; + break; + } + + return answer; +} + +void mitk::RESTManager::ReceiveRequest(const web::uri &uri, mitk::IRESTObserver *observer) +{ + // New instance of RESTServer in m_ServerMap, key is port of the request + int port = uri.port(); + + // Checking if port is free to add a new Server + if (0 == m_ServerMap.count(port)) + { + this->AddObserver(uri, observer); + // creating server instance + auto server = new RESTServer(uri.authority()); + // add reference to server instance to map + m_ServerMap[port] = server; + // start Server + server->OpenListener(); + + MITK_INFO << "new server " << mitk::RESTUtil::convertToUtf8(uri.authority().to_string()) << " at port " << port; + } + // If there is already a server under this port + else + { + this->RequestForATakenPort(uri, observer); + } +} + +web::json::value mitk::RESTManager::Handle(const web::uri &uri, const web::json::value &body) +{ + // Checking if there is an observer for the port and path + std::pair key(uri.port(), uri.path()); + if (0 != m_Observers.count(key)) + { + return m_Observers[key]->Notify(uri, body); + } + // No observer under this port, return null which results in status code 404 (s. RESTServer) + else + { + MITK_WARN << "No Observer can handle the data"; + return NULL; + } +} + +void mitk::RESTManager::HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri = L"") +{ + for (auto it = m_Observers.begin(); it != m_Observers.end();) + { + mitk::IRESTObserver *obsMap = it->second; + // Check wether observer is at this place in map + if (observer == obsMap) + { + // Check wether it is the right uri to be deleted + if (uri.is_empty() || uri.path() == it->first.second) + { + int port = it->first.first; + bool noObserverForPort = this->DeleteObserver(it, uri); + if (noObserverForPort) + { + // there isn't an observer at this port, delete m_ServerMap entry for this port + // close listener + m_ServerMap[port]->CloseListener(); + delete m_ServerMap[port]; + // delete server from map + m_ServerMap.erase(port); + } + } + else + { + ++it; + } + } + else + { + ++it; + } + } +} + +const std::map &mitk::RESTManager::GetServerMap() +{ + return m_ServerMap; +} + +std::map, mitk::IRESTObserver *> &mitk::RESTManager::GetObservers() +{ + return m_Observers; +} + +void mitk::RESTManager::AddObserver(const web::uri &uri, IRESTObserver *observer) +{ + // new observer has to be added + std::pair key(uri.port(), uri.path()); + m_Observers[key] = observer; +} + +void mitk::RESTManager::RequestForATakenPort(const web::uri &uri, IRESTObserver *observer) +{ + // Same host, means new observer but not a new server instance + if (uri.authority() == m_ServerMap[uri.port()]->GetUri()) + { + // new observer has to be added + std::pair key(uri.port(), uri.path()); + // only add a new observer if there isn't already an observer for this uri + if (0 == m_Observers.count(key)) + { + this->AddObserver(uri, observer); + + // info output + MITK_INFO << "started listening, no new server instance has been created"; + } + else + { + MITK_ERROR << "Threre is already a observer handeling this data"; + } + } + // Error, since another server can't be added under this port + else + { + MITK_ERROR << "There is already another server listening under this port"; + } +} + +bool mitk::RESTManager::DeleteObserver(std::map, IRESTObserver *>::iterator &it, + const web::uri &uri) +{ + // if yes + // 1. store port and path in a temporary variable + // (path is only needed to create a key for info output) + int port = it->first.first; + utility::string_t path = it->first.second; + std::pair key(port, path); + // 2. delete map entry + it = m_Observers.erase(it); + // 3. check, if there is another observer under this port in observer map (with bool flag) + for (auto o : m_Observers) + { + if (port == o.first.first) + { + // there still exists an observer for this port + return false; + } + } + return true; +} + +void mitk::RESTManager::SetServerMap(const int port, RESTServer *server) +{ + m_ServerMap[port] = server; +} + +void mitk::RESTManager::DeleteFromServerMap(const int port) +{ + m_ServerMap.erase(port); +} + +void mitk::RESTManager::SetObservers(const std::pair key, IRESTObserver *observer) +{ + m_Observers[key] = observer; +} diff --git a/Modules/CppRestSdk/src/mitkRESTServer.cpp b/Modules/CppRestSdk/src/mitkRESTServer.cpp new file mode 100644 index 0000000000..0d0dd28944 --- /dev/null +++ b/Modules/CppRestSdk/src/mitkRESTServer.cpp @@ -0,0 +1,72 @@ +#include "mitkRESTServer.h" +#include +#include + +mitk::RESTServer::RESTServer(const web::uri &uri) +{ + m_Uri = uri; +} + +mitk::RESTServer::~RESTServer() +{ +} + +void mitk::RESTServer::OpenListener() +{ + //create listener + m_Listener = MitkListener(m_Uri); + //Connect incoming get requests with HandleGet method + m_Listener.support(web::http::methods::GET, + std::bind(&mitk::RESTServer::HandleGet, this, std::placeholders::_1)); + //open listener + m_Listener.open().wait(); +} + +void mitk::RESTServer::CloseListener() +{ + //close listener + m_Listener.close().wait(); +} + +web::uri mitk::RESTServer::GetUri() +{ + return m_Uri; +} + +void mitk::RESTServer::HandleGet(const MitkRequest &request) +{ + int port = m_Listener.uri().port(); + //getting exact request uri has to be a parameter in handle function + web::uri_builder build(m_Listener.uri()); + build.append(request.absolute_uri()); + auto uriStringT = build.to_uri().to_string(); + + MITK_INFO << "Get Request for server at port " << port << " Exact request uri: " + << mitk::RESTUtil::convertToUtf8(uriStringT); + + web::json::value content; + //get RESTManager as microservice to call th Handle method of the manager + auto context = us::GetModuleContext(); + + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + web::json::value data = request.extract_json().get(); + //call the handle method + content = managerService->Handle(build.to_uri(), data); + } + } + if (content!=NULL) + { + //content handled by observer + request.reply(MitkRestStatusCodes::OK, content); + } + else + { + //no observer to handle data + request.reply(MitkRestStatusCodes::NotFound); + } +} \ No newline at end of file diff --git a/Modules/CppRestSdk/test/CMakeLists.txt b/Modules/CppRestSdk/test/CMakeLists.txt new file mode 100644 index 0000000000..2d5e45bb88 --- /dev/null +++ b/Modules/CppRestSdk/test/CMakeLists.txt @@ -0,0 +1,2 @@ +MITK_CREATE_MODULE_TESTS() + diff --git a/Modules/CppRestSdk/test/files.cmake b/Modules/CppRestSdk/test/files.cmake new file mode 100644 index 0000000000..7299b4bcce --- /dev/null +++ b/Modules/CppRestSdk/test/files.cmake @@ -0,0 +1,4 @@ +set(MODULE_TESTS + mitkRESTClientTest.cpp + mitkRESTServerTest.cpp +) \ No newline at end of file diff --git a/Modules/CppRestSdk/test/mitkRESTClientTest.cpp b/Modules/CppRestSdk/test/mitkRESTClientTest.cpp new file mode 100644 index 0000000000..a0dc6c713f --- /dev/null +++ b/Modules/CppRestSdk/test/mitkRESTClientTest.cpp @@ -0,0 +1,252 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +// Testing +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" + +// MITK includes +#include "mitkRESTClient.h" + +// VTK includes +#include + +#include "mitkIRESTManager.h" +#include +#include +#include +#include +#include +#include + +class mitkRESTClientTestSuite : public mitk::TestFixture, mitk::IRESTObserver +{ + CPPUNIT_TEST_SUITE(mitkRESTClientTestSuite); + + MITK_TEST(GetRequestValidURI_ReturnsExpectedJSON); + MITK_TEST(MultipleGetRequestValidURI_AllTasksFinish); + MITK_TEST(PutRequestValidURI_ReturnsExpectedJSON); + MITK_TEST(PostRequestValidURI_ReturnsExpectedJSON); + MITK_TEST(GetRequestInvalidURI_ThrowsException); + MITK_TEST(PutRequestInvalidURI_ThrowsException); + MITK_TEST(PostRequestInvalidURI_ThrowsException); + CPPUNIT_TEST_SUITE_END(); + +public: + mitk::IRESTManager *m_Service; + web::json::value m_Data; + + web::json::value Notify(const web::uri &uri, const web::json::value &data) override + { + return m_Data; + } + + /** + * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used + * members for a new test case. (If the members are not used in a test, the method does not need to be called). + */ + void setUp() override + { + m_Data[L"userId"] = web::json::value(1); + m_Data[L"id"] = web::json::value(1); + m_Data[L"title"] = web::json::value(U("this is a title")); + m_Data[L"body"] = web::json::value(U("this is a body")); + + us::ServiceReference serviceRef = + us::GetModuleContext()->GetServiceReference(); + if (serviceRef) + { + m_Service = us::GetModuleContext()->GetService(serviceRef); + } + + if (!m_Service) + { + CPPUNIT_FAIL("Getting Service in setUp() failed"); + } + + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + } + + void tearDown() override + { + m_Service->HandleDeleteObserver(this); + } + + void GetRequestValidURI_ReturnsExpectedJSON() + { + web::json::value *result = new web::json::value(); + + m_Service->SendRequest(L"http://localhost:8080/test") + .then([=](pplx::task resultTask) { + try + { + *result = resultTask.get(); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }) + .wait(); + + CPPUNIT_ASSERT_MESSAGE("Result is the expected JSON value", *result == m_Data); + } + + void MultipleGetRequestValidURI_AllTasksFinish() + { + int *count = new int(0); + + // Create multiple tasks e.g. as shown below + std::vector> tasks; + for (int i = 0; i < 20; ++i) + { + pplx::task singleTask = + m_Service->SendRequest(L"http://localhost:8080/test") + .then([=](pplx::task resultTask) { + // Do something when a single task is done + try + { + resultTask.get(); + *count +=1; + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }); + tasks.emplace_back(singleTask); + } + // Create a joinTask which includes all tasks you've created + auto joinTask = pplx::when_all(begin(tasks), end(tasks)); + // Run asynchonously + joinTask.then([=](pplx::task resultTask) { + // Do something when all tasks are finished + try + { + resultTask.get(); + *count += 1; + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }).wait(); + + CPPUNIT_ASSERT_MESSAGE("Multiple Requests", 21 == *count); + } + + void PutRequestValidURI_ReturnsExpectedJSON() + { + // optional: link might get invalid or content is changed + web::json::value *result = new web::json::value(); + + m_Service + ->SendRequest(L"https://jsonplaceholder.typicode.com/posts/1", mitk::IRESTManager::RequestType::Put, &m_Data) + .then([=](pplx::task resultTask) { + try + { + *result = resultTask.get(); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }) + .wait(); + + CPPUNIT_ASSERT_MESSAGE( + "Result is the expected JSON value, check if the link is still valid since this is an optional test", + *result == m_Data); + } + + void PostRequestValidURI_ReturnsExpectedJSON() + { + // optional: link might get invalid or content is changed + web::json::value *result = new web::json::value(); + web::json::value data; + + data[L"userId"] = m_Data[L"userId"]; + data[L"title"] = m_Data[L"title"]; + data[L"body"] = m_Data[L"body"]; + + m_Service->SendRequest(L"https://jsonplaceholder.typicode.com/posts", mitk::IRESTManager::RequestType::Post, &data) + .then([=](pplx::task resultTask) { + try + { + *result = resultTask.get(); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }) + .wait(); + + data[L"id"] = web::json::value(101); + CPPUNIT_ASSERT_MESSAGE( + "Result is the expected JSON value, check if the link is still valid since this is an optional test", + *result == data); + } + + void GetException() + { + //Method which makes a get request to an invalid uri + web::json::value *result = new web::json::value(); + + m_Service->SendRequest(L"http://localhost:1234/invalid") + .then([=](pplx::task resultTask) { *result = resultTask.get(); }) + .wait(); + } + void GetRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(GetException(), mitk::Exception); } + + void PutException() + { + //Method which makes a put request to an invalid uri + web::json::value *result = new web::json::value(); + + m_Service->SendRequest(L"http://localhost:1234/invalid", mitk::IRESTManager::RequestType::Put, &m_Data) + .then([=](pplx::task resultTask) { + *result = resultTask.get();}) + .wait(); + } + void PutRequestInvalidURI_ThrowsException() + { + CPPUNIT_ASSERT_THROW(PutException(), mitk::Exception); + } + + void PostException() + { + //Method which makes a post request to an invalid uri + web::json::value *result = new web::json::value(); + + m_Service->SendRequest(L"http://localhost:1234/invalid", mitk::IRESTManager::RequestType::Post, &m_Data) + .then([=](pplx::task resultTask) { + *result = resultTask.get(); + }) + .wait(); + } + void PostRequestInvalidURI_ThrowsException() + { + CPPUNIT_ASSERT_THROW(PostException(), mitk::Exception); + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkRESTClient) \ No newline at end of file diff --git a/Modules/CppRestSdk/test/mitkRESTServerTest.cpp b/Modules/CppRestSdk/test/mitkRESTServerTest.cpp new file mode 100644 index 0000000000..8fb6b6ea9a --- /dev/null +++ b/Modules/CppRestSdk/test/mitkRESTServerTest.cpp @@ -0,0 +1,217 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +// Testing +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" + +// MITK includes +#include "mitkRESTServer.h" + +// VTK includes +#include + +class mitkRESTServerTestSuite : public mitk::TestFixture, mitk::IRESTObserver +{ + CPPUNIT_TEST_SUITE(mitkRESTServerTestSuite); + + MITK_TEST(OpenListener_Succeed); + MITK_TEST(TwoListenerSameHostSamePort_OnlyOneOpened); + MITK_TEST(CloseListener_Succeed); + MITK_TEST(OpenMultipleListenerCloseOne_Succeed); + MITK_TEST(OpenMultipleListenerCloseAll_Succeed); + MITK_TEST(OpenListenerGetRequestSamePath_ReturnExpectedJSON); + MITK_TEST(CloseListener_NoRequestPossible); + MITK_TEST(OpenListenerGetRequestDifferentPath_ReturnNotFound); + MITK_TEST(OpenListenerCloseAndReopen_Succeed); + CPPUNIT_TEST_SUITE_END(); + +public: + mitk::IRESTManager *m_Service; + web::json::value m_Data; + + web::json::value Notify(const web::uri &uri, const web::json::value &data) override + { + return m_Data; + } + + /** + * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used + * members for a new test case. (If the members are not used in a test, the method does not need to be called). + */ + void setUp() override + { + m_Data[L"userId"] = web::json::value(1); + m_Data[L"id"] = web::json::value(1); + m_Data[L"title"] = web::json::value(U("this is a title")); + m_Data[L"body"] = web::json::value(U("this is a body")); + us::ServiceReference serviceRef = + us::GetModuleContext()->GetServiceReference(); + if (serviceRef) + { + m_Service = us::GetModuleContext()->GetService(serviceRef); + } + if (!m_Service) + { + CPPUNIT_FAIL("Getting Service in setUp() failed"); + } + } + + void tearDown() override { m_Service->HandleDeleteObserver(this); } + + void OpenListener_Succeed() + { + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + + CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); + } + + void TwoListenerSameHostSamePort_OnlyOneOpened() + { + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + m_Service->ReceiveRequest(L"http://localhost:8080/example", this); + + CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, observer map size is two", + 2 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, server map size is one", + 1 == m_Service->GetServerMap().size()); + } + + void CloseListener_Succeed() + { + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + + CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); + + m_Service->HandleDeleteObserver(this); + + CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size()); + } + + void OpenMultipleListenerCloseOne_Succeed() + { + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + m_Service->ReceiveRequest(L"http://localhost:8090/example", this); + + CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size()); + + m_Service->HandleDeleteObserver(this, L"http://localhost:8080/test"); + + CPPUNIT_ASSERT_MESSAGE("Closed one of two listeners, observer map is size is one", + 1 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Closed one of two listener, server map size is one", + 1 == m_Service->GetServerMap().size()); + } + + void OpenMultipleListenerCloseAll_Succeed() + { + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + m_Service->ReceiveRequest(L"http://localhost:8090/example", this); + + CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size()); + + m_Service->HandleDeleteObserver(this); + + CPPUNIT_ASSERT_MESSAGE("Closed all listeners, observer map is empty", 0 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Closed all listeners, server map is empty", 0 == m_Service->GetServerMap().size()); + } + + void OpenListenerGetRequestSamePath_ReturnExpectedJSON() + { + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + + web::json::value *result = new web::json::value(); + + m_Service->SendRequest(L"http://localhost:8080/test") + .then([=](pplx::task resultTask) { + try + { + *result = resultTask.get(); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }) + .wait(); + + CPPUNIT_ASSERT_MESSAGE("Opened listener and send request to same uri, returned expected JSON", *result == m_Data); + } + + void RequestToClosedListener() + { + web::json::value *result = new web::json::value(); + + m_Service->SendRequest(L"http://localhost:8080/test") + .then([=](pplx::task resultTask) { *result = resultTask.get(); }) + .wait(); + } + void CloseListener_NoRequestPossible() + { + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + + CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); + + m_Service->HandleDeleteObserver(this); + + CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size()); + + CPPUNIT_ASSERT_THROW(RequestToClosedListener(), mitk::Exception); + } + + void RequestToDifferentPathNotFound() + { + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + + web::json::value *result = new web::json::value(); + + m_Service->SendRequest(L"http://localhost:8080/example") + .then([=](pplx::task resultTask) { *result = resultTask.get(); }) + .wait(); + } + void OpenListenerGetRequestDifferentPath_ReturnNotFound() + { + CPPUNIT_ASSERT_THROW(RequestToDifferentPathNotFound(), mitk::Exception); + } + + void OpenListenerCloseAndReopen_Succeed() + { + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + + CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); + + m_Service->HandleDeleteObserver(this); + + CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size()); + + m_Service->ReceiveRequest(L"http://localhost:8080/test", this); + + CPPUNIT_ASSERT_MESSAGE("Reopened listener, observer map size is one", 1 == m_Service->GetObservers().size()); + CPPUNIT_ASSERT_MESSAGE("Reopened listener, server map size is one", 1 == m_Service->GetServerMap().size()); + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkRESTServer) \ No newline at end of file diff --git a/Modules/CppRestSdkQt/CMakeLists.txt b/Modules/CppRestSdkQt/CMakeLists.txt new file mode 100644 index 0000000000..f5edd1b638 --- /dev/null +++ b/Modules/CppRestSdkQt/CMakeLists.txt @@ -0,0 +1,17 @@ +if(MITK_USE_CppRestSdk) + + MITK_CREATE_MODULE( + DEPENDS MitkCore MitkCppRestSdk + PACKAGE_DEPENDS Qt5|Core + #WARNINGS_NO_ERRORS + AUTOLOAD_WITH MitkQtWidgets + ) + + if(TARGET ${MODULE_TARGET}) + find_package(OpenSSL REQUIRED) + + target_link_libraries(${MODULE_TARGET} PRIVATE cpprestsdk::cpprest) + target_link_libraries(${MODULE_TARGET} PUBLIC OpenSSL::SSL) + endif() + +endif() diff --git a/Modules/CppRestSdkQt/files.cmake b/Modules/CppRestSdkQt/files.cmake new file mode 100644 index 0000000000..a61e309ead --- /dev/null +++ b/Modules/CppRestSdkQt/files.cmake @@ -0,0 +1,11 @@ +file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") + +set(CPP_FILES +mitkRESTManagerQt.cpp +mitkCppRestSdkQtActivator.cpp +mitkRESTServerQt.cpp +) + +set(MOC_H_FILES +include/mitkRESTManagerQt.h +include/mitkRESTServerQt.h) \ No newline at end of file diff --git a/Modules/CppRestSdkQt/include/mitkRESTManagerQt.h b/Modules/CppRestSdkQt/include/mitkRESTManagerQt.h new file mode 100644 index 0000000000..1cbc47bdfa --- /dev/null +++ b/Modules/CppRestSdkQt/include/mitkRESTManagerQt.h @@ -0,0 +1,69 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkRESTManagerQt_h +#define mitkRESTManagerQt_h + + +#include +#include + +#include +#include +#include +#include + +#include + +namespace mitk +{ + /** + * @class RESTManagerQt + * @brief this is a microservice for managing REST-requests, used for Qt applications. + * + * This class inherits from the RESTManager in the CppRestSdk Module, which is the equivalent service for + * non-Qt applications. + */ + + class MITKCPPRESTSDKQT_EXPORT RESTManagerQt : public QObject, public RESTManager + { + Q_OBJECT + + public: + RESTManagerQt(); + ~RESTManagerQt() override; + + /** + * @brief starts listening for requests if there isn't another observer listening and the port is free + * + * @param uri defines the URI for which incoming requests should be send to the observer + * @param observer the observer which handles the incoming requests + */ + void ReceiveRequest(const web::uri &uri, IRESTObserver *observer) override; + + /** + * @brief Handles the deletion of an observer for all or a specific uri + * + * @param observer the observer which shouldn't receive requests anymore + * @param uri the uri for which the observer doesn't handle requests anymore (optional) + */ + void HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri) override; + + private: + std::map m_ServerThreadMap; // Map with threads for servers + }; +} // namespace mitk +#endif // !mitkRESTManager_h \ No newline at end of file diff --git a/Modules/CppRestSdkQt/include/mitkRESTServerQt.h b/Modules/CppRestSdkQt/include/mitkRESTServerQt.h new file mode 100644 index 0000000000..c1d2061724 --- /dev/null +++ b/Modules/CppRestSdkQt/include/mitkRESTServerQt.h @@ -0,0 +1,51 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkRESTServerQt_h +#define mitkRESTServerQt_h + +#include +#include + +namespace mitk +{ + class RESTServerQt : public QObject, public RESTServer + { + Q_OBJECT + + + public: + /** + * @brief Creates an server listening to the given URI + * + * @param uri the URI at which the server is listening for requests + */ + RESTServerQt(const web::uri &uri); + ~RESTServerQt(); + + public slots: + /** + * @brief Opens the listener and starts the listening process + */ + void OpenListener(); + + /** + * @brief Closes the listener and stops the listening process + */ + void CloseListener(); + }; +} // namespace mitk +#endif \ No newline at end of file diff --git a/Modules/CppRestSdkQt/src/mitkCppRestSdkQtActivator.cpp b/Modules/CppRestSdkQt/src/mitkCppRestSdkQtActivator.cpp new file mode 100644 index 0000000000..6ce703c567 --- /dev/null +++ b/Modules/CppRestSdkQt/src/mitkCppRestSdkQtActivator.cpp @@ -0,0 +1,32 @@ +#include "mitkCppRestSdkQtActivator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void MitkCppRestSdkQtActivator::Load(us::ModuleContext *context) +{ + // Registration of the RESTManagerMicroservice + m_RESTManagerQt.reset(new mitk::RESTManagerQt); + + us::ServiceProperties props; + //The standard RESTManager which is used for non-qt applications has a ranking of 5 + if (nullptr != QCoreApplication::instance) + { + props[us::ServiceConstants::SERVICE_RANKING()] = 10; + } + else + { + props[us::ServiceConstants::SERVICE_RANKING()] = 0; + } + context->RegisterService(m_RESTManagerQt.get(),props); +} + +void MitkCppRestSdkQtActivator::Unload(us::ModuleContext *) {} + +US_EXPORT_MODULE_ACTIVATOR(MitkCppRestSdkQtActivator) diff --git a/Modules/CppRestSdkQt/src/mitkCppRestSdkQtActivator.h b/Modules/CppRestSdkQt/src/mitkCppRestSdkQtActivator.h new file mode 100644 index 0000000000..7fcdd9e51e --- /dev/null +++ b/Modules/CppRestSdkQt/src/mitkCppRestSdkQtActivator.h @@ -0,0 +1,37 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKCPPRESTSDKQTACTIVATOR_H_ +#define MITKCPPRESTSDKQTACTIVATOR_H_ + +#include + +#include +#include +#include +#include + +class MitkCppRestSdkQtActivator : public us::ModuleActivator +{ + +public: + void Load(us::ModuleContext *context) override; + void Unload(us::ModuleContext *) override; + +private: + std::unique_ptr m_RESTManagerQt; +}; +#endif diff --git a/Modules/CppRestSdkQt/src/mitkRESTManagerQt.cpp b/Modules/CppRestSdkQt/src/mitkRESTManagerQt.cpp new file mode 100644 index 0000000000..d9bac35b67 --- /dev/null +++ b/Modules/CppRestSdkQt/src/mitkRESTManagerQt.cpp @@ -0,0 +1,80 @@ +#include "mitkRESTManagerQt.h" +#include +#include + +mitk::RESTManagerQt::RESTManagerQt() {} + +mitk::RESTManagerQt::~RESTManagerQt() {} + +void mitk::RESTManagerQt::ReceiveRequest(const web::uri &uri, mitk::IRESTObserver *observer) +{ + // New instance of RESTServer in m_ServerMap, key is port of the request + int port = uri.port(); + + // Checking if port is free to add a new Server + if (0 == GetServerMap().count(port)) + { + mitk::RESTManager::AddObserver(uri, observer); + + // creating server instance + auto server = new RESTServerQt(uri.authority()); + // add reference to server instance to map + mitk::RESTManager::SetServerMap(port, server); + + // Move server to seperate Thread and create connections between threads + m_ServerThreadMap[port] = new QThread; + server->moveToThread(m_ServerThreadMap[port]); + + connect(m_ServerThreadMap[port], &QThread::finished, server, &QObject::deleteLater); + + // starting Server + m_ServerThreadMap[port]->start(); + QMetaObject::invokeMethod(server, "OpenListener"); + + MITK_INFO << "new server " << mitk::RESTUtil::convertToUtf8(uri.authority().to_string()) << " at port " << port; + } + // If there is already a server under this port + else + { + mitk::RESTManager::RequestForATakenPort(uri, observer); + } +} + +void mitk::RESTManagerQt::HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri = L"") +{ + auto &observerMap = mitk::RESTManager::GetObservers(); + for (auto it = observerMap.begin(); it != observerMap.end();) + { + mitk::IRESTObserver *obsMap = it->second; + // Check wether observer is at this place in map + if (observer == obsMap) + { + // Check wether it is the right uri to be deleted + if (uri.is_empty() || uri.path() == it->first.second) + { + int port = it->first.first; + bool noObserverForPort = mitk::RESTManager::DeleteObserver(it, uri); + if (noObserverForPort) + { + // there isn't an observer at this port, delete m_ServerMap entry for this port + // close listener + QMetaObject::invokeMethod(static_cast(mitk::RESTManager::GetServerMap().at(port)), "CloseListener"); + // end thread + m_ServerThreadMap[port]->quit(); + m_ServerThreadMap[port]->wait(); + + // delete server from map + mitk::RESTManager::DeleteFromServerMap(port); + } + } + else + { + ++it; + } + } + else + { + ++it; + } + } +} diff --git a/Modules/CppRestSdkQt/src/mitkRESTServerQt.cpp b/Modules/CppRestSdkQt/src/mitkRESTServerQt.cpp new file mode 100644 index 0000000000..ca44ed4256 --- /dev/null +++ b/Modules/CppRestSdkQt/src/mitkRESTServerQt.cpp @@ -0,0 +1,17 @@ +#include "mitkRESTServerQt.h" + +mitk::RESTServerQt::RESTServerQt(const web::uri &uri) : RESTServer(uri) +{ +} + +mitk::RESTServerQt::~RESTServerQt() {} + +void mitk::RESTServerQt::OpenListener() +{ + mitk::RESTServer::OpenListener(); +} + +void mitk::RESTServerQt::CloseListener() +{ + mitk::RESTServer::CloseListener(); +} diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 55eb502956..023e4e2181 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,86 +1,88 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(MITK_MODULES Core CommandLine AppUtil RDF LegacyIO DataTypesExt Annotation LegacyGL AlgorithmsExt MapperExt DICOMReader DICOMReaderServices DICOMQI DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel Chart ImageStatistics ContourModel SurfaceInterpolation Segmentation PlanarFigureSegmentation QtWidgets QtWidgetsExt ImageStatisticsUI SegmentationUI MatchPointRegistration MatchPointRegistrationUI Classification GPGPU OpenIGTLink IGTBase IGT CameraCalibration OpenCL OpenCVVideoSupport QtOverlays ToFHardware ToFProcessing ToFUI PhotoacousticsHardware PhotoacousticsAlgorithms PhotoacousticsLib US USUI DicomUI Remeshing Python QtPython Persistence OpenIGTLinkUI IGTUI DicomRT RTUI IOExt XNAT TubeGraph BiophotonicsHardware DiffusionImaging TumorInvasionAnalysis BoundingShape RenderWindowManager RenderWindowManagerUI SemanticRelations SemanticRelationsUI CEST BasicImageProcessing ModelFit ModelFitUI Pharmacokinetics PharmacokineticsUI + CppRestSdk + CppRestSdkQt ) if(MITK_ENABLE_PIC_READER) list(APPEND MITK_MODULES IpPicSupportIO) endif() diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index f3316ce4d6..94822bc5c7 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,115 +1,116 @@ # Plug-ins must be ordered according to their dependencies set(MITK_PLUGINS org.blueberry.core.runtime:ON org.blueberry.core.expressions:OFF org.blueberry.core.commands:OFF org.blueberry.core.jobs:OFF org.blueberry.ui.qt:OFF org.blueberry.ui.qt.help:ON org.blueberry.ui.qt.log:ON org.blueberry.ui.qt.objectinspector:OFF #org.blueberry.test:ON #org.blueberry.uitest:ON #Testing/org.blueberry.core.runtime.tests:ON #Testing/org.blueberry.osgi.tests:ON org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.coreapplication:OFF org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.cmdlinemodules:OFF org.mitk.gui.qt.chartExample:OFF org.mitk.gui.qt.diffusionimagingapp:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.datamanagerlight:OFF org.mitk.gui.qt.datastorageviewertest:OFF org.mitk.gui.qt.properties:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.dicom:OFF org.mitk.gui.qt.dicominspector:OFF org.mitk.gui.qt.diffusionimaging:OFF org.mitk.gui.qt.diffusionimaging.connectomics:OFF org.mitk.gui.qt.diffusionimaging.denoising:OFF org.mitk.gui.qt.diffusionimaging.fiberfox:OFF org.mitk.gui.qt.diffusionimaging.fiberprocessing:OFF org.mitk.gui.qt.diffusionimaging.ivim:OFF org.mitk.gui.qt.diffusionimaging.odfpeaks:OFF org.mitk.gui.qt.diffusionimaging.partialvolume:OFF org.mitk.gui.qt.diffusionimaging.preprocessing:OFF org.mitk.gui.qt.diffusionimaging.reconstruction:OFF org.mitk.gui.qt.diffusionimaging.registration:OFF org.mitk.gui.qt.diffusionimaging.tractography:OFF org.mitk.gui.qt.diffusionimaging.python:OFF org.mitk.gui.qt.dosevisualization:OFF org.mitk.gui.qt.geometrytools:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.lasercontrol:OFF org.mitk.gui.qt.openigtlink:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.viewnavigator:OFF org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.pointsetinteractionmultispectrum:OFF org.mitk.gui.qt.python:OFF org.mitk.gui.qt.remeshing:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.aicpregistration:OFF org.mitk.gui.qt.renderwindowmanager:OFF org.mitk.gui.qt.semanticrelations:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.tubegraph:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.photoacoustics.pausviewer:OFF org.mitk.gui.qt.photoacoustics.pausmotioncompensation:OFF org.mitk.gui.qt.photoacoustics.imageprocessing:OFF org.mitk.gui.qt.photoacoustics.simulation:OFF org.mitk.gui.qt.photoacoustics.spectralunmixing:OFF org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF org.mitk.gui.qt.eventrecorder:OFF org.mitk.gui.qt.xnat:OFF org.mitk.gui.qt.igt.app.echotrack:OFF org.mitk.gui.qt.spectrocamrecorder:OFF org.mitk.gui.qt.classificationsegmentation:OFF org.mitk.gui.qt.overlaymanager:OFF org.mitk.gui.qt.igt.app.hummelprotocolmeasurements:OFF org.mitk.gui.qt.multilabelsegmentation:OFF org.mitk.matchpoint.core.helper:OFF org.mitk.gui.qt.matchpoint.algorithm.browser:OFF org.mitk.gui.qt.matchpoint.algorithm.control:OFF org.mitk.gui.qt.matchpoint.algorithm.batch:OFF org.mitk.gui.qt.matchpoint.mapper:OFF org.mitk.gui.qt.matchpoint.framereg:OFF org.mitk.gui.qt.matchpoint.visualizer:OFF org.mitk.gui.qt.matchpoint.evaluator:OFF org.mitk.gui.qt.matchpoint.manipulator:OFF org.mitk.gui.qt.preprocessing.resampling:OFF org.mitk.gui.qt.radiomics:OFF org.mitk.gui.qt.cest:OFF org.mitk.gui.qt.fit.demo:OFF org.mitk.gui.qt.fit.inspector:OFF org.mitk.gui.qt.fit.genericfitting:OFF org.mitk.gui.qt.pharmacokinetics.mri:OFF org.mitk.gui.qt.pharmacokinetics.pet:OFF org.mitk.gui.qt.pharmacokinetics.simulation:OFF org.mitk.gui.qt.pharmacokinetics.curvedescriptor:OFF org.mitk.gui.qt.pharmacokinetics.concentration.mri:OFF + org.mitk.gui.qt.cpprestexample:OFF ) diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/CMakeLists.txt b/Plugins/org.mitk.gui.qt.cpprestexample/CMakeLists.txt new file mode 100644 index 0000000000..0c6bb48d51 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/CMakeLists.txt @@ -0,0 +1,7 @@ +project(org_mitk_gui_qt_cpprestexample) + +mitk_create_plugin( + EXPORT_DIRECTIVE THREAD_EXPORT + EXPORTED_INCLUDE_SUFFIXES src + MODULE_DEPENDS PRIVATE MitkQtWidgets MitkCppRestSdkQt +) diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/files.cmake b/Plugins/org.mitk.gui.qt.cpprestexample/files.cmake new file mode 100644 index 0000000000..8807afe94a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/files.cmake @@ -0,0 +1,25 @@ +set(CPP_FILES + src/internal/PluginActivator.cpp + src/internal/QmitkClientView.cpp + src/internal/QmitkServerView.cpp +) + +set(UI_FILES + src/internal/QmitkClientView.ui + src/internal/QmitkServerView.ui +) + +set(MOC_H_FILES + src/internal/PluginActivator.h + src/internal/QmitkClientView.h + src/internal/QmitkServerView.h +) + +set(CACHED_RESOURCE_FILES + resources/ClientIcon.svg + resources/ServerIcon.svg + plugin.xml +) + +set(QRC_FILES +) diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.cpprestexample/manifest_headers.cmake new file mode 100644 index 0000000000..532e8236c2 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/manifest_headers.cmake @@ -0,0 +1,5 @@ +set(Plugin-Name "CppRestExample Plugin") +set(Plugin-Version "1.0") +set(Plugin-Vendor "CAMIC") +set(Plugin-ContactAddress "https://mitk.org") +set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/plugin.xml b/Plugins/org.mitk.gui.qt.cpprestexample/plugin.xml new file mode 100644 index 0000000000..359a53fbba --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/plugin.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/resources/ClientIcon.png b/Plugins/org.mitk.gui.qt.cpprestexample/resources/ClientIcon.png new file mode 100644 index 0000000000..67039a6cfb Binary files /dev/null and b/Plugins/org.mitk.gui.qt.cpprestexample/resources/ClientIcon.png differ diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/resources/ClientIcon.svg b/Plugins/org.mitk.gui.qt.cpprestexample/resources/ClientIcon.svg new file mode 100644 index 0000000000..cc8f386bb1 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/resources/ClientIcon.svg @@ -0,0 +1,111 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/resources/ServerIcon.svg b/Plugins/org.mitk.gui.qt.cpprestexample/resources/ServerIcon.svg new file mode 100644 index 0000000000..60961ebd98 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/resources/ServerIcon.svg @@ -0,0 +1,111 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/PluginActivator.cpp b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/PluginActivator.cpp new file mode 100644 index 0000000000..fe688d1185 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/PluginActivator.cpp @@ -0,0 +1,29 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "PluginActivator.h" +#include "QmitkClientView.h" +#include "QmitkServerView.h" + +void PluginActivator::start(ctkPluginContext* context) +{ + BERRY_REGISTER_EXTENSION_CLASS(QmitkClientView, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkServerView, context) +} + +void PluginActivator::stop(ctkPluginContext*) +{ +} diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/PluginActivator.h b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/PluginActivator.h new file mode 100644 index 0000000000..4a8cacfbb5 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/PluginActivator.h @@ -0,0 +1,33 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef PluginActivator_h +#define PluginActivator_h + +#include + +class PluginActivator : public QObject, public ctkPluginActivator +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_exampleplugin") + Q_INTERFACES(ctkPluginActivator) + +public: + void start(ctkPluginContext* context); + void stop(ctkPluginContext* context); +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkClientView.cpp b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkClientView.cpp new file mode 100644 index 0000000000..8f08f3c3a4 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkClientView.cpp @@ -0,0 +1,269 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkClientView.h" + +#include + +#include "cpprest/json.h" +#include +#include +#include +#include +#include +#include + +const std::string QmitkClientView::VIEW_ID = "org.mitk.views.clientview"; + +QmitkClientView::QmitkClientView() : m_Ui(new Ui::QmitkClientView) +{ +} + +QmitkClientView::~QmitkClientView() +{ + delete m_Ui; +} + +void QmitkClientView::CreateQtPartControl(QWidget *parent) +{ + m_Ui->setupUi(parent); + + connect(m_Ui->getMultiplePushButton, &QPushButton::clicked, this, &QmitkClientView::OnGetMultipleButtonClicked); + connect(m_Ui->getSinglePushButton, &QPushButton::clicked, this, &QmitkClientView::OnGetSingleButtonClicked); + connect(m_Ui->getSavePushButton, &QPushButton::clicked, this, &QmitkClientView::OnGetSaveButtonClicked); + connect(m_Ui->putPushButton, &QPushButton::clicked, this, &QmitkClientView::OnPutButtonClicked); + connect(m_Ui->postPushButton, &QPushButton::clicked, this, &QmitkClientView::OnPostButtonClicked); + connect(this, &QmitkClientView::UpdateProgressBar, this, &QmitkClientView::OnUpdateProgressBar); + connect(this, SIGNAL(UpdateLabel(QString)), this, SLOT(OnUpdateLabel(QString))); + m_Ui->progressBar->setValue(0); +} + +void QmitkClientView::OnUpdateProgressBar() +{ + m_Ui->progressBar->setValue(m_Ui->progressBar->value() + 5); +} + +void QmitkClientView::OnUpdateLabel(QString text) +{ + m_Ui->responseLabel->setText(text); +} + +void QmitkClientView::SetFocus() {} + +void QmitkClientView::OnGetMultipleButtonClicked() +{ + m_Ui->progressBar->setValue(0); + m_Ui->getMultiplePushButton->setDisabled(true); + //Get microservice + us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); + + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + //Create multiple tasks e.g. as shown below + std::vector> tasks; + for (int i = 0; i < 20; i++) + { + pplx::task singleTask = managerService->SendRequest(L"http://193.174.48.78:8090/dcm4chee-arc/aets/DCM4CHEE/rs/studies/1.2.840.113654.2.70.1.97144850941324808603541273584489321943/series/1.2.840.113654.2.70.1.15771179684190906938515254678965278540/instances") + .then([=](pplx::task resultTask) { + //Do something when a single task is done + try + { + resultTask.get(); + emit UpdateProgressBar(); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }); + tasks.emplace_back(singleTask); + } + //Create a joinTask which includes all tasks you've created + auto joinTask = pplx::when_all(begin(tasks), end(tasks)); + //Run asynchonously + joinTask.then([=](pplx::task resultTask) { + //Do something when all tasks are finished + try + { + resultTask.get(); + emit UpdateLabel("All tasks finished"); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }); + m_Ui->responseLabel->setText("Waiting for change"); + } + } + m_Ui->getMultiplePushButton->setEnabled(true); +} + +void QmitkClientView::OnGetSingleButtonClicked() +{ + m_Ui->putPushButton->setDisabled(true); + //Get the micorservice + us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); + + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + //Call the send request method which starts the actual request + managerService + ->SendRequest(L"http://193.174.48.78:8090/dcm4chee-arc/aets/DCM4CHEE/rs/studies/1.2.840.113654.2.70.1.97144850941324808603541273584489321943/series/1.2.840.113654.2.70.1.15771179684190906938515254678965278540/instances") + .then([=](pplx::task resultTask)/*It is important to use task-based continuation*/ { + try + { + //Get the result of the request + //This will throw an exception if the ascendent task threw an exception (e.g. invalid URI) + web::json::value result = resultTask.get(); + //Do something with the result (e.g. convert it to a QSrting to update an UI element) + utility::string_t stringT = result.to_string(); + std::string stringStd(stringT.begin(), stringT.end()); + QString stringQ = QString::fromStdString(stringStd); + //Note: if you want to update your UI, do this by using signals and slots. + //The UI can't be updated from a Thread different to the Qt main thread + emit UpdateLabel(stringQ); + } + catch (const mitk::Exception &exception) + { + //Exceptions from ascendent tasks are catched here + MITK_ERROR << exception.what(); + return; + } + }); + } + } + m_Ui->putPushButton->setEnabled(true); +} + +void QmitkClientView::OnGetSaveButtonClicked() +{ + m_Ui->putPushButton->setDisabled(true); + us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); + + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + managerService + ->SendRequest(L"http://193.174.48.78:8090/dcm4chee-arc/aets/DCM4CHEE/rs/studies/1.2.840.113654.2.70.1.97144850941324808603541273584489321943/series/1.2.840.113654.2.70.1.15771179684190906938515254678965278540/instances", mitk::IRESTManager::RequestType::Get, NULL,L"FileStream.txt") + .then([=](pplx::task resultTask) { + try + { + web::json::value result = resultTask.get(); + utility::string_t stringT = result.to_string(); + std::string stringStd(stringT.begin(), stringT.end()); + QString stringQ = QString::fromStdString(stringStd); + emit UpdateLabel(stringQ); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }); + m_Ui->responseLabel->setText("Waiting for change"); + } + } + m_Ui->putPushButton->setEnabled(true); +} + +void QmitkClientView::OnPutButtonClicked() +{ + m_Ui->putPushButton->setDisabled(true); + us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); + + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + web::json::value data; + data[L"userId"] = web::json::value(1); + data[L"id"] = web::json::value(1); + data[L"title"] = web::json::value(U("this is a changed title")); + data[L"body"] = web::json::value(U("and the body is changed as well")); + managerService->SendRequest( + L"https://jsonplaceholder.typicode.com/posts/1", mitk::IRESTManager::RequestType::Put, &data) + .then([=](pplx::task resultTask) { + try + { + web::json::value result = resultTask.get(); + utility::string_t stringT = result.to_string(); + std::string stringStd(stringT.begin(), stringT.end()); + QString stringQ = QString::fromStdString(stringStd); + emit UpdateLabel(stringQ); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }); + } + } + m_Ui->putPushButton->setEnabled(true); +} + +void QmitkClientView::OnPostButtonClicked() +{ + m_Ui->postPushButton->setDisabled(true); + us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); + + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + web::json::value data; + data[L"userId"] = web::json::value(1); + data[L"title"] = web::json::value(U("this is a new title")); + data[L"body"] = web::json::value(U("this is a new body")); + managerService + ->SendRequest(L"https://jsonplaceholder.typicode.com/posts", mitk::IRESTManager::RequestType::Post, &data) + .then([=](pplx::task resultTask) { + try + { + web::json::value result = resultTask.get(); + utility::string_t stringT = result.to_string(); + std::string stringStd(stringT.begin(), stringT.end()); + QString stringQ = QString::fromStdString(stringStd); + emit UpdateLabel(stringQ); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << exception.what(); + return; + } + }); + } + } + m_Ui->postPushButton->setEnabled(true); +} diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkClientView.h b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkClientView.h new file mode 100644 index 0000000000..15ccb1c4aa --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkClientView.h @@ -0,0 +1,62 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QmitkClientView_h +#define QmitkClientView_h + +#include +#include "cpprest/uri.h" +#include +namespace Ui +{ + class QmitkClientView; +} + +namespace mitk +{ + class RESTManager; +} + +class QmitkClientView : public QmitkAbstractView +{ + Q_OBJECT + +public: + static const std::string VIEW_ID; + + QmitkClientView(); + ~QmitkClientView() override; + + void CreateQtPartControl(QWidget *parent) override; + +signals: + void UpdateProgressBar(); + void UpdateLabel(QString); +private slots: + void OnGetMultipleButtonClicked(); + void OnGetSingleButtonClicked(); + void OnGetSaveButtonClicked(); + void OnPutButtonClicked(); + void OnPostButtonClicked(); + void OnUpdateProgressBar(); + void OnUpdateLabel(QString text); + +private: + void SetFocus() override; + Ui::QmitkClientView *m_Ui; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkClientView.ui b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkClientView.ui new file mode 100644 index 0000000000..37ca54a216 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkClientView.ui @@ -0,0 +1,93 @@ + + + QmitkClientView + + + + 0 + 0 + 253 + 265 + + + + Client View + + + + + + + + + GET (single tasks) + + + + + + + GET (multiple tasks) + + + + + + + GET and Save to File + + + + + + + PUT + + + + + + + POST + + + + + + + 24 + + + + + + + This label changes after request + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 220 + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkServerView.cpp b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkServerView.cpp new file mode 100644 index 0000000000..aa602c5c00 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkServerView.cpp @@ -0,0 +1,146 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkServerView.h" + +#include + +#include +#include +#include +#include +#include +#include + +const std::string QmitkServerView::VIEW_ID = "org.mitk.views.serverview"; + +QmitkServerView::QmitkServerView() + : m_Ui(new Ui::QmitkServerView) +{ +} + +web::json::value QmitkServerView::Notify(const web::uri &uri, const web::json::value &data) +{ + MITK_INFO << "Observer: Data in observer"; + return data.at(U("key 1")); +} + +QmitkServerView::~QmitkServerView() +{ + delete m_Ui; +} + +void QmitkServerView::CreateQtPartControl(QWidget* parent) +{ + m_Ui->setupUi(parent); + + connect(m_Ui->stopAllPushButton, &QPushButton::clicked, this, &QmitkServerView::OnStopAllButtonClicked); + + connect(m_Ui->testListenCheckBox, &QCheckBox::clicked, this, &QmitkServerView::OnTestListenCheckBoxClicked); + connect(m_Ui->exampleListenCheckBox, &QCheckBox::clicked, this, &QmitkServerView::OnExampleListenCheckBoxClicked); + connect(m_Ui->port8090CheckBox, &QCheckBox::clicked, this, &QmitkServerView::OnPort8090ListenCheckBoxClicked); + + connect(m_Ui->stopAllPushButton, &QPushButton::clicked, this, &QmitkServerView::OnStopAllButtonClicked); +} + +void QmitkServerView::SetFocus() +{ +} + + +void QmitkServerView::StartListening(web::uri uri) +{ + us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); + + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + managerService->ReceiveRequest(uri, this); + } + } +} + +void QmitkServerView::StopListening(web::uri uri) +{ + us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); + + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + managerService->HandleDeleteObserver(this,uri); + } + } +} + +void QmitkServerView::OnStopAllButtonClicked() +{ + us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); + + auto managerRef = context->GetServiceReference(); + if (managerRef) + { + auto managerService = context->GetService(managerRef); + if (managerService) + { + managerService->HandleDeleteObserver(this); + } + } + m_Ui->testListenCheckBox->setChecked(false); + m_Ui->exampleListenCheckBox->setChecked(false); + m_Ui->port8090CheckBox->setChecked(false); +} + +void QmitkServerView::OnTestListenCheckBoxClicked() +{ + if (m_Ui->testListenCheckBox->isChecked()) + { + StartListening(L"http://localhost:8080/test"); + } + else + { + StopListening(L"http://localhost:8080/test"); + } +} + +void QmitkServerView::OnExampleListenCheckBoxClicked() +{ + if (m_Ui->exampleListenCheckBox->isChecked()) + { + StartListening(L"http://localhost:8080/example"); + } + else + { + StopListening(L"http://localhost:8080/example"); + } +} + +void QmitkServerView::OnPort8090ListenCheckBoxClicked() +{ + if (m_Ui->port8090CheckBox->isChecked()) + { + StartListening(L"http://localhost:8090"); + } + else + { + StopListening(L"http://localhost:8090"); + } +} diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkServerView.h b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkServerView.h new file mode 100644 index 0000000000..e46846662c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkServerView.h @@ -0,0 +1,60 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QmitkServerView_h +#define QmitkServerView_h + +#include +#include +#include "cpprest/uri.h" + +namespace Ui +{ + class QmitkServerView; +} + +namespace mitk +{ + class RESTManager; +} + +class QmitkServerView : public QmitkAbstractView, public mitk::IRESTObserver +{ + Q_OBJECT + +public: + static const std::string VIEW_ID; + + QmitkServerView(); + ~QmitkServerView() override; + + void CreateQtPartControl(QWidget *parent) override; + web::json::value Notify(const web::uri &uri, const web::json::value &data) override; + +private slots: + void OnStopAllButtonClicked(); + void OnTestListenCheckBoxClicked(); + void OnExampleListenCheckBoxClicked(); + void OnPort8090ListenCheckBoxClicked(); + +private: + void SetFocus() override; + void StartListening(web::uri); + void StopListening(web::uri); + Ui::QmitkServerView *m_Ui; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkServerView.ui b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkServerView.ui new file mode 100644 index 0000000000..e5f4e1a6bf --- /dev/null +++ b/Plugins/org.mitk.gui.qt.cpprestexample/src/internal/QmitkServerView.ui @@ -0,0 +1,76 @@ + + + QmitkServerView + + + + 0 + 0 + 253 + 206 + + + + Server View + + + + + + http://localhost:8080/test + + + + + + + http://localhost:8080/example + + + + + + + http://localhost:8090 + + + + + + + Stop Listening all + + + + + + + + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 220 + + + + + + + + + + diff --git a/SuperBuild.cmake b/SuperBuild.cmake index ca0f1a690b..8d638be6be 100644 --- a/SuperBuild.cmake +++ b/SuperBuild.cmake @@ -1,466 +1,467 @@ include(mitkFunctionInstallExternalCMakeProject) #----------------------------------------------------------------------------- # Convenient macro allowing to download a file #----------------------------------------------------------------------------- if(NOT MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL) set(MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL http://mitk.org/download/thirdparty) endif() macro(downloadFile url dest) file(DOWNLOAD ${url} ${dest} STATUS status) list(GET status 0 error_code) list(GET status 1 error_msg) if(error_code) message(FATAL_ERROR "error: Failed to download ${url} - ${error_msg}") endif() endmacro() #----------------------------------------------------------------------------- # MITK Prerequisites #----------------------------------------------------------------------------- if(UNIX AND NOT APPLE) include(mitkFunctionCheckPackageHeader) # Check for libxt-dev mitkFunctionCheckPackageHeader(StringDefs.h libxt-dev /usr/include/X11/) # Check for libtiff4-dev mitkFunctionCheckPackageHeader(tiff.h libtiff4-dev) endif() # We need a proper patch program. On Linux and MacOS, we assume # that "patch" is available. On Windows, we download patch.exe # if not patch program is found. find_program(PATCH_COMMAND patch) if((NOT PATCH_COMMAND OR NOT EXISTS ${PATCH_COMMAND}) AND WIN32) downloadFile(${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/patch.exe ${CMAKE_CURRENT_BINARY_DIR}/patch.exe) find_program(PATCH_COMMAND patch ${CMAKE_CURRENT_BINARY_DIR}) endif() if(NOT PATCH_COMMAND) message(FATAL_ERROR "No patch program found.") endif() #----------------------------------------------------------------------------- # ExternalProjects #----------------------------------------------------------------------------- get_property(external_projects GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) list(REMOVE_ITEM external_projects Python) list(REMOVE_ITEM external_projects OpenMP) if(MITK_CTEST_SCRIPT_MODE) # Write a file containing the list of enabled external project targets. # This file can be read by a ctest script to separately build projects. set(SUPERBUILD_TARGETS ) foreach(proj ${external_projects}) if(MITK_USE_${proj}) list(APPEND SUPERBUILD_TARGETS ${proj}) endif() endforeach() file(WRITE "${CMAKE_BINARY_DIR}/SuperBuildTargets.cmake" "set(SUPERBUILD_TARGETS ${SUPERBUILD_TARGETS})") endif() # A list of "nice" external projects, playing well together with CMake set(nice_external_projects ${external_projects}) list(REMOVE_ITEM nice_external_projects Boost) foreach(proj ${nice_external_projects}) if(MITK_USE_${proj}) set(EXTERNAL_${proj}_DIR "${${proj}_DIR}" CACHE PATH "Path to ${proj} build directory") mark_as_advanced(EXTERNAL_${proj}_DIR) if(EXTERNAL_${proj}_DIR) set(${proj}_DIR ${EXTERNAL_${proj}_DIR}) endif() endif() endforeach() set(EXTERNAL_BOOST_ROOT "${BOOST_ROOT}" CACHE PATH "Path to Boost directory") mark_as_advanced(EXTERNAL_BOOST_ROOT) if(EXTERNAL_BOOST_ROOT) set(BOOST_ROOT ${EXTERNAL_BOOST_ROOT}) endif() # Setup file for setting custom ctest vars configure_file( CMake/SuperbuildCTestCustom.cmake.in ${MITK_BINARY_DIR}/CTestCustom.cmake @ONLY ) if(BUILD_TESTING) set(EXTERNAL_MITK_DATA_DIR "${MITK_DATA_DIR}" CACHE PATH "Path to the MITK data directory") mark_as_advanced(EXTERNAL_MITK_DATA_DIR) if(EXTERNAL_MITK_DATA_DIR) set(MITK_DATA_DIR ${EXTERNAL_MITK_DATA_DIR}) endif() endif() #----------------------------------------------------------------------------- # External project settings #----------------------------------------------------------------------------- include(ExternalProject) set(ep_prefix "${CMAKE_BINARY_DIR}/ep") set_property(DIRECTORY PROPERTY EP_PREFIX ${ep_prefix}) # Compute -G arg for configuring external projects with the same CMake generator: if(CMAKE_EXTRA_GENERATOR) set(gen "${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}") else() set(gen "${CMAKE_GENERATOR}") endif() # Use this value where semi-colons are needed in ep_add args: set(sep "^^") ## if(MSVC_VERSION) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /bigobj /MP") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /MP") endif() if(MITK_USE_Boost_LIBRARIES) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ALL_DYN_LINK") endif() # This is a workaround for passing linker flags # actually down to the linker invocation set(_cmake_required_flags_orig ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "-Wl,-rpath") mitkFunctionCheckCompilerFlags(${CMAKE_REQUIRED_FLAGS} _has_rpath_flag) set(CMAKE_REQUIRED_FLAGS ${_cmake_required_flags_orig}) set(_install_rpath_linkflag ) if(_has_rpath_flag) if(APPLE) set(_install_rpath_linkflag "-Wl,-rpath,@loader_path/../lib") else() set(_install_rpath_linkflag "-Wl,-rpath='$ORIGIN/../lib'") endif() endif() set(_install_rpath) if(APPLE) set(_install_rpath "@loader_path/../lib") elseif(UNIX) # this work for libraries as well as executables set(_install_rpath "\$ORIGIN/../lib") endif() set(ep_common_args -DCMAKE_CXX_EXTENSIONS:STRING=${CMAKE_CXX_EXTENSIONS} -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED:BOOL=${CMAKE_CXX_STANDARD_REQUIRED} -DCMAKE_MACOSX_RPATH:BOOL=TRUE "-DCMAKE_INSTALL_RPATH:STRING=${_install_rpath}" -DBUILD_TESTING:BOOL=OFF -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} -DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${MITK_CXX14_FLAG}" #debug flags -DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} -DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG} #release flags -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE} -DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE} #relwithdebinfo -DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DCMAKE_C_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO} #link flags -DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS} -DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS} -DCMAKE_MODULE_LINKER_FLAGS:STRING=${CMAKE_MODULE_LINKER_FLAGS} ) set(DCMTK_CMAKE_DEBUG_POSTFIX ) # python libraries wont work with it if(NOT MITK_USE_Python) list(APPEND ep_common_args -DCMAKE_DEBUG_POSTFIX:STRING=d) set(DCMTK_CMAKE_DEBUG_POSTFIX d) endif() set(ep_common_cache_args ) set(ep_common_cache_default_args "-DCMAKE_PREFIX_PATH:PATH=;${CMAKE_PREFIX_PATH}" "-DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH}" "-DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH}" ) # Pass the CMAKE_OSX variables to external projects if(APPLE) set(MAC_OSX_ARCHITECTURE_ARGS -DCMAKE_OSX_ARCHITECTURES:PATH=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET:PATH=${CMAKE_OSX_DEPLOYMENT_TARGET} -DCMAKE_OSX_SYSROOT:PATH=${CMAKE_OSX_SYSROOT} ) set(ep_common_args ${MAC_OSX_ARCHITECTURE_ARGS} ${ep_common_args} ) endif() set(mitk_superbuild_ep_args) set(mitk_depends ) # Include external projects include(CMakeExternals/MITKData.cmake) foreach(p ${external_projects}) if(EXISTS ${CMAKE_SOURCE_DIR}/CMakeExternals/${p}.cmake) include(CMakeExternals/${p}.cmake) else() foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) get_filename_component(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIR} ABSOLUTE) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMakeExternals) if(EXISTS ${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/${p}.cmake) include(${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/${p}.cmake) break() endif() endforeach() endif() list(APPEND mitk_superbuild_ep_args -DMITK_USE_${p}:BOOL=${MITK_USE_${p}} ) get_property(_package GLOBAL PROPERTY MITK_${p}_PACKAGE) if(_package) list(APPEND mitk_superbuild_ep_args -D${p}_DIR:PATH=${${p}_DIR}) endif() list(APPEND mitk_depends ${${p}_DEPENDS}) endforeach() if (SWIG_EXECUTABLE) list(APPEND mitk_superbuild_ep_args -DSWIG_EXECUTABLE=${SWIG_EXECUTABLE}) endif() #----------------------------------------------------------------------------- # Set superbuild boolean args #----------------------------------------------------------------------------- set(mitk_cmake_boolean_args BUILD_SHARED_LIBS WITH_COVERAGE BUILD_TESTING MITK_BUILD_ALL_PLUGINS MITK_BUILD_ALL_APPS MITK_BUILD_EXAMPLES MITK_USE_Qt5 MITK_USE_SYSTEM_Boost MITK_USE_BLUEBERRY MITK_USE_OpenCL + MITK_USE_CppRestSdk MITK_ENABLE_PIC_READER ) #----------------------------------------------------------------------------- # Create the final variable containing superbuild boolean args #----------------------------------------------------------------------------- set(mitk_superbuild_boolean_args) foreach(mitk_cmake_arg ${mitk_cmake_boolean_args}) list(APPEND mitk_superbuild_boolean_args -D${mitk_cmake_arg}:BOOL=${${mitk_cmake_arg}}) endforeach() if(MITK_BUILD_ALL_PLUGINS) list(APPEND mitk_superbuild_boolean_args -DBLUEBERRY_BUILD_ALL_PLUGINS:BOOL=ON) endif() #----------------------------------------------------------------------------- # MITK Utilities #----------------------------------------------------------------------------- set(proj MITK-Utilities) ExternalProject_Add(${proj} DOWNLOAD_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS ${mitk_depends} ) #----------------------------------------------------------------------------- # Additional MITK CXX/C Flags #----------------------------------------------------------------------------- set(MITK_ADDITIONAL_C_FLAGS "" CACHE STRING "Additional C Flags for MITK") set(MITK_ADDITIONAL_C_FLAGS_RELEASE "" CACHE STRING "Additional Release C Flags for MITK") set(MITK_ADDITIONAL_C_FLAGS_DEBUG "" CACHE STRING "Additional Debug C Flags for MITK") mark_as_advanced(MITK_ADDITIONAL_C_FLAGS MITK_ADDITIONAL_C_FLAGS_DEBUG MITK_ADDITIONAL_C_FLAGS_RELEASE) set(MITK_ADDITIONAL_CXX_FLAGS "" CACHE STRING "Additional CXX Flags for MITK") set(MITK_ADDITIONAL_CXX_FLAGS_RELEASE "" CACHE STRING "Additional Release CXX Flags for MITK") set(MITK_ADDITIONAL_CXX_FLAGS_DEBUG "" CACHE STRING "Additional Debug CXX Flags for MITK") mark_as_advanced(MITK_ADDITIONAL_CXX_FLAGS MITK_ADDITIONAL_CXX_FLAGS_DEBUG MITK_ADDITIONAL_CXX_FLAGS_RELEASE) set(MITK_ADDITIONAL_EXE_LINKER_FLAGS "" CACHE STRING "Additional exe linker flags for MITK") set(MITK_ADDITIONAL_SHARED_LINKER_FLAGS "" CACHE STRING "Additional shared linker flags for MITK") set(MITK_ADDITIONAL_MODULE_LINKER_FLAGS "" CACHE STRING "Additional module linker flags for MITK") mark_as_advanced(MITK_ADDITIONAL_EXE_LINKER_FLAGS MITK_ADDITIONAL_SHARED_LINKER_FLAGS MITK_ADDITIONAL_MODULE_LINKER_FLAGS) #----------------------------------------------------------------------------- # MITK Configure #----------------------------------------------------------------------------- if(MITK_INITIAL_CACHE_FILE) set(mitk_initial_cache_arg -C "${MITK_INITIAL_CACHE_FILE}") endif() set(mitk_optional_cache_args ) foreach(type RUNTIME ARCHIVE LIBRARY) if(DEFINED CTK_PLUGIN_${type}_OUTPUT_DIRECTORY) list(APPEND mitk_optional_cache_args -DCTK_PLUGIN_${type}_OUTPUT_DIRECTORY:PATH=${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}) endif() endforeach() # Optional python variables if(MITK_USE_Python) list(APPEND mitk_optional_cache_args -DMITK_USE_Python:BOOL=${MITK_USE_Python} -DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE} -DPYTHON_INCLUDE_DIR:PATH=${PYTHON_INCLUDE_DIR} -DPYTHON_LIBRARY:FILEPATH=${PYTHON_LIBRARY} -DPYTHON_INCLUDE_DIR2:PATH=${PYTHON_INCLUDE_DIR2} ) endif() if(Eigen_INCLUDE_DIR) list(APPEND mitk_optional_cache_args -DEigen_INCLUDE_DIR:PATH=${Eigen_INCLUDE_DIR} ) endif() # Optional pass through of Doxygen if(DOXYGEN_EXECUTABLE) list(APPEND mitk_optional_cache_args -DDOXYGEN_EXECUTABLE:FILEPATH=${DOXYGEN_EXECUTABLE} ) endif() set(proj MITK-Configure) ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} DOWNLOAD_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_CACHE_ARGS # --------------- Build options ---------------- -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} "-DCMAKE_PREFIX_PATH:PATH=${ep_prefix};${CMAKE_PREFIX_PATH}" "-DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH}" "-DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH}" # --------------- Compile options ---------------- -DCMAKE_CXX_EXTENSIONS:STRING=${CMAKE_CXX_EXTENSIONS} -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED:BOOL=${CMAKE_CXX_STANDARD_REQUIRED} -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} ${MITK_ADDITIONAL_C_FLAGS}" "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${MITK_ADDITIONAL_CXX_FLAGS}" # debug flags "-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} ${MITK_ADDITIONAL_CXX_FLAGS_DEBUG}" "-DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG} ${MITK_ADDITIONAL_C_FLAGS_DEBUG}" # release flags "-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE} ${MITK_ADDITIONAL_CXX_FLAGS_RELEASE}" "-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE} ${MITK_ADDITIONAL_C_FLAGS_RELEASE}" # relwithdebinfo -DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DCMAKE_C_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO} # link flags "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS} ${MITK_ADDITIONAL_EXE_LINKER_FLAGS}" "-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS} ${MITK_ADDITIONAL_SHARED_LINKER_FLAGS}" "-DCMAKE_MODULE_LINKER_FLAGS:STRING=${CMAKE_MODULE_LINKER_FLAGS} ${MITK_ADDITIONAL_MODULE_LINKER_FLAGS}" # Output directories -DMITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY} -DMITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY} -DMITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY} # ------------- Boolean build options -------------- ${mitk_superbuild_boolean_args} ${mitk_optional_cache_args} -DMITK_USE_SUPERBUILD:BOOL=OFF -DMITK_BUILD_CONFIGURATION:STRING=${MITK_BUILD_CONFIGURATION} -DCTEST_USE_LAUNCHERS:BOOL=${CTEST_USE_LAUNCHERS} # ----------------- Miscellaneous --------------- -DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH} -DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH} -DMITK_CTEST_SCRIPT_MODE:STRING=${MITK_CTEST_SCRIPT_MODE} -DMITK_SUPERBUILD_BINARY_DIR:PATH=${MITK_BINARY_DIR} -DMITK_MODULES_TO_BUILD:INTERNAL=${MITK_MODULES_TO_BUILD} -DMITK_WHITELIST:STRING=${MITK_WHITELIST} -DMITK_WHITELISTS_EXTERNAL_PATH:STRING=${MITK_WHITELISTS_EXTERNAL_PATH} -DMITK_WHITELISTS_INTERNAL_PATH:STRING=${MITK_WHITELISTS_INTERNAL_PATH} -DMITK_EXTENSION_DIRS:STRING=${MITK_EXTENSION_DIRS} -DMITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES} -DMITK_ACCESSBYITK_FLOATING_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES} -DMITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES} -DMITK_ACCESSBYITK_VECTOR_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} -DMITK_ACCESSBYITK_DIMENSIONS:STRING=${MITK_ACCESSBYITK_DIMENSIONS} # --------------- External project options --------------- -DMITK_DATA_DIR:PATH=${MITK_DATA_DIR} -DMITK_EXTERNAL_PROJECT_PREFIX:PATH=${ep_prefix} -DCppMicroServices_DIR:PATH=${CppMicroServices_DIR} -DDCMTK_CMAKE_DEBUG_POSTFIX:STRING=${DCMTK_CMAKE_DEBUG_POSTFIX} -DBOOST_ROOT:PATH=${BOOST_ROOT} -DBOOST_LIBRARYDIR:PATH=${BOOST_LIBRARYDIR} -DMITK_USE_Boost_LIBRARIES:STRING=${MITK_USE_Boost_LIBRARIES} -DQt5_DIR:PATH=${Qt5_DIR} CMAKE_ARGS ${mitk_initial_cache_arg} ${MAC_OSX_ARCHITECTURE_ARGS} ${mitk_superbuild_ep_args} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} BINARY_DIR ${CMAKE_BINARY_DIR}/MITK-build BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS MITK-Utilities ) mitkFunctionInstallExternalCMakeProject(${proj}) #----------------------------------------------------------------------------- # MITK #----------------------------------------------------------------------------- if(CMAKE_GENERATOR MATCHES ".*Makefiles.*") set(mitk_build_cmd "$(MAKE)") else() set(mitk_build_cmd ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/MITK-build --config ${CMAKE_CFG_INTDIR}) endif() if(NOT DEFINED SUPERBUILD_EXCLUDE_MITKBUILD_TARGET OR NOT SUPERBUILD_EXCLUDE_MITKBUILD_TARGET) set(MITKBUILD_TARGET_ALL_OPTION "ALL") else() set(MITKBUILD_TARGET_ALL_OPTION "") endif() add_custom_target(MITK-build ${MITKBUILD_TARGET_ALL_OPTION} COMMAND ${mitk_build_cmd} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/MITK-build DEPENDS MITK-Configure ) #----------------------------------------------------------------------------- # Custom target allowing to drive the build of the MITK project itself #----------------------------------------------------------------------------- add_custom_target(MITK COMMAND ${mitk_build_cmd} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/MITK-build )