diff --git a/CMakeExternals/Boost-post_install-APPLE.cmake b/CMakeExternals/Boost-post_install-APPLE.cmake new file mode 100644 index 0000000000..0ae85b9331 --- /dev/null +++ b/CMakeExternals/Boost-post_install-APPLE.cmake @@ -0,0 +1,11 @@ +message(STATUS "Fixing relative dependencies") + +file(GLOB boost_dylibs ./libboost*.dylib) + +foreach(in ${boost_dylibs}) + foreach(other_boost_dylib ${boost_dylibs}) + get_filename_component(from ${other_boost_dylib} NAME) + set(to "@rpath/${from}") + execute_process(COMMAND install_name_tool -change ${from} ${to} ${in}) + endforeach() +endforeach() diff --git a/CMakeExternals/Boost-post_install-WIN32.cmake b/CMakeExternals/Boost-post_install-WIN32.cmake new file mode 100644 index 0000000000..4a54f4d2a6 --- /dev/null +++ b/CMakeExternals/Boost-post_install-WIN32.cmake @@ -0,0 +1,10 @@ +message(STATUS "Moving DLLs from lib to bin directory") + +file(GLOB boost_dlls boost_*.dll) + +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ../bin) + +foreach(boost_dll ${boost_dlls}) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${boost_dll} ../bin) + execute_process(COMMAND ${CMAKE_COMMAND} -E remove ${boost_dll}) +endforeach() diff --git a/CMakeExternals/Boost-pre_download.cmake b/CMakeExternals/Boost-pre_download.cmake new file mode 100644 index 0000000000..3d1c9fda3a --- /dev/null +++ b/CMakeExternals/Boost-pre_download.cmake @@ -0,0 +1,12 @@ +execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files extract-Boost.cmake extract-Boost.replacement.cmake + RESULT_VARIABLE is_original +) + +if(is_original) + message(STATUS "Hooking into extract script") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy extract-Boost.cmake extract-Boost.original.cmake) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy extract-Boost.replacement.cmake extract-Boost.cmake) +else() + message(STATUS "Reusing already extracted files") +endif() diff --git a/CMakeExternals/Boost.cmake b/CMakeExternals/Boost.cmake index 636c90e7e8..a27c1141bb 100644 --- a/CMakeExternals/Boost.cmake +++ b/CMakeExternals/Boost.cmake @@ -1,208 +1,317 @@ #----------------------------------------------------------------------------- # Boost #----------------------------------------------------------------------------- include(mitkFunctionGetMSVCVersion) -# Sanity checks +#[[ Sanity checks ]] if(DEFINED BOOST_ROOT AND NOT EXISTS ${BOOST_ROOT}) message(FATAL_ERROR "BOOST_ROOT variable is defined but corresponds to non-existing directory") endif() string(REPLACE "^^" ";" MITK_USE_Boost_LIBRARIES "${MITK_USE_Boost_LIBRARIES}") set(proj Boost) set(proj_DEPENDENCIES ) set(Boost_DEPENDS ${proj}) if(NOT DEFINED BOOST_ROOT AND NOT MITK_USE_SYSTEM_Boost) - set(_boost_version 1_68_0) - set(_boost_install_include_dir include/boost) + set(BOOST_ROOT ${ep_prefix}) + if(WIN32) - set(_boost_install_include_dir include/boost-${_boost_version}/boost) + set(BOOST_LIBRARYDIR "${BOOST_ROOT}/lib") endif() - set(_boost_libs ) - set(_with_boost_libs ) - set(_install_lib_dir ) + #[[ If you update Boost, make sure that the FindBoost module of the minimum + required version of CMake supports the new version of Boost. - # Set the boost root to the libraries install directory - set(BOOST_ROOT "${ep_prefix}") + In case you are using a higher version of CMake, download at least the + source code of the minimum required version of CMake to look into the + right version of the FindBoost module: - if(MITK_USE_Boost_LIBRARIES) - string(REPLACE ";" "," _boost_libs "${MITK_USE_Boost_LIBRARIES}") - foreach(_boost_lib ${MITK_USE_Boost_LIBRARIES}) - list(APPEND _with_boost_libs ${_with_boost_libs} --with-${_boost_lib}) - endforeach() - endif() + /share/cmake-/Modules/FindBoost.cmake - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_boost_address_model "address-model=64") - else() - set(_boost_address_model "address-model=32") - endif() + Search for a list called _Boost_KNOWN_VERSIONS. If the new version is + not included in this list, you have three options: + + * Update the minimum required version of CMake. This may require + adaptions of other parts of our CMake scripts and has the most + impact on other MITK developers. Yet this is the safest and + cleanest option. + + * Set Boost_ADDITIONAL_VERSIONS (see the documentation of the + FindBoost module). As Boost libraries and dependencies between + them are hard-coded in the FindBoost module only for known versions, + this may cause trouble for other MITK developers relying on new + components of Boost or components with changed dependencies. + + * Copy a newer version of the FindBoost module into our CMake + directory. Our CMake directory has a higher precedence than the + default CMake module directory. Doublecheck if the minimum required + version of CMake is able to process the newer version of the + FindBoost module. Also, DO NOT FORGET to mention this option right + above the call of cmake_minimum_required() in the top-level + CMakeLists.txt file AND in this file right above the set(url) + command below so if we update the minimum required version of CMake + or use another option in the future, we do not forget to remove our + copy of the FindBoost module again. ]] + + set(url "${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_1_68_0.7z") + set(md5 ae25f29cdb82cf07e8e26187ddf7d330) if(WIN32) - set(_shell_extension .bat) - set(_boost_layout) - if(MSVC) + #[[ See Task T25540 for details. Can be removed with Boost version greater + 1.68. In case the patch fails a "cd ." is executed to force the return + return value to be 0 (success). We need this because we reuse the + extracted source files and patching an already patched file returns + an error code that we can ignore. ]] + set(patch_cmd PATCH_COMMAND ${PATCH_COMMAND} --binary -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/Boost.patch || cd .) + endif() + + + + if(MITK_USE_Boost_LIBRARIES) + + #[[ Boost has a two-step build process. In the first step, a bootstrap + script is called to build b2, an executable that is used to actually + build Boost in the second step. + + The bootstrap script expects a toolset (compiler) argument that is + used to build the b2 executable. The scripts and their expected + argument format differ between Windows and Unix. ]] + + if(WIN32) + mitkFunctionGetMSVCVersion() - # Work around due to BOOST toolset limitations - # Remove if task T24222 is fixed - if(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR GREATER 0) - set(VISUAL_STUDIO_VERSION_MINOR 1) + if(VISUAL_STUDIO_VERSION_MINOR EQUAL 0) + + #[[ Use just the major version in the toolset name. ]] + set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}) + + elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR GREATER 0) + + #[[ There is no generic way of deducing the Boost toolset from the + current minor version of Visual Studio 2017. All we can do for now + is to assume that for all versions greater than 14.10 and less + than 15.0 toolset vc141 is the right choice. ]] + set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}1) + + else() + + #[[ Fallback to the generic case. Be prepared to add another elseif + branch above for future versions of Visual Studio. ]] + set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}${VISUAL_STUDIO_VERSION_MINOR}) + endif() - # End Work around - set(_boost_with_toolset "vc${VISUAL_STUDIO_VERSION_MAJOR}") - if(${VISUAL_STUDIO_VERSION_MINOR}) - set(_boost_with_toolset "${_boost_with_toolset}${VISUAL_STUDIO_VERSION_MINOR}") + else() + + #[[ We support GCC and Clang on Unix. On macOS, the toolset must be set + to darwin. The actual compiler for all of these toolkits is set + further below, after the bootstrap script but before b2. ]] + + if(APPLE) + set(toolset darwin) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL GNU) + set(toolset gcc) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang) + set(toolset clang) endif() - set(_boost_toolset "msvc-${VISUAL_STUDIO_VERSION_MAJOR}.${VISUAL_STUDIO_VERSION_MINOR}") + + if(toolset) + set(bootstrap_args --with-toolset=${toolset}) + endif() + + #[[ At least give it a shot if the toolset is something else and let + the bootstrap script decide on the toolset by not passing any + argument. ]] + endif() - set(_install_lib_dir "--libdir=/bin") - set(WIN32_CMAKE_SCRIPT ${ep_prefix}/src/${proj}-cmake/MoveBoostLibsToLibDirForWindows.cmake) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeExternals/MoveBoostLibsToLibDirForWindows.cmake.in ${WIN32_CMAKE_SCRIPT} @ONLY) - set(_windows_move_libs_cmd COMMAND ${CMAKE_COMMAND} -P ${WIN32_CMAKE_SCRIPT}) - else() - set(_shell_extension .sh) - set(_boost_layout "--layout=tagged") - endif() - if(UNIX AND NOT APPLE) - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(_boost_with_toolset "gcc") - elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(_boost_with_toolset "clang") + #[[ The call of b2 is more complex. b2 arguments are grouped into options + and properties. Options follow the standard format for arguments while + properties are plain key-value pairs. ]] + + set(b2_options + --build-dir= + --stagedir= + --ignore-site-config #[[ Build independent of any site.config file ]] + -q #[[ Stop at first error ]] + ) + + if(APPLE AND CMAKE_OSX_SYSROOT) + + #[[ Specify the macOS platform SDK to be used. ]] + list(APPEND b2_options --sysroot=${CMAKE_OSX_SYSROOT}) + + endif() + + foreach(lib ${MITK_USE_Boost_LIBRARIES}) + list(APPEND b2_options --with-${lib}) + endforeach() + + set(b2_properties + threading=multi + runtime-link=shared + "cxxflags=${CMAKE_CXX_FLAGS} ${MITK_CXX14_FLAG}" + ) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND b2_properties address-model=64) else() - message(FATAL_ERROR "Compiler '${CMAKE_CXX_COMPILER_ID}' not supported. Use GNU or Clang instead.") + list(APPEND b2_properties address-model=32) endif() - get_filename_component(_cxx_compiler_name "${CMAKE_CXX_COMPILER}" NAME) - string(REGEX MATCH "^[0-9]+\\.[0-9]+" _compiler_version "${CMAKE_CXX_COMPILER_VERSION}") - if(_cxx_compiler_name MATCHES "${_compiler_version}") - set(_boost_toolset "${_boost_with_toolset}-${_compiler_version}") - endif() - endif() - if(_boost_toolset) - set(_boost_toolset "--toolset=${_boost_toolset}") - endif() + if(BUILD_SHARED_LIBS) + list(APPEND b2_properties link=shared) + else() + list(APPEND b2_properties link=static) + endif() + + list(APPEND b2_properties "\ +$<$:variant=debug>\ +$<$:variant=release>\ +$<$:variant=release>\ +$<$:variant=release>") + + if(WIN32) + + set(bootstrap_cmd if not exist b2.exe \( call bootstrap.bat ${bootstrap_args} \)) + set(build_cmd cd && b2 ${b2_options} ${b2_properties} stage) - set (APPLE_SYSROOT_FLAG) - if(APPLE) - set(APPLE_CMAKE_SCRIPT ${ep_prefix}/src/${proj}-cmake/ChangeBoostLibsInstallNameForMac.cmake) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeExternals/ChangeBoostLibsInstallNameForMac.cmake.in ${APPLE_CMAKE_SCRIPT} @ONLY) - set(_macos_change_install_name_cmd COMMAND ${CMAKE_COMMAND} -P ${APPLE_CMAKE_SCRIPT}) + else() + + set(bootstrap_cmd test -e ./b2 || ./bootstrap.sh ${bootstrap_args}) + set(build_cmd cd && ./b2 ${b2_options} ${b2_properties} stage) + + #[[ We already told Boost if we want to use GCC or Clang but so far we + were not able to specify the exact same compiler we set in CMake + when configuring the MITK superbuild for the first time. + For example, this can be different from the system default + when multiple versions of the same compiler are installed + at the same time. + + The bootstrap script creates a configuration file for b2 that should + be modified if necessary before b2 is called. + We look for a line like + + using gcc ; + + and replace it with something more specific like + + using gcc : : /usr/bin/gcc-7.3 ; + + We use the stream editor sed for the replacement but as macOS is + based on BSD Unix, we use the limited but portable BSD syntax + instead of the more powerful GNU syntax. We also use | instead of + the more commonly used / separator for sed because the replacement + contains slashes. ]] + + if(toolset) + set(configure_cmd COMMAND + sed -i.backup "\ +s|\ +using[[:space:]][[:space:]]*${toolset}[[:space:]]*$|\ +using ${toolset} : : ${CMAKE_CXX_COMPILER} $|\ +g" + /project-config.jam + ) + endif() - # Set OSX_SYSROOT - if (NOT ${CMAKE_OSX_SYSROOT} STREQUAL "") - set (APPLE_SYSROOT_FLAG --sysroot=${CMAKE_OSX_SYSROOT}) endif() + endif() - set(_boost_variant "$<$:debug>$<$:release>") - set(_boost_link shared) - if(NOT BUILD_SHARED_LIBS) - set(_boost_link static) + if(NOT configure_cmd) + set(configure_cmd cd .) #[[ Do nothing ]] endif() - set(_boost_cxxflags ) - if(CMAKE_CXX_FLAGS OR MITK_CXX14_FLAG) - set(_boost_cxxflags "cxxflags=${MITK_CXX14_FLAG} ${CMAKE_CXX_FLAGS}") + + if(NOT build_cmd) + set(build_cmd cd .) #[[ Do nothing ]] endif() - set(_boost_linkflags ) - if(BUILD_SHARED_LIBS AND _install_rpath_linkflag) - set(_boost_linkflags "linkflags=${_install_rpath_linkflag}") + + if(WIN32) + set(install_cmd + if not exist $ + \( ${CMAKE_COMMAND} -E copy_directory /boost /include/boost \) + ) + else() + set(install_cmd + test -e /include/boost/config.hpp || + ${CMAKE_COMMAND} -E copy_directory /boost /include/boost + ) endif() - set(_build_cmd "/b2" - ${APPLE_SYSROOT_FLAG} - ${_boost_toolset} - ${_boost_layout} - "--prefix=" - ${_install_lib_dir} - ${_with_boost_libs} - # Use the option below to view the shell commands (for debugging) - #-d+4 - variant=${_boost_variant} - link=${_boost_link} - ${_boost_cxxflags} - ${_boost_linkflags} - ${_boost_address_model} - threading=multi - runtime-link=shared - # Some distributions site config breaks boost build - # For example on Gentoo: http://stackoverflow.com/questions/23013433/how-to-install-modular-boost - --ignore-site-config - -q + ExternalProject_Add(${proj} + URL ${url} + URL_MD5 ${md5} + ${patch_cmd} + CONFIGURE_COMMAND ${configure_cmd} + BUILD_COMMAND ${build_cmd} + INSTALL_COMMAND ${install_cmd} ) - if(MITK_USE_Boost_LIBRARIES) - set(_boost_build_cmd BUILD_COMMAND ${_build_cmd} install ${_macos_change_install_name_cmd} ${_windows_move_libs_cmd}) - else() - set(_boost_build_cmd BUILD_COMMAND - ${CMAKE_COMMAND} -E echo "copying Boost headers..." - COMMAND ${CMAKE_COMMAND} -E copy_directory "/boost" "/${_boost_install_include_dir}") + if(bootstrap_cmd) + ExternalProject_Add_Step(${proj} bootstrap + COMMAND ${bootstrap_cmd} + DEPENDEES patch + DEPENDERS configure + WORKING_DIRECTORY + ) endif() - set(_boost_patch_cmd PATCH_COMMAND ${PATCH_COMMAND} --binary -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/Boost.patch) + if(WIN32) - ExternalProject_Add(${proj} - LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_${_boost_version}.7z - URL_MD5 ae25f29cdb82cf07e8e26187ddf7d330 - BINARY_DIR "${ep_prefix}/src/${proj}" - ${_boost_patch_cmd} - CONFIGURE_COMMAND "/bootstrap${_shell_extension}" - --with-toolset=${_boost_with_toolset} - --with-libraries=${_boost_libs} - "--prefix=" - ${_boost_build_cmd} - INSTALL_COMMAND "" # done in BUILD_COMMAND - DEPENDS ${proj_DEPENDENCIES} - ) + #[[ Reuse already extracted files. ]] - ExternalProject_Get_Property(${proj} install_dir) + set(stamp_dir ${ep_prefix}/src/Boost-stamp) - if(WIN32) - set(BOOST_LIBRARYDIR "${install_dir}/lib") - endif() + configure_file( + ${CMAKE_CURRENT_LIST_DIR}/extract-Boost.replacement.cmake + ${stamp_dir}/extract-Boost.replacement.cmake + COPYONLY) - # Manual install commands (for a MITK super-build install) - # until the Boost CMake system is used. + ExternalProject_Add_Step(${proj} pre_download + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-pre_download.cmake + DEPENDEES mkdir + DEPENDERS download + WORKING_DIRECTORY ${stamp_dir} + ) - # We just copy the include directory - install(DIRECTORY "${install_dir}/${_boost_install_include_dir}" - DESTINATION "include" - COMPONENT dev - ) + endif() if(MITK_USE_Boost_LIBRARIES) - # Copy the boost libraries - file(GLOB _boost_libs - "${install_dir}/lib/libboost*.so*" - "${install_dir}/lib/libboost*.dylib") - install(FILES ${_boost_libs} - DESTINATION "lib" - COMPONENT runtime) - - file(GLOB _boost_libs - "${install_dir}/bin/libboost*.dll") - install(FILES ${_boost_libs} - DESTINATION "bin" - COMPONENT runtime) - - file(GLOB _boost_libs - "${install_dir}/lib/libboost*.lib" - "${install_dir}/lib/libboost*.a") - install(FILES ${_boost_libs} - DESTINATION "lib" - COMPONENT dev) + + if(WIN32) + + #[[ Move DLLs from lib to bin directory. ]] + + ExternalProject_Add_Step(${proj} post_install + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-WIN32.cmake + DEPENDEES install + WORKING_DIRECTORY /lib + ) + + elseif(APPLE) + + #[[ Boost does not follow the common practice of either using rpath or + absolute paths for referencing dependencies. We have to use the + install_name_tool to fix this. ]] + + ExternalProject_Add_Step(${proj} post_install + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-APPLE.cmake + DEPENDEES install + WORKING_DIRECTORY /lib + ) + + endif() + endif() else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeExternals/ChangeBoostLibsInstallNameForMac.cmake.in b/CMakeExternals/ChangeBoostLibsInstallNameForMac.cmake.in deleted file mode 100644 index e01d3a73bd..0000000000 --- a/CMakeExternals/ChangeBoostLibsInstallNameForMac.cmake.in +++ /dev/null @@ -1,23 +0,0 @@ -# Scan the MITK-Superbuild/ep/lib directory for *.dylib files and change their install names for mac -# On macOS system each shared library usually has a install name which is the absolute path of the library. -# For some reasons boost libs do not contain the absolute path but just their name -# (e.g. "libboost_thread.dylib" should be named "") - -# Get all the shared libraries which are located in the Boost-install/lib directory -file(GLOB dylibFiles @BOOST_ROOT@/lib/libboost*.dylib) - -# For each shared library call the install_name_tool in order to change the install name of the according library -foreach(_dylib ${dylibFiles}) - message("Fixing boost install name for lib: ${_dylib}") - get_filename_component(_dylib_name ${_dylib} NAME) - execute_process(COMMAND install_name_tool -id \@rpath/${_dylib_name} ${_dylib}) - foreach(_dep_dylib ${dylibFiles}) - get_filename_component(_dep_dylib_name ${_dep_dylib} NAME) - execute_process(COMMAND install_name_tool -change ${_dep_dylib_name} \@rpath/${_dep_dylib_name} ${_dylib}) - endforeach() -endforeach() - - - - - diff --git a/CMakeExternals/MoveBoostLibsToLibDirForWindows.cmake.in b/CMakeExternals/MoveBoostLibsToLibDirForWindows.cmake.in deleted file mode 100644 index b18ef085d0..0000000000 --- a/CMakeExternals/MoveBoostLibsToLibDirForWindows.cmake.in +++ /dev/null @@ -1,7 +0,0 @@ -file(GLOB libFiles "@BOOST_ROOT@/bin/boost_*.lib") - -foreach(libFile ${libFiles}) - message(STATUS "Moving lib file from bin to lib directory: ${libFile}") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${libFile}" "@BOOST_ROOT@/lib") - execute_process(COMMAND ${CMAKE_COMMAND} -E remove "${libFile}") -endforeach() diff --git a/CMakeExternals/extract-Boost.replacement.cmake b/CMakeExternals/extract-Boost.replacement.cmake new file mode 100644 index 0000000000..be64ccaf9a --- /dev/null +++ b/CMakeExternals/extract-Boost.replacement.cmake @@ -0,0 +1,4 @@ +get_filename_component(sample Boost/boost ABSOLUTE) +if(NOT EXISTS "${sample}") + include(Boost-stamp/extract-Boost.original.cmake) +endif()