diff --git a/CMake/mitkFunctionCreateModule.cmake b/CMake/mitkFunctionCreateModule.cmake index adde582878..3c4df77d3f 100644 --- a/CMake/mitkFunctionCreateModule.cmake +++ b/CMake/mitkFunctionCreateModule.cmake @@ -1,608 +1,612 @@ function(_link_directories_for_packages) set(ALL_LIBRARY_DIRS ) foreach(package ${ARGN}) if(NOT ${package} MATCHES "^(Qt[45].*|ITK|VTK)$") foreach(dir ${MODULES_PACKAGE_DEPENDS_DIRS}) if(EXISTS "${dir}/MITK_${package}_Config.cmake") include("${dir}/MITK_${package}_Config.cmake") break() endif() endforeach() endif() endforeach() if(ALL_LIBRARY_DIRS) list(REMOVE_DUPLICATES ALL_LIBRARY_DIRS) link_directories(${ALL_LIBRARY_DIRS}) endif() endfunction() ################################################################## # # mitk_create_module # #! Creates a module for the automatic module dependency system within MITK. #! Configurations are generated in the moduleConf directory. #! #! USAGE: #! #! \code #! MITK_CREATE_MODULE([] #! [INCLUDE_DIRS ] #! [INTERNAL_INCLUDE_DIRS ] #! [DEPENDS ] #! [PACKAGE_DEPENDS ] #! [TARGET_DEPENDS #! [EXPORT_DEFINE ] #! [QT_MODULE] #! [HEADERS_ONLY] #! [WARNINGS_AS_ERRORS] #! \endcode #! #! The parameter specifies the name of the module which is used #! create a logical target name. The parameter is options in case the #! MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME variable evaluates to TRUE. The #! module name will then be derived from the directory name in which this #! macro is called. #! #! If set, the following variables will be used to validate the module name: #! #! MITK_MODULE_NAME_REGEX_MATCH The module name must match this regular expression. #! MITK_MODULE_NAME_REGEX_NOT_MATCH The module name must not match this regular expression. #! #! If the MITK_MODULE_NAME_PREFIX variable is set, the module name will be prefixed #! with its contents. #! #! A modules source files are specified in a separate CMake file usually #! called files.cmake, located in the module root directory. The #! mitk_create_module() macro evaluates the following CMake variables #! from the files.cmake file: #! #! - CPP_FILES A list of .cpp files #! - H_FILES A list of .h files without a corresponding .cpp file #! - TXX_FILES A list of .txx files #! - RESOURCE_FILES A list of files (resources) which are embedded into the module #! - MOC_H_FILES A list of Qt header files which should be processed by the MOC #! - UI_FILES A list of .ui Qt UI files #! - QRC_FILES A list of .qrc Qt resource files #! - DOX_FILES A list of .dox Doxygen files #! #! List of variables available after the function is called: #! - MODULE_NAME #! - MODULE_TARGET #! - MODULE_IS_ENABLED #! - MODULE_SUBPROJECTS #! - ALL_META_DEPENDENCIES #! #! \param QT_MODULE deprecated. Just use Qt4 or Qt5 in the PACKAGE_DEPENDS argument. #! \param HEADERS_ONLY specify this if the modules just contains header files. ################################################################## function(mitk_create_module) set(_macro_params SUBPROJECTS # list of CDash labels VERSION # module version number, e.g. "1.2.0" INCLUDE_DIRS # exported include dirs (used in mitkMacroCreateModuleConf.cmake) INTERNAL_INCLUDE_DIRS # include dirs internal to this module DEPENDS # list of modules this module depends on DEPENDS_INTERNAL # list of modules this module internally depends on PACKAGE_DEPENDS # list of "packages this module depends on (e.g. Qt, VTK, etc.) TARGET_DEPENDS # list of CMake targets this module should depend on EXPORT_DEFINE # export macro name for public symbols of this module AUTOLOAD_WITH # a module target name identifying the module which will trigger the # automatic loading of this module ADDITIONAL_LIBS # list of addidtional libraries linked to this module FILES_CMAKE # file name of a CMake file setting source list variables # (defaults to files.cmake) GENERATED_CPP # not used (?) DEPRECATED_SINCE # marks this modules as deprecated DESCRIPTION # a description for this module ) set(_macro_options QT_MODULE # the module makes use of Qt4 features and needs moc and ui generated files FORCE_STATIC # force building this module as a static library HEADERS_ONLY # this module is a headers-only library GCC_DEFAULT_VISIBILITY # do not use gcc visibility flags - all symbols will be exported NO_INIT # do not create CppMicroServices initialization code NO_FEATURE_INFO # do not create a feature info by calling add_feature_info() WARNINGS_AS_ERRORS # treat all compiler warnings as errors EXECUTABLE # create an executable; do not use directly, use mitk_create_executable() instead ) MACRO_PARSE_ARGUMENTS(MODULE "${_macro_params}" "${_macro_options}" ${ARGN}) set(MODULE_NAME ${MODULE_DEFAULT_ARGS}) if(NOT MODULE_NAME) if(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME) get_filename_component(MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) else() message(SEND_ERROR "The module name must not be empty") endif() endif() set(_module_type module) set(_Module_type Module) if(MODULE_EXECUTABLE) set(_module_type executable) set(_Module_type Executable) endif() if(MITK_MODULE_NAME_REGEX_MATCH) if(NOT ${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_MATCH}) message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" does not match the regular expression \"${MITK_MODULE_NAME_REGEX_MATCH}\".") endif() endif() if(MITK_MODULE_NAME_REGEX_NOT_MATCH) if(${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_NOT_MATCH}) message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" must not match the regular expression \"${MITK_MODULE_NAME_REGEX_NOT_MATCH}\".") endif() endif() if(MITK_MODULE_NAME_PREFIX AND NOT MODULE_NAME MATCHES "^${MITK_MODULE_NAME_PREFIX}.*$") set(MODULE_NAME "${MITK_MODULE_NAME_PREFIX}${MODULE_NAME}") endif() if(NOT MODULE_FILES_CMAKE) set(MODULE_FILES_CMAKE files.cmake) endif() if(NOT IS_ABSOLUTE ${MODULE_FILES_CMAKE}) set(MODULE_FILES_CMAKE ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_FILES_CMAKE}) endif() if (MODULE_QT_MODULE) message(WARNING "QT_MODULE keyword is deprecated (in ${_module_type} ${MODULE_NAME}). Please use PACKAGE_DEPENDS Qt4|QtCore and/or PACKAGE_DEPENDS Qt5|Core instead") if (NOT "${MODULE_PACKAGE_DEPENDS}" MATCHES "^.*Qt4.*$") list(APPEND MODULE_PACKAGE_DEPENDS Qt4|QtGui) endif() endif() if(MODULE_HEADERS_ONLY) set(MODULE_TARGET ) if(MODULE_AUTOLOAD_WITH) message(SEND_ERROR "A headers only module cannot be auto-loaded") endif() else() set(MODULE_TARGET ${MODULE_NAME}) endif() if(MODULE_DEPRECATED_SINCE) set(MODULE_IS_DEPRECATED 1) else() set(MODULE_IS_DEPRECATED 0) endif() if(NOT MODULE_SUBPROJECTS) if(MITK_DEFAULT_SUBPROJECTS) set(MODULE_SUBPROJECTS ${MITK_DEFAULT_SUBPROJECTS}) endif() endif() # check if the subprojects exist as targets if(MODULE_SUBPROJECTS) foreach(subproject ${MODULE_SUBPROJECTS}) if(NOT TARGET ${subproject}) message(SEND_ERROR "The subproject ${subproject} does not have a corresponding target") endif() endforeach() endif() # assume worst case set(MODULE_IS_ENABLED 0) # first we check if we have an explicit module build list if(MITK_MODULES_TO_BUILD) list(FIND MITK_MODULES_TO_BUILD ${MODULE_NAME} _MOD_INDEX) if(_MOD_INDEX EQUAL -1) set(MODULE_IS_EXCLUDED 1) endif() endif() if(NOT MODULE_IS_EXCLUDED) # first of all we check for the dependencies _mitk_parse_package_args(${MODULE_PACKAGE_DEPENDS}) mitk_check_module_dependencies(MODULES ${MODULE_DEPENDS} PACKAGES ${PACKAGE_NAMES} MISSING_DEPENDENCIES_VAR _MISSING_DEP PACKAGE_DEPENDENCIES_VAR PACKAGE_NAMES) if(_MISSING_DEP) if(MODULE_NO_FEATURE_INFO) message("${_Module_type} ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}") endif() set(MODULE_IS_ENABLED 0) else() set(MODULE_IS_ENABLED 1) # now check for every package if it is enabled. This overlaps a bit with # MITK_CHECK_MODULE ... foreach(_package ${PACKAGE_NAMES}) if((DEFINED MITK_USE_${_package}) AND NOT (MITK_USE_${_package})) message("${_Module_type} ${MODULE_NAME} won't be built. Turn on MITK_USE_${_package} if you want to use it.") set(MODULE_IS_ENABLED 0) break() endif() endforeach() if(MODULE_IS_ENABLED) # clear variables defined in files.cmake set(RESOURCE_FILES ) set(CPP_FILES ) set(H_FILES ) set(TXX_FILES ) set(DOX_FILES ) set(UI_FILES ) set(MOC_H_FILES ) set(QRC_FILES ) # clear other variables set(Q${KITNAME}_GENERATED_CPP ) set(Q${KITNAME}_GENERATED_MOC_CPP ) set(Q${KITNAME}_GENERATED_QRC_CPP ) set(Q${KITNAME}_GENERATED_UI_CPP ) # check and set-up auto-loading if(MODULE_AUTOLOAD_WITH) if(NOT TARGET "${MODULE_AUTOLOAD_WITH}") message(SEND_ERROR "The module target \"${MODULE_AUTOLOAD_WITH}\" specified as the auto-loading module for \"${MODULE_NAME}\" does not exist") endif() set(_module_autoload_meta_target "${MODULE_AUTOLOAD_WITH}-autoload") # create a meta-target if it does not already exist if(NOT TARGET ${_module_autoload_meta_target}) add_custom_target(${_module_autoload_meta_target}) endif() endif() # Convert relative include dirs to absolute dirs set(_include_dirs . ${MODULE_INCLUDE_DIRS}) set(MODULE_INCLUDE_DIRS) foreach(dir ${_include_dirs}) get_filename_component(_abs_dir ${dir} ABSOLUTE) list(APPEND MODULE_INCLUDE_DIRS ${_abs_dir}) endforeach() list(APPEND MODULE_INCLUDE_DIRS ${MITK_BINARY_DIR} ${MODULES_CONF_DIRS}) # Convert relative internal include dirs to absolute dirs set(_include_dirs ${MODULE_INTERNAL_INCLUDE_DIRS}) set(MODULE_INTERNAL_INCLUDE_DIRS) foreach(dir ${_include_dirs}) get_filename_component(_abs_dir ${dir} ABSOLUTE) list(APPEND MODULE_INTERNAL_INCLUDE_DIRS ${_abs_dir}) endforeach() # Qt generates headers in the binary tree list(APPEND MODULE_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) # Add the module specific include dirs include_directories(${MODULE_INCLUDE_DIRS} ${MODULE_INTERNAL_INCLUDE_DIRS}) if(NOT MODULE_EXECUTABLE) _MITK_CREATE_MODULE_CONF() endif() if(NOT MODULE_EXPORT_DEFINE) set(MODULE_EXPORT_DEFINE ${MODULE_NAME}_EXPORT) endif(NOT MODULE_EXPORT_DEFINE) if(MITK_GENERATE_MODULE_DOT) message("MODULEDOTNAME ${MODULE_NAME}") foreach(dep ${MODULE_DEPENDS}) message("MODULEDOT \"${MODULE_NAME}\" -> \"${dep}\" ; ") endforeach(dep) endif(MITK_GENERATE_MODULE_DOT) # ok, now create the module itself include(${MODULE_FILES_CMAKE}) set(module_c_flags ) set(module_c_flags_debug ) set(module_c_flags_release ) set(module_cxx_flags ) set(module_cxx_flags_debug ) set(module_cxx_flags_release ) if(MODULE_GCC_DEFAULT_VISIBILITY) set(use_visibility_flags 0) else() # We only support hidden visibility for gcc for now. Clang 3.0 still has troubles with # correctly marking template declarations and explicit template instantiations as exported. # See http://comments.gmane.org/gmane.comp.compilers.clang.scm/50028 # and http://llvm.org/bugs/show_bug.cgi?id=10113 if(CMAKE_COMPILER_IS_GNUCXX) set(use_visibility_flags 1) else() # set(use_visibility_flags 0) endif() endif() if(CMAKE_COMPILER_IS_GNUCXX) # MinGW does not export all symbols automatically, so no need to set flags. # # With gcc < 4.5, RTTI symbols from classes declared in third-party libraries # which are not "gcc visibility aware" are marked with hidden visibility in # DSOs which include the class declaration and which are compiled with # hidden visibility. This leads to dynamic_cast and exception handling problems. # While this problem could be worked around by sandwiching the include # directives for the third-party headers between "#pragma visibility push/pop" # statements, it is generally safer to just use default visibility with # gcc < 4.5. if(${GCC_VERSION} VERSION_LESS "4.5" OR MINGW) set(use_visibility_flags 0) endif() endif() if(use_visibility_flags) mitkFunctionCheckCAndCXXCompilerFlags("-fvisibility=hidden" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-fvisibility-inlines-hidden" module_c_flags module_cxx_flags) endif() configure_file(${MITK_SOURCE_DIR}/CMake/moduleExports.h.in ${CMAKE_BINARY_DIR}/${MODULES_CONF_DIRNAME}/${MODULE_NAME}Exports.h @ONLY) if(MODULE_WARNINGS_AS_ERRORS) if(MSVC_VERSION) mitkFunctionCheckCAndCXXCompilerFlags("/WX" module_c_flags module_cxx_flags) else() mitkFunctionCheckCAndCXXCompilerFlags("-Werror" module_c_flags module_cxx_flags) # The flag "c++0x-static-nonintegral-init" has been renamed in newer Clang # versions to "static-member-init", see # http://clang-developers.42468.n3.nabble.com/Wc-0x-static-nonintegral-init-gone-td3999651.html # # Also, older Clang and seemingly all gcc versions do not warn if unknown # "-no-*" flags are used, so CMake will happily append any -Wno-* flag to the # command line. This may get confusing if unrelated compiler errors happen and # the error output then additionally contains errors about unknown flags (which # is not the case if there were no compile errors). # # So instead of using -Wno-* we use -Wno-error=*, which will be properly rejected by # the compiler and if applicable, prints the specific warning as a real warning and # not as an error (although -Werror was given). mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=c++0x-static-nonintegral-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=static-member-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unknown-warning" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=gnu" module_c_flags module_cxx_flags) # VNL headers throw a lot of these, not fixable for us at least in ITK 3 mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unused-parameter" module_c_flags module_cxx_flags) # Some DICOM header file in ITK mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=cast-align" module_c_flags module_cxx_flags) endif() endif(MODULE_WARNINGS_AS_ERRORS) if(MODULE_FORCE_STATIC) set(_STATIC STATIC) else() set(_STATIC ) endif(MODULE_FORCE_STATIC) # create a meta-target for auto-loaded modules add_custom_target(${MODULE_NAME}-autoload) if(NOT MODULE_HEADERS_ONLY) if(NOT MODULE_NO_INIT OR RESOURCE_FILES) find_package(CppMicroServices QUIET NO_MODULE REQUIRED) endif() if(NOT MODULE_NO_INIT) usFunctionGenerateModuleInit(CPP_FILES) endif() + + set(binary_res_files ) + set(source_res_files ) if(RESOURCE_FILES) - # Add a source level dependency on resource files - list(APPEND CPP_FILES ${MODULE_TARGET}_resources.cpp) + set(res_dir Resources) + foreach(res_file ${RESOURCE_FILES}) + if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${res_dir}/${res_file}) + list(APPEND binary_res_files "${res_file}") + else() + list(APPEND source_res_files "${res_file}") + endif() + endforeach() + + # Add a source level dependencies on resource files + if(source_res_files) + list(APPEND CPP_FILES ${MODULE_TARGET}_resources.cpp) + endif() + if(binary_res_files) + list(APPEND CPP_FILES ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET}_binary_resources.cpp) + endif() endif() endif() # Qt 4 case if(MITK_USE_Qt4) if(UI_FILES) qt4_wrap_ui(Q${KITNAME}_GENERATED_UI_CPP ${UI_FILES}) endif() if(MOC_H_FILES) qt4_wrap_cpp(Q${KITNAME}_GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) endif() if(QRC_FILES) qt4_add_resources(Q${KITNAME}_GENERATED_QRC_CPP ${QRC_FILES}) endif() endif() # all the same for Qt 5 if(MITK_USE_Qt5) if(UI_FILES) qt5_wrap_ui(Q${KITNAME}_GENERATED_UI_CPP ${UI_FILES}) endif() if(MOC_H_FILES) qt5_wrap_cpp(Q${KITNAME}_GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) endif() if(QRC_FILES) qt5_add_resources(Q${KITNAME}_GENERATED_QRC_CPP ${QRC_FILES}) endif() endif() set(Q${KITNAME}_GENERATED_CPP ${Q${KITNAME}_GENERATED_CPP} ${Q${KITNAME}_GENERATED_UI_CPP} ${Q${KITNAME}_GENERATED_MOC_CPP} ${Q${KITNAME}_GENERATED_QRC_CPP}) ORGANIZE_SOURCES(SOURCE ${CPP_FILES} HEADER ${H_FILES} TXX ${TXX_FILES} DOC ${DOX_FILES} UI ${UI_FILES} QRC ${QRC_FILES} MOC ${Q${KITNAME}_GENERATED_MOC_CPP} GEN_QRC ${Q${KITNAME}_GENERATED_QRC_CPP} GEN_UI ${Q${KITNAME}_GENERATED_UI_CPP}) set(coverage_sources ${CPP_FILES} ${H_FILES} ${GLOBBED__H_FILES} ${CORRESPONDING__H_FILES} ${TXX_FILES} ${TOOL_CPPS} ${TOOL_GUI_CPPS}) if(MODULE_SUBPROJECTS) set_property(SOURCE ${coverage_sources} APPEND PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) endif() if(NOT MODULE_HEADERS_ONLY) # We have to include the MITK__Config.cmake files here because # some external packages do not provide exported targets with an # absolute path to link to. So we need to add link directories *before* # add_library() or add_executable() is called. So far, this is needed only # for GDCM and ACVD. _link_directories_for_packages(${PACKAGE_NAMES}) # Apply properties to the module target. # We cannot use set_target_properties like below since there is no way to # differentiate C/C++ and Releas/Debug flags using target properties. # See http://www.cmake.org/Bug/view.php?id=6493 #set_target_properties(${MODULE_TARGET} PROPERTIES # COMPILE_FLAGS "${module_compile_flags}") # # Strangely, we need to set the variables below in the parent scope # (outside of the function) to be picked up by the target. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${module_c_flags}" PARENT_SCOPE) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${module_c_flags_debug}" PARENT_SCOPE) set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${module_c_flags_release}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${module_cxx_flags}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${module_cxx_flags_debug}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${module_cxx_flags_release}" PARENT_SCOPE) if(MODULE_EXECUTABLE) add_executable(${MODULE_TARGET} ${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP} ${DOX_FILES} ${UI_FILES} ${QRC_FILES}) set(_us_module_name main) else() add_library(${MODULE_TARGET} ${_STATIC} ${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP} ${DOX_FILES} ${UI_FILES} ${QRC_FILES}) set(_us_module_name ${MODULE_TARGET}) endif() set_property(TARGET ${MODULE_TARGET} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=${_us_module_name}) set_property(TARGET ${MODULE_TARGET} PROPERTY US_MODULE_NAME ${_us_module_name}) if(MODULE_TARGET_DEPENDS) add_dependencies(${MODULE_TARGET} ${MODULE_TARGET_DEPENDS}) endif() if(MODULE_SUBPROJECTS) set_property(TARGET ${MODULE_TARGET} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) foreach(subproject ${MODULE_SUBPROJECTS}) add_dependencies(${subproject} ${MODULE_TARGET}) endforeach() endif() set(DEPENDS "${MODULE_DEPENDS}") if(NOT MODULE_NO_INIT) # Add a CppMicroServices dependency implicitly, since it is # needed for the generated "module initialization" code. set(DEPENDS "CppMicroServices;${DEPENDS}") endif() if(DEPENDS OR MODULE_PACKAGE_DEPENDS) mitk_use_modules(TARGET ${MODULE_TARGET} MODULES ${DEPENDS} PACKAGES ${MODULE_PACKAGE_DEPENDS} ) endif() if(MINGW) target_link_libraries(${MODULE_TARGET} ssp) # add stack smash protection lib endif() # Add additional library search directories to a global property which # can be evaluated by other CMake macros, e.g. our install scripts. if(MODULE_ADDITIONAL_LIBS) target_link_libraries(${MODULE_TARGET} ${MODULE_ADDITIONAL_LIBS}) get_property(_mitk_additional_library_search_paths GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) foreach(_lib_filepath ${MODULE_ADDITIONAL_LIBS}) get_filename_component(_search_path "${_lib_filepath}" PATH) if(_search_path) list(APPEND _mitk_additional_library_search_paths "${_search_path}") endif() endforeach() if(_mitk_additional_library_search_paths) list(REMOVE_DUPLICATES _mitk_additional_library_search_paths) set_property(GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS ${_mitk_additional_library_search_paths}) endif() endif() # add the target name to a global property which is used in the top-level # CMakeLists.txt file to export the target set_property(GLOBAL APPEND PROPERTY MITK_MODULE_TARGETS ${MODULE_TARGET}) if(MODULE_AUTOLOAD_WITH) # for auto-loaded modules, adapt the output directory add_dependencies(${_module_autoload_meta_target} ${MODULE_TARGET}) if(WIN32) set(_module_output_prop RUNTIME_OUTPUT_DIRECTORY) else() set(_module_output_prop LIBRARY_OUTPUT_DIRECTORY) endif() set(_module_output_dir ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) get_target_property(_module_is_imported ${MODULE_AUTOLOAD_WITH} IMPORTED) if(NOT _module_is_imported) # if the auto-loading module is not imported, get its location # and put the auto-load module relative to it. get_target_property(_module_output_dir ${MODULE_AUTOLOAD_WITH} ${_module_output_prop}) set_target_properties(${MODULE_TARGET} PROPERTIES ${_module_output_prop} ${_module_output_dir}/${MODULE_AUTOLOAD_WITH}) else() set_target_properties(${MODULE_TARGET} PROPERTIES ${_module_output_prop} ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) endif() set_target_properties(${MODULE_TARGET} PROPERTIES MITK_AUTOLOAD_DIRECTORY ${MODULE_AUTOLOAD_WITH}) # add the auto-load module name as a property set_property(TARGET ${MODULE_AUTOLOAD_WITH} APPEND PROPERTY MITK_AUTOLOAD_TARGETS ${MODULE_TARGET}) endif() - if(RESOURCE_FILES) - set(res_dir Resources) - set(binary_res_files ) - set(source_res_files ) - foreach(res_file ${RESOURCE_FILES}) - if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${res_dir}/${res_file}) - list(APPEND binary_res_files "${res_file}") - else() - list(APPEND source_res_files "${res_file}") - endif() - endforeach() - - set(res_macro_args ) - if(binary_res_files) - usFunctionAddResources(TARGET ${MODULE_TARGET} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${res_dir} - FILES ${binary_res_files}) - endif() - if(source_res_files) - usFunctionAddResources(TARGET ${MODULE_TARGET} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${res_dir} - FILES ${source_res_files}) - endif() + if(binary_res_files) + usFunctionAddResources(TARGET ${MODULE_TARGET} + SOURCE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET}_binary_resources.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${res_dir} + FILES ${binary_res_files}) + endif() + if(source_res_files) + usFunctionAddResources(TARGET ${MODULE_TARGET} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${res_dir} + FILES ${source_res_files}) endif() endif() endif() endif() endif() if(NOT MODULE_IS_ENABLED AND NOT MODULE_EXECUTABLE) _MITK_CREATE_MODULE_CONF() endif() if(_MISSING_DEP) if(MODULE_DESCRIPTION) set(MODULE_DESCRIPTION "${MODULE_DESCRIPTION} (missing dependencies: ${_MISSING_DEP})") else() set(MODULE_DESCRIPTION "(missing dependencies: ${_MISSING_DEP})") endif() endif() if(NOT MODULE_NO_FEATURE_INFO) add_feature_info(${MODULE_NAME} MODULE_IS_ENABLED "${MODULE_DESCRIPTION}") endif() set(MODULE_NAME ${MODULE_NAME} PARENT_SCOPE) set(MODULE_TARGET ${MODULE_TARGET} PARENT_SCOPE) set(MODULE_IS_ENABLED ${MODULE_IS_ENABLED} PARENT_SCOPE) set(MODULE_SUBPROJECTS ${MODULE_SUBPROJECTS} PARENT_SCOPE) set(ALL_META_DEPENDENCIES ${ALL_META_DEPENDENCIES} PARENT_SCOPE) endfunction() diff --git a/Core/CppMicroServices/cmake/usFunctionAddResources.cmake b/Core/CppMicroServices/cmake/usFunctionAddResources.cmake index b2c7b46784..12717feada 100644 --- a/Core/CppMicroServices/cmake/usFunctionAddResources.cmake +++ b/Core/CppMicroServices/cmake/usFunctionAddResources.cmake @@ -1,136 +1,149 @@ #! \ingroup MicroServicesCMake #! \brief Add resources to a library or executable. #! #! This CMake function uses an external command line program to generate a ZIP archive #! containing data from external resources such as text files or images or other ZIP #! archives. The created archive file is appended as a binary blob to the target file. #! #! Each module can call this function to add resources and make them available at #! runtime through the Module class. Multiple calls to this function append the #! input files to the target file. #! #! \note To set-up correct file dependencies from your module target to your resource #! files, you have to add a file named \e $_resources.cpp -#! to the source list of the target. This ensures that changed resource files -#! will automatically be re-added to the module. +#! to the source list of the target (or the value provided to the SOURCE_OUTPUT +#! parameter). This ensures that changed resource files will automatically be +#! re-added to the module. #! #! In the case of linking static modules which contain resources to the target module, #! adding the static module target name to the ZIP_ARCHIVES list will merge its #! resources into the target module. #! #! Example usage: #! \code{.cmake} #! set(module_srcs ) #! usFunctionAddResources(TARGET mylib #! MODULE_NAME org_me_mylib #! FILES config.properties logo.png #! ) #! \endcode #! #! \param TARGET (required) The target to which the resource files are added. #! \param MODULE_NAME (required/optional) The module name of the target, as specified in #! the \c US_MODULE_NAME pre-processor definition of that target. This parameter #! is optional if a target property with the name US_MODULE_NAME exists, containing #! the required module name. +#! \param SOURCE_OUTPUT (optional) The name for a generated source which can be included in +#! the source list of the TARGET to set-up resource dependencies. If empty, the value +#! defautls to ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_resources.cpp. If a relative path +#! is given, it will be appended to the current binary directory. Setting this parameter +#! to a non-default value for repeated calls of this macro for the same target is +#! recommended. #! \param COMPRESSION_LEVEL (optional) The zip compression level (0-9). Defaults to the default zip #! level. Level 0 disables compression. #! \param WORKING_DIRECTORY (optional) The root path for all resource files listed after the #! FILES argument. If no or a relative path is given, it is considered relativ to the #! current CMake source directory. #! \param FILES (optional) A list of resource files (paths to external files in the file system) #! relative to the current working directory. #! \param ZIP_ARCHIVES (optional) A list of zip archives (relative to the current working directory #! or absolute file paths) whose contents is merged into the target module. If a list entry #! is a valid target name and that target is a static library, its absolute file path is #! used instead. #! function(usFunctionAddResources) - cmake_parse_arguments(US_RESOURCE "" "TARGET;MODULE_NAME;WORKING_DIRECTORY;COMPRESSION_LEVEL" "FILES;ZIP_ARCHIVES" ${ARGN}) + cmake_parse_arguments(US_RESOURCE "" "TARGET;MODULE_NAME;SOURCE_OUTPUT;WORKING_DIRECTORY;COMPRESSION_LEVEL" "FILES;ZIP_ARCHIVES" ${ARGN}) if(NOT US_RESOURCE_TARGET) message(SEND_ERROR "TARGET argument not specified.") endif() if(NOT US_RESOURCE_MODULE_NAME) get_target_property(US_RESOURCE_MODULE_NAME ${US_RESOURCE_TARGET} US_MODULE_NAME) if(NOT US_RESOURCE_MODULE_NAME) message(SEND_ERROR "Either the MODULE_NAME argument or the US_MODULE_NAME target property is required.") endif() endif() + if(NOT US_RESOURCE_SOURCE_OUTPUT) + set(US_RESOURCE_SOURCE_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${US_RESOURCE_TARGET}_resources.cpp") + elseif(NOT IS_ABSOLUTE ${US_RESOURCE_SOURCE_OUTPUT}) + set(US_RESOURCE_SOURCE_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${US_RESOURCE_SOURCE_OUTPUT}") + endif() + if(NOT US_RESOURCE_FILES AND NOT US_RESOURCE_ZIP_ARCHIVES) message(WARNING "No resources specified. Skipping resource processing.") return() endif() if(NOT US_RESOURCE_WORKING_DIRECTORY) set(US_RESOURCE_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() if(NOT IS_ABSOLUTE ${US_RESOURCE_WORKING_DIRECTORY}) set(US_RESOURCE_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${US_RESOURCE_WORKING_DIRECTORY}") endif() if(US_RESOURCE_COMPRESSION_LEVEL) set(cmd_line_args -${US_RESOURCE_COMPRESSION_LEVEL}) endif() set(resource_compiler ${US_RCC_EXECUTABLE}) if(TARGET ${US_RCC_EXECUTABLE_NAME}) set(resource_compiler ${US_RCC_EXECUTABLE_NAME}) elseif(NOT resource_compiler) message(FATAL_ERROR "The CppMicroServices resource compiler was not found. Check the US_RCC_EXECUTABLE CMake variable.") endif() set(_cmd_deps ) foreach(_file ${US_RESOURCE_FILES}) if(IS_ABSOLUTE ${_file}) list(APPEND _cmd_deps ${_file}) else() list(APPEND _cmd_deps ${US_RESOURCE_WORKING_DIRECTORY}/${_file}) endif() endforeach() set(_zip_args ) if(US_RESOURCE_ZIP_ARCHIVES) foreach(_zip_archive ${US_RESOURCE_ZIP_ARCHIVES}) if(TARGET ${_zip_archive}) get_target_property(_is_static_lib ${_zip_archive} TYPE) if(_is_static_lib STREQUAL "STATIC_LIBRARY") list(APPEND _cmd_deps ${_zip_archive}) list(APPEND _zip_args $) endif() else() if(IS_ABSOLUTE ${_zip_archive}) list(APPEND _cmd_deps ${_zip_archive}) else() list(APPEND _cmd_deps ${US_RESOURCE_WORKING_DIRECTORY}/${_zip_archive}) endif() list(APPEND _zip_args ${_zip_archive}) endif() endforeach() endif() # This command depends on the given resource files and creates a source # file which must be added to the source list of the related target. # This way, the following command is executed if the resources change # and it just touches the created source file to force a (actually unnecessary) # re-linking and hence the execution of POST_BUILD commands. add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${US_RESOURCE_TARGET}_resources.cpp - COMMAND ${CMAKE_COMMAND} -E copy ${US_CMAKE_RESOURCE_DEPENDENCIES_CPP} ${CMAKE_CURRENT_BINARY_DIR}/${US_RESOURCE_TARGET}_resources.cpp + OUTPUT ${US_RESOURCE_SOURCE_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E copy ${US_CMAKE_RESOURCE_DEPENDENCIES_CPP} ${US_RESOURCE_SOURCE_OUTPUT} DEPENDS ${_cmd_deps} ${resource_compiler} COMMENT "Checking resource dependencies for ${US_RESOURCE_TARGET}" VERBATIM ) add_custom_command( TARGET ${US_RESOURCE_TARGET} POST_BUILD COMMAND ${resource_compiler} ${cmd_line_args} $ ${US_RESOURCE_MODULE_NAME} -a ${US_RESOURCE_FILES} -m ${_zip_args} WORKING_DIRECTORY ${US_RESOURCE_WORKING_DIRECTORY} COMMENT "Adding resources to ${US_RESOURCE_TARGET}" VERBATIM ) endfunction() diff --git a/Core/CppMicroServices/core/test/modules/libRWithResources/CMakeLists.txt b/Core/CppMicroServices/core/test/modules/libRWithResources/CMakeLists.txt index 9d25945705..23f5a6b972 100644 --- a/Core/CppMicroServices/core/test/modules/libRWithResources/CMakeLists.txt +++ b/Core/CppMicroServices/core/test/modules/libRWithResources/CMakeLists.txt @@ -1,22 +1,23 @@ set(resource_files icons/compressable.bmp icons/cppmicroservices.png icons/readme.txt foo.txt special_chars.dummy.txt test.xml ) -configure_file(resources/foo.txt ${CMAKE_CURRENT_BINARY_DIR}/foo2.txt @COPYONLY) +configure_file(resources/foo.txt ${CMAKE_CURRENT_BINARY_DIR}/resources/foo2.txt @COPYONLY) -#usFunctionCreateTestModuleWithResources(TestModuleR -# SOURCES usTestModuleR.cpp -# RESOURCES ${resource_files}) +usFunctionCreateTestModuleWithResources(TestModuleR + SOURCES ${CMAKE_CURRENT_BINARY_DIR}/TestModuleR_binary_resources.cpp + RESOURCES ${resource_files} +) -usFunctionCreateTestModule(TestModuleR) usFunctionAddResources( TARGET TestModuleR - WORKING_DIRECTORY resources - FILES ${resource_files} - ) + SOURCE_OUTPUT TestModuleR_binary_resources.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/resources + FILES foo2.txt +) diff --git a/Core/CppMicroServices/core/test/usModuleResourceTest.cpp b/Core/CppMicroServices/core/test/usModuleResourceTest.cpp index d6a7a94c67..507a1e5348 100644 --- a/Core/CppMicroServices/core/test/usModuleResourceTest.cpp +++ b/Core/CppMicroServices/core/test/usModuleResourceTest.cpp @@ -1,452 +1,455 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include #include "usTestingMacros.h" #include #include US_USE_NAMESPACE namespace { // Please confirm that a character count differing from the following targets is not due to // a misconfiguration of your versioning software (Correct line endings for your system) // See issue #18 ( https://github.com/saschazelzer/CppMicroServices/issues/18 ) void checkResourceInfo(const ModuleResource& res, const std::string& path, const std::string& baseName, const std::string& completeBaseName, const std::string& suffix, const std::string& completeSuffix, int size, bool children = false) { US_TEST_CONDITION_REQUIRED(res.IsValid(), "Valid resource") US_TEST_CONDITION(res.GetBaseName() == baseName, "GetBaseName()") US_TEST_CONDITION(res.GetChildren().empty() == !children, "No children") US_TEST_CONDITION(res.GetCompleteBaseName() == completeBaseName, "GetCompleteBaseName()") US_TEST_CONDITION(res.GetName() == completeBaseName + "." + suffix, "GetName()") US_TEST_CONDITION(res.GetResourcePath() == path + completeBaseName + "." + suffix, "GetResourcePath()") US_TEST_CONDITION(res.GetPath() == path, "GetPath()") US_TEST_CONDITION(res.GetSize() == size, "Data size") US_TEST_CONDITION(res.GetSuffix() == suffix, "Suffix") US_TEST_CONDITION(res.GetCompleteSuffix() == completeSuffix, "Complete suffix") } void testTextResource(Module* module) { ModuleResource res = module->GetResource("foo.txt"); #ifdef US_PLATFORM_WINDOWS checkResourceInfo(res, "/", "foo", "foo", "txt", "txt", 16, false); const std::streampos ssize(13); const std::string fileData = "foo and\nbar\n\n"; #else checkResourceInfo(res, "/", "foo", "foo", "txt", "txt", 13, false); const std::streampos ssize(12); const std::string fileData = "foo and\nbar\n"; #endif ModuleResourceStream rs(res); rs.seekg(0, std::ios::end); US_TEST_CONDITION(rs.tellg() == ssize, "Stream content length"); rs.seekg(0, std::ios::beg); std::string content; content.reserve(res.GetSize()); char buffer[1024]; while (rs.read(buffer, sizeof(buffer))) { content.append(buffer, sizeof(buffer)); } content.append(buffer, static_cast(rs.gcount())); US_TEST_CONDITION(rs.eof(), "EOF check"); US_TEST_CONDITION(content == fileData, "Resource content"); rs.clear(); rs.seekg(0); US_TEST_CONDITION_REQUIRED(rs.tellg() == std::streampos(0), "Move to start") US_TEST_CONDITION_REQUIRED(rs.good(), "Start re-reading"); std::vector lines; std::string line; while (std::getline(rs, line)) { lines.push_back(line); } US_TEST_CONDITION_REQUIRED(lines.size() > 1, "Number of lines") US_TEST_CONDITION(lines[0] == "foo and", "Check first line") US_TEST_CONDITION(lines[1] == "bar", "Check second line") } void testTextResourceAsBinary(Module* module) { ModuleResource res = module->GetResource("foo.txt"); #ifdef US_PLATFORM_WINDOWS checkResourceInfo(res, "/", "foo", "foo", "txt", "txt", 16, false); const std::streampos ssize(16); const std::string fileData = "foo and\r\nbar\r\n\r\n"; #else checkResourceInfo(res, "/", "foo", "foo", "txt", "txt", 13, false); const std::streampos ssize(13); const std::string fileData = "foo and\nbar\n\n"; #endif ModuleResourceStream rs(res, std::ios_base::binary); rs.seekg(0, std::ios::end); US_TEST_CONDITION(rs.tellg() == ssize, "Stream content length"); rs.seekg(0, std::ios::beg); std::string content; content.reserve(res.GetSize()); char buffer[1024]; while (rs.read(buffer, sizeof(buffer))) { content.append(buffer, sizeof(buffer)); } content.append(buffer, static_cast(rs.gcount())); US_TEST_CONDITION(rs.eof(), "EOF check"); US_TEST_CONDITION(content == fileData, "Resource content"); } void testInvalidResource(Module* module) { ModuleResource res = module->GetResource("invalid"); US_TEST_CONDITION_REQUIRED(res.IsValid() == false, "Check invalid resource") US_TEST_CONDITION(res.GetName().empty(), "Check empty name") US_TEST_CONDITION(res.GetPath().empty(), "Check empty path") US_TEST_CONDITION(res.GetResourcePath().empty(), "Check empty resource path") US_TEST_CONDITION(res.GetBaseName().empty(), "Check empty base name") US_TEST_CONDITION(res.GetCompleteBaseName().empty(), "Check empty complete base name") US_TEST_CONDITION(res.GetSuffix().empty(), "Check empty suffix") US_TEST_CONDITION(res.GetChildren().empty(), "Check empty children") US_TEST_CONDITION(res.GetSize() == 0, "Check zero size") ModuleResourceStream rs(res); US_TEST_CONDITION(rs.good() == true, "Check invalid resource stream") rs.ignore(); US_TEST_CONDITION(rs.good() == false, "Check invalid resource stream") US_TEST_CONDITION(rs.eof() == true, "Check invalid resource stream") } void testSpecialCharacters(Module* module) { ModuleResource res = module->GetResource("special_chars.dummy.txt"); #ifdef US_PLATFORM_WINDOWS checkResourceInfo(res, "/", "special_chars", "special_chars.dummy", "txt", "dummy.txt", 56, false); const std::streampos ssize(54); const std::string fileData = "German Füße (feet)\nFrench garçon de café (waiter)\n"; #else checkResourceInfo(res, "/", "special_chars", "special_chars.dummy", "txt", "dummy.txt", 54, false); const std::streampos ssize(53); const std::string fileData = "German Füße (feet)\nFrench garçon de café (waiter)"; #endif ModuleResourceStream rs(res); rs.seekg(0, std::ios_base::end); US_TEST_CONDITION(rs.tellg() == ssize, "Stream content length"); rs.seekg(0, std::ios_base::beg); std::string content; content.reserve(res.GetSize()); char buffer[1024]; while (rs.read(buffer, sizeof(buffer))) { content.append(buffer, sizeof(buffer)); } content.append(buffer, static_cast(rs.gcount())); US_TEST_CONDITION(rs.eof(), "EOF check"); US_TEST_CONDITION(content == fileData, "Resource content"); } void testBinaryResource(Module* module) { ModuleResource res = module->GetResource("/icons/cppmicroservices.png"); checkResourceInfo(res, "/icons/", "cppmicroservices", "cppmicroservices", "png", "png", 2424, false); ModuleResourceStream rs(res, std::ios_base::binary); rs.seekg(0, std::ios_base::end); std::streampos resLength = rs.tellg(); rs.seekg(0); std::ifstream png(US_CORE_SOURCE_DIR "/test/modules/libRWithResources/resources/icons/cppmicroservices.png", std::ifstream::in | std::ifstream::binary); US_TEST_CONDITION_REQUIRED(png.is_open(), "Open reference file") png.seekg(0, std::ios_base::end); std::streampos pngLength = png.tellg(); png.seekg(0); US_TEST_CONDITION(res.GetSize() == resLength, "Check resource size") US_TEST_CONDITION_REQUIRED(resLength == pngLength, "Compare sizes") char c1 = 0; char c2 = 0; bool isEqual = true; int count = 0; while (png.get(c1) && rs.get(c2)) { ++count; if (c1 != c2) { isEqual = false; break; } } US_TEST_CONDITION_REQUIRED(count == pngLength, "Check if everything was read"); US_TEST_CONDITION_REQUIRED(isEqual, "Equal binary contents"); US_TEST_CONDITION(png.eof(), "EOF check"); } void testCompressedResource(Module* module) { ModuleResource res = module->GetResource("/icons/compressable.bmp"); checkResourceInfo(res, "/icons/", "compressable", "compressable", "bmp", "bmp", 300122, false); ModuleResourceStream rs(res, std::ios_base::binary); rs.seekg(0, std::ios_base::end); std::streampos resLength = rs.tellg(); rs.seekg(0); std::ifstream bmp(US_CORE_SOURCE_DIR "/test/modules/libRWithResources/resources/icons/compressable.bmp", std::ifstream::in | std::ifstream::binary); US_TEST_CONDITION_REQUIRED(bmp.is_open(), "Open reference file") bmp.seekg(0, std::ios_base::end); std::streampos bmpLength = bmp.tellg(); bmp.seekg(0); US_TEST_CONDITION(300122 == resLength, "Check resource size") US_TEST_CONDITION_REQUIRED(resLength == bmpLength, "Compare sizes") char c1 = 0; char c2 = 0; bool isEqual = true; int count = 0; while (bmp.get(c1) && rs.get(c2)) { ++count; if (c1 != c2) { isEqual = false; break; } } US_TEST_CONDITION_REQUIRED(count == bmpLength, "Check if everything was read"); US_TEST_CONDITION_REQUIRED(isEqual, "Equal binary contents"); US_TEST_CONDITION(bmp.eof(), "EOF check"); } struct ResourceComparator { bool operator()(const ModuleResource& mr1, const ModuleResource& mr2) const { return mr1 < mr2; } }; void testResourceTree(Module* module) { ModuleResource res = module->GetResource(""); US_TEST_CONDITION(res.GetResourcePath() == "/", "Check root file path") US_TEST_CONDITION(res.IsDir() == true, "Check type") std::vector children = res.GetChildren(); std::sort(children.begin(), children.end()); - US_TEST_CONDITION_REQUIRED(children.size() == 4, "Check child count") + US_TEST_CONDITION_REQUIRED(children.size() == 5, "Check child count") US_TEST_CONDITION(children[0] == "foo.txt", "Check child name") - US_TEST_CONDITION(children[1] == "icons/", "Check child name") - US_TEST_CONDITION(children[2] == "special_chars.dummy.txt", "Check child name") - US_TEST_CONDITION(children[3] == "test.xml", "Check child name") + US_TEST_CONDITION(children[1] == "foo2.txt", "Check child name") + US_TEST_CONDITION(children[2] == "icons/", "Check child name") + US_TEST_CONDITION(children[3] == "special_chars.dummy.txt", "Check child name") + US_TEST_CONDITION(children[4] == "test.xml", "Check child name") US_TEST_CONDITION(module->FindResources("!$noexist=?", std::string(), "true").empty(), "Check not existant path"); ModuleResource readme = module->GetResource("/icons/readme.txt"); US_TEST_CONDITION(readme.IsFile() && readme.GetChildren().empty(), "Check file resource") ModuleResource icons = module->GetResource("icons/"); US_TEST_CONDITION(icons.IsDir() && !icons.IsFile() && !icons.GetChildren().empty(), "Check directory resource") children = icons.GetChildren(); US_TEST_CONDITION_REQUIRED(children.size() == 3, "Check icons child count") std::sort(children.begin(), children.end()); US_TEST_CONDITION(children[0] == "compressable.bmp", "Check child name") US_TEST_CONDITION(children[1] == "cppmicroservices.png", "Check child name") US_TEST_CONDITION(children[2] == "readme.txt", "Check child name") ResourceComparator resourceComparator; // find all .txt files std::vector nodes = module->FindResources("", "*.txt", false); std::sort(nodes.begin(), nodes.end(), resourceComparator); - US_TEST_CONDITION_REQUIRED(nodes.size() == 2, "Found child count") + US_TEST_CONDITION_REQUIRED(nodes.size() == 3, "Found child count") US_TEST_CONDITION(nodes[0].GetResourcePath() == "/foo.txt", "Check child name") - US_TEST_CONDITION(nodes[1].GetResourcePath() == "/special_chars.dummy.txt", "Check child name") + US_TEST_CONDITION(nodes[1].GetResourcePath() == "/foo2.txt", "Check child name") + US_TEST_CONDITION(nodes[2].GetResourcePath() == "/special_chars.dummy.txt", "Check child name") nodes = module->FindResources("", "*.txt", true); std::sort(nodes.begin(), nodes.end(), resourceComparator); - US_TEST_CONDITION_REQUIRED(nodes.size() == 3, "Found child count") + US_TEST_CONDITION_REQUIRED(nodes.size() == 4, "Found child count") US_TEST_CONDITION(nodes[0].GetResourcePath() == "/foo.txt", "Check child name") - US_TEST_CONDITION(nodes[1].GetResourcePath() == "/icons/readme.txt", "Check child name") - US_TEST_CONDITION(nodes[2].GetResourcePath() == "/special_chars.dummy.txt", "Check child name") + US_TEST_CONDITION(nodes[1].GetResourcePath() == "/foo2.txt", "Check child name") + US_TEST_CONDITION(nodes[2].GetResourcePath() == "/icons/readme.txt", "Check child name") + US_TEST_CONDITION(nodes[3].GetResourcePath() == "/special_chars.dummy.txt", "Check child name") // find all resources nodes = module->FindResources("", "", true); - US_TEST_CONDITION(nodes.size() == 7, "Total resource number") + US_TEST_CONDITION(nodes.size() == 8, "Total resource number") nodes = module->FindResources("", "**", true); - US_TEST_CONDITION(nodes.size() == 7, "Total resource number") + US_TEST_CONDITION(nodes.size() == 8, "Total resource number") // test pattern matching nodes.clear(); nodes = module->FindResources("/icons", "*micro*.png", false); US_TEST_CONDITION(nodes.size() == 1 && nodes[0].GetResourcePath() == "/icons/cppmicroservices.png", "Check file pattern matches") nodes.clear(); nodes = module->FindResources("", "*.txt", true); - US_TEST_CONDITION(nodes.size() == 3, "Check recursive pattern matches") + US_TEST_CONDITION(nodes.size() == 4, "Check recursive pattern matches") } void testResourceOperators(Module* module) { ModuleResource invalid = module->GetResource("invalid"); ModuleResource foo = module->GetResource("foo.txt"); US_TEST_CONDITION_REQUIRED(foo.IsValid() && foo, "Check valid resource") ModuleResource foo2(foo); US_TEST_CONDITION(foo == foo, "Check equality operator") US_TEST_CONDITION(foo == foo2, "Check copy constructor and equality operator") US_TEST_CONDITION(foo != invalid, "Check inequality with invalid resource") ModuleResource xml = module->GetResource("/test.xml"); US_TEST_CONDITION_REQUIRED(xml.IsValid() && xml, "Check valid resource") US_TEST_CONDITION(foo != xml, "Check inequality") US_TEST_CONDITION(foo < xml, "Check operator<") // check operator< by using a set std::set resources; resources.insert(foo); resources.insert(foo); resources.insert(xml); US_TEST_CONDITION(resources.size() == 2, "Check operator< with set") // check hash function specialization US_UNORDERED_SET_TYPE resources2; resources2.insert(foo); resources2.insert(foo); resources2.insert(xml); US_TEST_CONDITION(resources2.size() == 2, "Check operator< with unordered set") // check operator<< std::ostringstream oss; oss << foo; US_TEST_CONDITION(oss.str() == foo.GetResourcePath(), "Check operator<<") } void testResourceFromExecutable(Module* module) { ModuleResource resource = module->GetResource("usTestResource.txt"); US_TEST_CONDITION_REQUIRED(resource.IsValid(), "Check valid executable resource") std::string line; ModuleResourceStream rs(resource); std::getline(rs, line); US_TEST_CONDITION(line == "meant to be compiled into the test driver", "Check executable resource content") } } // end unnamed namespace int usModuleResourceTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ModuleResourceTest"); ModuleContext* mc = GetModuleContext(); assert(mc); #ifdef US_BUILD_SHARED_LIBS #ifdef US_PLATFORM_WINDOWS const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif SharedLibrary libR(LIB_PATH, "TestModuleR"); try { libR.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } #endif Module* moduleR = ModuleRegistry::GetModule("TestModuleR"); US_TEST_CONDITION_REQUIRED(moduleR != NULL, "Test for existing module TestModuleR") US_TEST_CONDITION(moduleR->GetName() == "TestModuleR", "Test module name") testInvalidResource(moduleR); testResourceFromExecutable(mc->GetModule()); testResourceTree(moduleR); testResourceOperators(moduleR); testTextResource(moduleR); testTextResourceAsBinary(moduleR); testSpecialCharacters(moduleR); testBinaryResource(moduleR); testCompressedResource(moduleR); ModuleResource foo = moduleR->GetResource("foo.txt"); US_TEST_CONDITION(foo.IsValid() == true, "Valid resource") #ifdef US_BUILD_SHARED_LIBS libR.Unload(); US_TEST_CONDITION(foo.IsValid() == true, "Still valid resource") #endif US_TEST_END() } diff --git a/Core/CppMicroServices/tools/usResourceCompiler.c b/Core/CppMicroServices/tools/usResourceCompiler.c index 83487fc933..60268099df 100644 --- a/Core/CppMicroServices/tools/usResourceCompiler.c +++ b/Core/CppMicroServices/tools/usResourceCompiler.c @@ -1,783 +1,806 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "miniz.h" #include #include #include #include #define US_STR_(x) #x #define US_STR(x) US_STR_(x) static int cleanup_archives(mz_zip_archive* writeArchive, mz_zip_archive* readArchive) { if (writeArchive && writeArchive->m_zip_mode != MZ_ZIP_MODE_INVALID) { if (writeArchive->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { if (!mz_zip_writer_finalize_archive(writeArchive)) { return -1; } } if (writeArchive->m_zip_mode != MZ_ZIP_MODE_INVALID) { if (!mz_zip_writer_end(writeArchive)) { return -1; } } } if (readArchive && readArchive->m_zip_mode != MZ_ZIP_MODE_INVALID) { if (!mz_zip_reader_end(readArchive)) { return -1; } } return 0; } static void exit_printf(mz_zip_archive* writeArchive, mz_zip_archive* readArchive, const char* format, ...) { va_list args; cleanup_archives(writeArchive, readArchive); fprintf(stderr, "error: "); va_start(args, format); vfprintf(stderr, format, args); va_end(args); exit(EXIT_FAILURE); } static void exit_perror(mz_zip_archive* writeArchive, mz_zip_archive* readArchive, const char* desc) { cleanup_archives(writeArchive, readArchive); fprintf(stderr, "error: "); perror(desc); exit(EXIT_FAILURE); } // --------------------------------------------------------------------------------- // -------------------------- PLATFORM SPECIFIC CODE ------------------------- // --------------------------------------------------------------------------------- #if defined(_WIN32) || defined(_WIN64) #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN #include #include #include #include #include #include static char* get_error_str() { // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); return lpMsgBuf; } static void free_error_str(char* buf) { LocalFree(buf); } static FILE* us_create_tmp_file() { TCHAR tmpPath[MAX_PATH]; TCHAR tmpFileName[MAX_PATH]; HANDLE hTmpFile = INVALID_HANDLE_VALUE; FILE* tmpFile = NULL; int tmpfd = 0; UINT retVal = 0; retVal = GetTempPath(MAX_PATH, tmpPath); if (retVal > MAX_PATH || retVal == 0) { exit_printf(NULL, NULL, get_error_str()); } retVal = GetTempFileName(tmpPath, TEXT("us_"), 0, tmpFileName); if (retVal == 0) { exit_printf(NULL, NULL, get_error_str()); } if (_sopen_s(&tmpfd, tmpFileName, _O_CREAT | _O_TEMPORARY | _O_RDWR | _O_BINARY, _SH_DENYRW, _S_IREAD | _S_IWRITE)) { exit_printf(NULL, NULL, get_error_str()); } tmpFile = _fdopen(tmpfd, "r+b"); if (tmpFile == NULL) { exit_printf(NULL, NULL, get_error_str()); } return tmpFile; } static void free_tmp_path(char* buf) { free(buf); } static char* us_strcpy(char* dest, size_t dest_size, const char* src) { if (strcpy_s(dest, dest_size, src)) { char* err_str = get_error_str(); exit_printf(NULL, NULL, err_str); } return dest; } static char* us_strncpy(char* dest, size_t dest_size, const char* src, size_t count) { if (strncpy_s(dest, dest_size, src, count)) { char* err_str = get_error_str(); exit_printf(NULL, NULL, err_str); } return dest; } static FILE* us_fopen(const char* filename, const char* mode) { FILE* file = NULL; fopen_s(&file, filename, mode); return file; } #define US_CWD(b, s) _getcwd(b, s) #define US_CLOSE _close #define US_READ _read #define US_FOPEN us_fopen #define US_FTRUNCATE _chsize_s #define US_FILENO _fileno #define US_STRCASECMP _stricmp #define US_STRCPY us_strcpy #define US_STRNCPY us_strncpy #define US_SSCANF sscanf_s #else #include static char* get_error_str() { return strerror(errno); } static void free_error_str(char* buf) { } static FILE* us_create_tmp_file() { FILE* file = tmpfile(); if (file == NULL) { exit_printf(NULL, NULL, get_error_str()); } return file; } static void free_tmp_path(char* buf) { } static char* us_strcpy(char* dest, size_t dest_size, const char* src) { return strcpy(dest, src); } static char* us_strncpy(char* dest, size_t dest_size, const char* src, size_t count) { return strncpy(dest, src, count); } #define US_CWD(b, s) getcwd(b, s) #define US_CLOSE close #define US_READ read #define US_FOPEN fopen #define US_FTRUNCATE ftruncate #define US_FILENO fileno #define US_STRCASECMP strcasecmp #define US_STRCPY us_strcpy #define US_STRNCPY us_strncpy #define US_SSCANF sscanf #endif // --------------------------------------------------------------------------------- // ----------------------------- DEBUGGING STUFF ----------------------------- // --------------------------------------------------------------------------------- //#define DEBUG_TRACE #ifdef DEBUG_TRACE #define dbg_print printf #else static int void_printf(const char* format, ...) { return 0; } #define dbg_print void_printf #endif // --------------------------------------------------------------------------------- // ----------------------------- HELPER FUNCTIONS ---------------------------- // --------------------------------------------------------------------------------- void* malloc_or_abort(size_t size) { void* p; if (size == 0) size = 1; p = malloc(size); if (!p) { // try to print an error message; this might very well fail fprintf(stderr, "Could not allocate enough memory (%ld bytes)\n", size); abort(); } return p; } static int cmpzipindex(const void *i1, const void *i2) { mz_uint index1 = *(const mz_uint*)i1; mz_uint index2 = *(const mz_uint*)i2; return index1 == index2 ? 0 : (index1 < index2 ? -1 : 1); } static int cmpstringp(const void *p1, const void *p2) { return US_STRCASECMP(* (char * const *) p1, * (char * const *) p2); } typedef struct us_archived_names_tag { char** names; mz_uint size; mz_uint capacity; mz_uint orderedSize; } us_archived_names; static void us_archived_names_free(us_archived_names * archivedNames) { mz_uint i; for (i = 0; i < archivedNames->size; ++i) { free(archivedNames->names[i]); } free(archivedNames->names); } enum { US_OK = 0, US_ARCHIVED_NAMES_ERROR_DUPLICATE = 1, US_MZ_ERROR_ADD_FILE = 2, US_ERROR_INVALID = 3 }; // messages can take two arguments: // 1. The archive entry name // 2. The path of the zip archive const char* us_error_msg[] = { "ok\n", "Duplicate entry '%s' (in %s)\n", "Could not add resource %s\n" }; static int us_archived_names_append(us_archived_names* archivedNames, const char* archiveName) { if (archivedNames->names != NULL && bsearch(&archiveName, archivedNames->names, archivedNames->orderedSize, sizeof(char*), cmpstringp) != NULL) { return US_ARCHIVED_NAMES_ERROR_DUPLICATE; } if (archivedNames->size >= archivedNames->capacity) { size_t newCapacity = archivedNames->size > archivedNames->capacity + 100 ? archivedNames->size + 1 : archivedNames->capacity + 100; archivedNames->names = realloc(archivedNames->names, newCapacity * sizeof(char*)); if (archivedNames->names == NULL) { fprintf(stderr, "Could not realloc enough memory (%ld bytes)\n", newCapacity); abort(); } memset(archivedNames->names + archivedNames->capacity, 0, sizeof(char*) * (newCapacity - archivedNames->capacity)); archivedNames->capacity = (mz_uint)newCapacity; } if (archivedNames->names[archivedNames->size] == NULL) { archivedNames->names[archivedNames->size] = malloc_or_abort(MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE * sizeof(char)); } US_STRCPY(archivedNames->names[archivedNames->size], MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE, archiveName); ++archivedNames->size; return US_OK; } static void us_archived_names_sort(us_archived_names* archivedNames) { qsort(archivedNames->names, archivedNames->size, sizeof(char*), cmpstringp); archivedNames->orderedSize = archivedNames->size; } static int us_zip_writer_add_dir_entries(mz_zip_archive* pZip, const char* pArchive_name, us_archived_names* archived_dirs) { size_t end; size_t length = strlen(pArchive_name); char dirName[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; if (sizeof dirName < length - 1) { // This should be impossible fprintf(stderr, "Archive file name '%s' too long (%ld > %ld)", pArchive_name, length-1, sizeof dirName); exit(EXIT_FAILURE); } // split the archive name into directory tokens for (end = 0; end < length; ++end) { if (pArchive_name[end] == '/') { US_STRNCPY(dirName, sizeof dirName, pArchive_name, end + 1); if (end < length-1) { dirName[end+1] = '\0'; } if (us_archived_names_append(archived_dirs, dirName) == US_OK) { dbg_print("-- found new dir entry %s\n", dirName); // The directory entry does not yet exist, so add it if (!mz_zip_writer_add_mem(pZip, dirName, NULL, 0, MZ_NO_COMPRESSION)) { dbg_print("-- zip add_mem error\n"); return US_MZ_ERROR_ADD_FILE; } us_archived_names_sort(archived_dirs); } } } return US_OK; } static int us_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, us_archived_names* archived_names, us_archived_names* archived_dirs) { int retCode = us_archived_names_append(archived_names, pArchive_name); if (US_OK != retCode) return retCode; if (!mz_zip_writer_add_file(pZip, pArchive_name, pSrc_filename, pComment, comment_size, level_and_flags)) { return US_MZ_ERROR_ADD_FILE; } return us_zip_writer_add_dir_entries(pZip, pArchive_name, archived_dirs); } static int us_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index, us_archived_names* archived_names, us_archived_names* archived_dirs, char* archiveName, mz_uint archiveNameSize) { int retCode = 0; mz_uint numBytes = mz_zip_reader_get_filename(pSource_zip, file_index, archiveName, archiveNameSize); if (numBytes > 1 && archiveName[numBytes-2] != '/') { retCode = us_archived_names_append(archived_names, archiveName); if (US_OK != retCode) return retCode; if (!mz_zip_writer_add_from_zip_reader(pZip, pSource_zip, file_index)) { return US_MZ_ERROR_ADD_FILE; } } return us_zip_writer_add_dir_entries(pZip, archiveName, archived_dirs); } // --------------------------------------------------------------------------------- // ----------------------------- MAIN ENTRY POINT ---------------------------- // --------------------------------------------------------------------------------- int main(int argc, char** argv) { int compressionLevel = 6; int argIndex = 0; int bPrintHelp = 0; int errCode = US_OK; int mergeFlag = 0; const char* moduleFile = NULL; const char* moduleName = NULL; size_t moduleNameLength = 0; char currDir[1024]; FILE* moduleFileStream = NULL; mz_zip_archive writeArchive; mz_zip_archive readArchive; us_archived_names archivedNames; us_archived_names archivedDirs; FILE* tmp_archive_file = NULL; char archiveName[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; int numZipArgs = 0; int* zipArgIndices = NULL; mz_zip_archive currFileArchive; mz_uint oldIndex = 0; mz_uint numOldEntries = 0; int zipArgIndex = 0; char readBuffer[1024]; mz_uint numRead = 0; // --------------------------------------------------------------------------------- // COMMAND LINE VALIDATION // --------------------------------------------------------------------------------- if (argc < 4) { bPrintHelp = 1; } else if (argv[1][0] == '-') { if (strlen(argv[1]) == 2) { if (US_SSCANF(argv[1], "-%1d", &compressionLevel) != 1) { bPrintHelp = 1; } else { argIndex = 1; if (argc < 5 || compressionLevel < 0 || compressionLevel > 9) bPrintHelp = 1; } } else { bPrintHelp = 1; } } if (bPrintHelp) { printf("A resource compiler for C++ Micro Services modules\n\n"); printf("Usage: usResourceCompiler [-#] modulefile modulename [[-a] file...] [-m archive...]\n\n"); printf("Add files to modulefile as zip entries and merge archives.\n\n"); printf(" -# (-0, -1, -2, -3, -4, -5, -6, -7, -8, -9)\n"); printf(" The Zip compression level. The default compression level is -6.\n"); printf(" modulefile The absolute path of the module (shared library).\n"); printf(" modulename The module name as specified in the MODULE_NAME compile definition.\n"); printf(" file Path to a resource file, relative to the current working directory.\n"); printf(" archive Path to a zip archive for merging into modulefile.\n"); exit(EXIT_SUCCESS); } if (NULL == US_CWD(currDir, sizeof(currDir))) { perror(US_STR(US_CWD)); exit(EXIT_FAILURE); } moduleFile = argv[++argIndex]; moduleName = argv[++argIndex]; moduleNameLength = strlen(moduleName); // --------------------------------------------------------------------------------- // OPEN MODULE FILE (shared library) // --------------------------------------------------------------------------------- memset(&writeArchive, 0, sizeof(writeArchive)); memset(&readArchive, 0, sizeof(readArchive)); - // Try to open the module file for appending a new zip archive later - dbg_print("Try open module file '%s' as r+b... ", moduleFile); - if (NULL == (moduleFileStream = US_FOPEN(moduleFile, "r+b"))) - { - dbg_print("failure\n"); - exit_perror(&writeArchive, &readArchive, "fopen"); - } - else - { - dbg_print("success\n"); - } - dbg_print("Check for valid module zip archive... "); memset(&archivedNames, 0, sizeof archivedNames); memset(&archivedDirs, 0, sizeof archivedDirs); // Check if moduleFile is a valid zip archive if (!mz_zip_reader_init_file(&readArchive, moduleFile, 0)) { dbg_print("no\n"); } else { dbg_print("yes\n"); } // --------------------------------------------------------------------------------- // ZIP ARCHIVE WRITING (temporary archive) // --------------------------------------------------------------------------------- // Create a new zip archive which will be appended to the moduleFile later dbg_print("Creating temporary zip archive\n"); tmp_archive_file = us_create_tmp_file(); if (!mz_zip_writer_init_stream(&writeArchive, tmp_archive_file, 0)) { exit_printf(&writeArchive, &readArchive, "Internal error, could not init new zip archive\n"); } dbg_print("Initialized temporary zip archive\n"); // Add current files to the zip archive zipArgIndices = malloc_or_abort(argc * sizeof *zipArgIndices); while(++argIndex < argc) { const char* fileName = argv[argIndex]; const size_t fileNameLength = strlen(fileName); // determine the argument type if (strcmp(argv[argIndex], "-a") == 0) { mergeFlag = 0; continue; } else if (strcmp(argv[argIndex], "-m") == 0) { mergeFlag = 1; continue; } if (mergeFlag) { // check if the current file is a valid zip archive memset(&currFileArchive, 0, sizeof(currFileArchive)); if (mz_zip_reader_init_file(&currFileArchive, fileName, 0)) { dbg_print("Input is a valid zip archive: %s\n", fileName); zipArgIndices[numZipArgs++] = argIndex; mz_zip_reader_end(&currFileArchive); } // silently ignore files which are not zip archives continue; } if (fileNameLength + 1 + moduleNameLength > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1) { exit_printf(&writeArchive, &readArchive, "Resource filename too long: %s\n", moduleName); } US_STRCPY(archiveName, sizeof archiveName, moduleName); archiveName[moduleNameLength] = '/'; US_STRCPY(archiveName + moduleNameLength + 1, (sizeof archiveName) - (moduleNameLength + 1), fileName); // add the current file to the new archive if ((errCode = us_zip_writer_add_file(&writeArchive, archiveName, fileName, NULL, 0, compressionLevel, &archivedNames, &archivedDirs))) { dbg_print("Adding %s failed\n", archiveName); exit_printf(&writeArchive, &readArchive, us_error_msg[errCode], archiveName, fileName); } if (mz_zip_reader_locate_file(&readArchive, archiveName, NULL, 0) > -1) { // we found a duplicate printf("updating: %s\n", archiveName); } else { printf(" adding: %s\n", archiveName); } } us_archived_names_sort(&archivedNames); dbg_print("Added cmd line files to tmp archive\n"); // --------------------------------------------------------------------------------- // MERGE MODULE ZIP ARCHIVE (into temporary archive) // --------------------------------------------------------------------------------- // Add all entries from the old archive oldIndex = 0; numOldEntries = mz_zip_reader_get_num_files(&readArchive); for (; oldIndex < numOldEntries; ++oldIndex) { errCode = us_zip_writer_add_from_zip_reader(&writeArchive, &readArchive, oldIndex, &archivedNames, &archivedDirs, archiveName, (mz_uint) sizeof archiveName); if (errCode == US_ARCHIVED_NAMES_ERROR_DUPLICATE) { // Duplicates in the module archive are not an error, // they were already treated as "updates" continue; } if (errCode) { exit_printf(&writeArchive, &readArchive, us_error_msg[errCode], archiveName, moduleFile); } } us_archived_names_sort(&archivedNames); dbg_print("Merged original entries into tmp archive\n"); // Now merge in the zip archives given on the command line for (zipArgIndex = 0; zipArgIndex < numZipArgs; ++zipArgIndex) { mz_zip_archive currZipArchive; const char* currArchiveFileName = NULL; mz_uint currZipIndex = 0; mz_uint numZipIndices = 0; memset(&currZipArchive, 0, sizeof(mz_zip_archive)); currArchiveFileName = argv[zipArgIndices[zipArgIndex]]; if (!mz_zip_reader_init_file(&currZipArchive, currArchiveFileName, 0)) { exit_printf(&writeArchive, &readArchive, "Could not initialize zip archive %s\n", currArchiveFileName); } numZipIndices = mz_zip_reader_get_num_files(&currZipArchive); for (currZipIndex = 0; currZipIndex < numZipIndices; ++currZipIndex) { printf(" merging: %s (from %s)\n", archiveName, currArchiveFileName); errCode = us_zip_writer_add_from_zip_reader(&writeArchive, &currZipArchive, currZipIndex, &archivedNames, &archivedDirs, archiveName, sizeof archiveName); if (errCode == US_ARCHIVED_NAMES_ERROR_DUPLICATE) { printf(" warning: Merge failed: "); printf(us_error_msg[errCode], archiveName, currArchiveFileName); } else if (errCode != US_OK) { mz_zip_reader_end(&currZipArchive); exit_printf(&writeArchive, &readArchive, us_error_msg[errCode], archiveName, currArchiveFileName); } } mz_zip_reader_end(&currZipArchive); us_archived_names_sort(&archivedNames); } // We are finished, finalize the temporary archive if (!mz_zip_writer_finalize_archive(&writeArchive)) { exit_printf(&writeArchive, &readArchive, "Could not finalize zip archive\n"); } dbg_print("Finalized tmp zip archive\n"); + // Close the module file + mz_zip_reader_end(&readArchive); + // --------------------------------------------------------------------------------- // APPEND NEW ARCHIVE TO MODULE // --------------------------------------------------------------------------------- if (archivedNames.size > 0) { + // Open the module file for appending the temporary zip archive + dbg_print("Opening module file '%s' as r+b... ", moduleFile); + if (NULL == (moduleFileStream = US_FOPEN(moduleFile, "r+b"))) + { + dbg_print("failure\n"); + exit_perror(&writeArchive, &readArchive, "fopen"); + } + else + { + dbg_print("success\n"); + } + // Append the temporary zip archive to the module if (numOldEntries > 0) { // we need to truncate the module file first if (US_FTRUNCATE(US_FILENO(moduleFileStream), readArchive.m_archive_file_ofs)) { exit_perror(&writeArchive, &readArchive, "ftruncate"); } + +#ifdef DEBUG_TRACE + { + long currPos = ftell(moduleFileStream); + fseek(moduleFileStream, 0, SEEK_END); + dbg_print("Truncated module file size: %ld bytes\n", ftell(moduleFileStream)); + fseek(moduleFileStream, currPos, SEEK_SET); + } +#endif + } if (fseek(tmp_archive_file, 0, SEEK_SET) == -1) { exit_perror(&writeArchive, &readArchive, "fseek"); } clearerr(tmp_archive_file); if (fseek(moduleFileStream, 0, SEEK_END) == -1) { exit_perror(&writeArchive, &readArchive, "fseek"); } dbg_print("Appending temporary zip archive to module\n"); do { numRead = US_READ(US_FILENO(tmp_archive_file), readBuffer, sizeof(readBuffer)); if (numRead == -1) { exit_perror(&writeArchive, &readArchive, "read"); } fwrite(readBuffer, numRead, 1, moduleFileStream); if (ferror(moduleFileStream)) { exit_printf(&writeArchive, &readArchive, "Appending resources failed\n"); } } while (numRead != 0); + +#ifdef DEBUG_TRACE + { + long currPos = ftell(moduleFileStream); + fseek(moduleFileStream, 0, SEEK_END); + dbg_print("New module file size: %ld bytes\n", ftell(moduleFileStream)); + fseek(moduleFileStream, currPos, SEEK_SET); + } +#endif + } // --------------------------------------------------------------------------------- // CLEANUP // --------------------------------------------------------------------------------- free(zipArgIndices); us_archived_names_free(&archivedNames); us_archived_names_free(&archivedDirs); if (cleanup_archives(&writeArchive, &readArchive) == -1) { fprintf(stderr, "Internal error finalizing zip archive"); return EXIT_FAILURE; } fclose(moduleFileStream); dbg_print("Successfully added resources\n"); return EXIT_SUCCESS; } diff --git a/Modules/DiffusionImaging/FiberTracking/Testing/CMakeLists.txt b/Modules/DiffusionImaging/FiberTracking/Testing/CMakeLists.txt index a95c0f35f1..f2ad9da614 100644 --- a/Modules/DiffusionImaging/FiberTracking/Testing/CMakeLists.txt +++ b/Modules/DiffusionImaging/FiberTracking/Testing/CMakeLists.txt @@ -1,14 +1,14 @@ MITK_CREATE_MODULE_TESTS() if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") mitkAddCustomModuleTest(mitkFiberBundleXReaderWriterTest mitkFiberBundleXReaderWriterTest ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX.fib) -#mitkAddCustomModuleTest(mitkGibbsTrackingTest mitkGibbsTrackingTest ${MITK_DATA_DIR}/DiffusionImaging/qBallImage.qbi ${MITK_DATA_DIR}/DiffusionImaging/diffusionImageMask.nrrd ${MITK_DATA_DIR}/DiffusionImaging/gibbsTrackingParameters.gtp ${MITK_DATA_DIR}/DiffusionImaging/gibbsTractogram.fib) +mitkAddCustomModuleTest(mitkGibbsTrackingTest mitkGibbsTrackingTest ${MITK_DATA_DIR}/DiffusionImaging/qBallImage.qbi ${MITK_DATA_DIR}/DiffusionImaging/diffusionImageMask.nrrd ${MITK_DATA_DIR}/DiffusionImaging/gibbsTrackingParameters.gtp ${MITK_DATA_DIR}/DiffusionImaging/gibbsTractogram.fib) mitkAddCustomModuleTest(mitkStreamlineTrackingTest mitkStreamlineTrackingTest ${MITK_DATA_DIR}/DiffusionImaging/tensorImage.dti ${MITK_DATA_DIR}/DiffusionImaging/diffusionImageMask.nrrd ${MITK_DATA_DIR}/DiffusionImaging/streamlineTractogramInterpolated.fib) #mitkAddCustomModuleTest(mitkPeakExtractionTest mitkPeakExtractionTest ${MITK_DATA_DIR}/DiffusionImaging/qBallImage_SHCoeffs.nrrd ${MITK_DATA_DIR}/DiffusionImaging/diffusionImageMask.nrrd ${MITK_DATA_DIR}/DiffusionImaging/qBallImage_VectorField.fib) mitkAddCustomModuleTest(mitkLocalFiberPlausibilityTest mitkLocalFiberPlausibilityTest ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX.fib ${MITK_DATA_DIR}/DiffusionImaging/LDFP_GT_DIRECTION_0.nrrd ${MITK_DATA_DIR}/DiffusionImaging/LDFP_GT_DIRECTION_1.nrrd ${MITK_DATA_DIR}/DiffusionImaging/LDFP_ERROR_IMAGE.nrrd ${MITK_DATA_DIR}/DiffusionImaging/LDFP_NUM_DIRECTIONS.nrrd ${MITK_DATA_DIR}/DiffusionImaging/LDFP_VECTOR_FIELD.fib ${MITK_DATA_DIR}/DiffusionImaging/LDFP_ERROR_IMAGE_IGNORE.nrrd) mitkAddCustomModuleTest(mitkFiberTransformationTest mitkFiberTransformationTest ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX.fib ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX_transformed.fib) mitkAddCustomModuleTest(mitkFiberExtractionTest mitkFiberExtractionTest ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX.fib ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX_extracted.fib ${MITK_DATA_DIR}/DiffusionImaging/ROI1.pf ${MITK_DATA_DIR}/DiffusionImaging/ROI2.pf ${MITK_DATA_DIR}/DiffusionImaging/ROI3.pf ${MITK_DATA_DIR}/DiffusionImaging/ROIIMAGE.nrrd ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX_inside.fib ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX_outside.fib ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX_passing-mask.fib ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX_ending-in-mask.fib ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX_subtracted.fib ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX_added.fib) mitkAddCustomModuleTest(mitkFiberGenerationTest mitkFiberGenerationTest ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/Fiducial_0.pf ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/Fiducial_1.pf ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/Fiducial_2.pf ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/uniform.fib ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/gaussian.fib) mitkAddCustomModuleTest(mitkFiberfoxSignalGenerationTest mitkFiberfoxSignalGenerationTest ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/gaussian.fib ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/StickBall_RELAX.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/StickAstrosticks_RELAX.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/StickDot_RELAX.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/TensorBall_RELAX.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/StickTensorBall_RELAX.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/StickTensorBallAstrosticks_RELAX.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/gibbsringing.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/ghost.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/aliasing.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/eddy.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/linearmotion.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/randommotion.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/spikes.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/riciannoise.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/chisquarenoise.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/distortions.dwi ${MITK_DATA_DIR}/DiffusionImaging/Fiberfox/Fieldmap.nrrd) mitkAddCustomModuleTest(mitkFiberfoxAddArtifactsToDwiTest mitkFiberfoxAddArtifactsToDwiTest) ENDIF() diff --git a/Modules/DiffusionImaging/FiberTracking/Testing/files.cmake b/Modules/DiffusionImaging/FiberTracking/Testing/files.cmake index 8f4e9e3952..5ac537c171 100644 --- a/Modules/DiffusionImaging/FiberTracking/Testing/files.cmake +++ b/Modules/DiffusionImaging/FiberTracking/Testing/files.cmake @@ -1,15 +1,14 @@ SET(MODULE_CUSTOM_TESTS mitkFiberBundleXReaderWriterTest.cpp - # temporarily disabled, see bug #18253 - #mitkGibbsTrackingTest.cpp + mitkGibbsTrackingTest.cpp mitkStreamlineTrackingTest.cpp mitkPeakExtractionTest.cpp mitkLocalFiberPlausibilityTest.cpp mitkFiberTransformationTest.cpp mitkFiberExtractionTest.cpp mitkFiberGenerationTest.cpp mitkFiberfoxSignalGenerationTest.cpp mitkFiberfoxAddArtifactsToDwiTest.cpp )