From 8d767a3a2139b710f97e3b14553aa98fdae1362f Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Wed, 5 Feb 2020 19:38:18 +0100 Subject: [PATCH] CMake Deployment: Remove Dependencies component - Install will also copy the dependencies. - Made the dependencies search recursive, not just two steps - Moved install code to a script, not having to escape quotes and dollars Change-Id: I6c5fb19e4094e9dcd4b339d5b90be6e7c5055223 Reviewed-by: Alessandro Portale --- .github/workflows/build_cmake.yml | 4 +- CMakeLists.txt | 5 + cmake/CMakeLists.txt | 101 ++++++++ cmake/InstallDependentSharedObjects.cmake.in | 150 ++++++++++++ src/CMakeLists.txt | 2 - src/QtCreatorDeployment.cmake | 236 ------------------- 6 files changed, 257 insertions(+), 241 deletions(-) create mode 100644 cmake/CMakeLists.txt create mode 100644 cmake/InstallDependentSharedObjects.cmake.in delete mode 100644 src/QtCreatorDeployment.cmake diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 4a504f70a7b..7c864928490 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -365,9 +365,7 @@ jobs: - name: Install Strip - run: | - ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake --install build --prefix instdir --strip - ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake --install build --prefix instdir --component Dependencies + run: ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake --install build --prefix instdir --strip - name: Install Devel run: ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake --install build --prefix instdir-dev --component Devel diff --git a/CMakeLists.txt b/CMakeLists.txt index 44a5ad6a7e4..c4077ac3e99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,6 +138,11 @@ endif() add_subdirectory(doc) +# CMake will include in a cmake_install.cmake at the end the subdirectories +# At this point all the previous install scripts have been included +# Deployment is being done in cmake/CMakeLists.txt +add_subdirectory(cmake) + feature_summary(INCLUDE_QUIET_PACKAGES WHAT PACKAGES_FOUND PACKAGES_NOT_FOUND ENABLED_FEATURES DISABLED_FEATURES diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt new file mode 100644 index 00000000000..2d2d9f2b1b2 --- /dev/null +++ b/cmake/CMakeLists.txt @@ -0,0 +1,101 @@ +# Deployment of Qt, Clang, C++ Runtime libraries is being done in this script. +# +# It should be included as a subdirectory as last because of CMake's +# cmake_install.cmake script behviour of including subdirectories at the end +# of the script, not respecting the order of install commands from CMakeLists.txt +# +# This way we are sure that all the binaries have been installed before. + +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16) + get_target_property(moc_binary Qt5::moc IMPORTED_LOCATION) + get_filename_component(moc_dir "${moc_binary}" DIRECTORY) + get_filename_component(qt5_base_dir "${moc_dir}/../" ABSOLUTE) + + if (MSVC AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + set(exclusion_mask PATTERN "*d.dll" EXCLUDE) + endif() + + if (WIN32) + set(qt5_plugin_dest_dir ${IDE_BIN_PATH}/plugins) + set(qt5_qml_dest_dir ${IDE_BIN_PATH}/qml) + elseif(APPLE) + set(qt5_plugin_dest_dir ${IDE_PLUGIN_PATH}) + set(qt5_qml_dest_dir ${IDE_DATA_PATH}/../Imports/qtquick2) + else() + set(qt5_plugin_dest_dir ${IDE_LIBRARY_BASE_PATH}/Qt/plugins) + set(qt5_qml_dest_dir ${IDE_LIBRARY_BASE_PATH}/Qt/qml) + endif() + + foreach(plugin + designer iconengines imageformats platforms platformthemes + printsupport qmltooling sqldrivers styles) + if(NOT EXISTS "${qt5_base_dir}/plugins/${plugin}") + continue() + endif() + install( + DIRECTORY "${qt5_base_dir}/plugins/${plugin}" + DESTINATION ${qt5_plugin_dest_dir} + ${exclusion_mask} + ) + list(APPEND qt5_plugin_directories "${qt5_plugin_dest_dir}/${plugin}") + endforeach() + + install( + DIRECTORY "${qt5_base_dir}/qml/" + DESTINATION ${qt5_qml_dest_dir} + PATTERN "qml/*" + ${exclusion_mask} + ) + + install(CODE " + function(create_qt_conf location base_dir) + get_filename_component(install_prefix \"\${CMAKE_INSTALL_PREFIX}\" ABSOLUTE) + file(RELATIVE_PATH qt_conf_binaries + \"\${install_prefix}/\${base_dir}\" + \"\${install_prefix}/${IDE_BIN_PATH}\" + ) + if (NOT qt_conf_binaries) + set(qt_conf_binaries .) + endif() + file(RELATIVE_PATH qt_conf_plugins + \"\${install_prefix}/\${base_dir}\" + \"\${install_prefix}/${qt5_plugin_dest_dir}\" + ) + file(RELATIVE_PATH qt_conf_qml + \"\${install_prefix}/\${base_dir}\" + \"\${install_prefix}/${qt5_qml_dest_dir}\" + ) + + file(WRITE \"\${CMAKE_INSTALL_PREFIX}/\${location}/qt.conf\" + \"[Paths]\n\" + \"Binaries=\${qt_conf_binaries}\n\" + \"Plugins=\${qt_conf_plugins}\n\" + \"Qml2Imports=\${qt_conf_qml}\n\" + ) + endfunction() + + if(APPLE) + create_qt_conf(\"${IDE_DATA_PATH}\" \"${IDE_DATA_PATH}/..\") + create_qt_conf(\"${IDE_LIBEXEC_PATH}\" \"${IDE_DATA_PATH}/..\") + else() + create_qt_conf(\"${IDE_APP_PATH}\" \"${IDE_APP_PATH}\") + create_qt_conf(\"${IDE_LIBEXEC_PATH}\" \"${IDE_LIBEXEC_PATH}\") + endif() + + " + ) + + # Analyze the binaries and install missing dependencies if they are + # found the CMAKE_PREFIX_PATH e.g. Qt, Clang + configure_file(InstallDependentSharedObjects.cmake.in InstallDependentSharedObjects.cmake @ONLY) + install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/InstallDependentSharedObjects.cmake) + + if (MSVC) + set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP ON) + include(InstallRequiredsystemLibraries) + + install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} + DESTINATION ${IDE_APP_PATH} + ) + endif() +endif() diff --git a/cmake/InstallDependentSharedObjects.cmake.in b/cmake/InstallDependentSharedObjects.cmake.in new file mode 100644 index 00000000000..21494890808 --- /dev/null +++ b/cmake/InstallDependentSharedObjects.cmake.in @@ -0,0 +1,150 @@ +if (MINGW AND @CMAKE_CXX_COMPILER_ID@ MATCHES "Clang") + set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL objdump) +endif() +if (WIN32) + set(filter_regex PRE_EXCLUDE_REGEXES "api-ms-.*|ext-ms-.*") +elseif (APPLE) + set(filter_regex PRE_EXCLUDE_REGEXES "libiodbc.*|libpq.*") +endif() + +get_filename_component(install_prefix "${CMAKE_INSTALL_PREFIX}" ABSOLUTE) + +# Get the dependencies of Qt's plugins +foreach(plugin @qt5_plugin_directories@) + file(GLOB plugin_files "${install_prefix}/${plugin}/*@CMAKE_SHARED_LIBRARY_SUFFIX@") + list(APPEND qt5_plugin_files "${plugin_files}") +endforeach() + +# Get the qml module dependencies +file(GLOB_RECURSE qml_plugin_files "${install_prefix}/@qt5_qml_dest_dir@/*/*@CMAKE_SHARED_LIBRARY_SUFFIX@") +list(APPEND qt5_plugin_files "${qml_plugin_files}") + +set(installed_EXECUTABLES_NOT_PREFIXED "@__QTC_INSTALLED_EXECUTABLES@") +set(installed_LIBRARIES_NOT_PREFIXED "@__QTC_INSTALLED_LIBRARIES@") +set(installed_MODULES_NOT_PREFIXED "@__QTC_INSTALLED_PLUGINS@") + +foreach(binary_type EXECUTABLES LIBRARIES MODULES) + foreach(element IN LISTS installed_${binary_type}_NOT_PREFIXED) + if (EXISTS "${install_prefix}/${element}") + list(APPEND installed_${binary_type} "${install_prefix}/${element}") + endif() + endforeach() +endforeach() + +# Install first the dependencies, and continue to install the dependencies +# of the installed dependencies e.g. libicu for libQt5Core on Linux. + +list(LENGTH installed_LIBRARIES installed_libraries_size) +unset(qt_plugins_installed) + +while(installed_libraries_size GREATER 0) + foreach(binary_type EXECUTABLES LIBRARIES MODULES) + list(LENGTH installed_${binary_type} list_size) + if (NOT list_size EQUAL 0) + set(${binary_type}_TO_ANALYZE ${binary_type} "${installed_${binary_type}}") + else() + set(${binary_type}_TO_ANALYZE "") + endif() + endforeach() + + file(GET_RUNTIME_DEPENDENCIES + UNRESOLVED_DEPENDENCIES_VAR unresolved_deps + RESOLVED_DEPENDENCIES_VAR resolved_deps + CONFLICTING_DEPENDENCIES_PREFIX conflicts + ${EXECUTABLES_TO_ANALYZE} + ${LIBRARIES_TO_ANALYZE} + ${MODULES_TO_ANALYZE} + DIRECTORIES + "${install_prefix}/@IDE_BIN_PATH@" + "${install_prefix}/@IDE_PLUGIN_PATH@" + "${install_prefix}/@IDE_LIBRARY_PATH@" + "${install_prefix}/@IDE_LIBRARY_BASE_PATH@/Qt/lib" + ${filter_regex} + ) + + # Clear for next step + set(installed_EXECUTABLES "") + set(installed_LIBRARIES "") + set(installed_MODULES "") + + if (NOT qt_plugins_installed) + if (WIN32) + # Needed by QmlDesigner, QmlProfiler, but they are not referenced directly. + list(APPEND unresolved_deps libEGL.dll libGLESv2.dll) + endif() + + set(qt_plugins_installed TRUE) + set(installed_MODULES "${qt5_plugin_files}") + endif() + + list(REMOVE_DUPLICATES unresolved_deps) + list(REMOVE_DUPLICATES resolved_deps) + + file(TO_CMAKE_PATH "@CMAKE_PREFIX_PATH@" prefix_path) + + # Add parent link directory paths. Needed for e.g. MinGW choco libstdc++-6.dll + if (WIN32) + foreach(path "@CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES@") + get_filename_component(parent_path "${path}" DIRECTORY) + list(APPEND prefix_path "${parent_path}") + endforeach() + endif() + + # On Linux the Qt libraries might exist in the system, and they are passed + # as resolved, therefore scan the resolved dependencies too + if (NOT WIN32 AND NOT APPLE) + list(APPEND unresolved_deps "${resolved_deps}") + endif() + + foreach(so IN LISTS unresolved_deps) + + # Skip the installed dependencies + string(FIND "${so}" "${install_prefix}" found_prefix_path) + if (NOT found_prefix_path EQUAL -1) + continue() + endif() + + # On Linux get the name of the resolved Qt dependency, this would come from ldconfig + # with the full path on the system + if (NOT APPLE AND NOT WIN32) + get_filename_component(so "${so}" NAME) + endif() + + if (APPLE) + string(REPLACE "@rpath/" "" so "${so}") + get_filename_component(so_dir "${so}" DIRECTORY) + endif() + + message(STATUS "Dependency: ${so}") + foreach(p IN LISTS prefix_path) + if (WIN32) + set(so_src "${p}/bin/${so}") + set(so_dest "${install_prefix}/@IDE_BIN_PATH@") + elseif(APPLE) + set(so_src "${p}/lib/${so}") + set(so_dest "${install_prefix}/@IDE_LIBRARY_PATH@/${so_dir}") + else() + set(so_src "${p}/lib/${so}") + if (p STREQUAL "@qt5_base_dir@") + set(so_dest "${install_prefix}/@IDE_LIBRARY_BASE_PATH@/Qt/lib") + else() + set(so_dest "${install_prefix}/@IDE_LIBRARY_PATH@") + endif() + endif() + + message(STATUS "Looking at: ${so_src}") + if (EXISTS "${so_src}") + file(INSTALL "${so_src}" DESTINATION "${so_dest}" FOLLOW_SYMLINK_CHAIN) + if (APPLE) + get_filename_component(so "${so}" NAME) + endif() + list(APPEND installed_LIBRARIES "${so_dest}/${so}") + break() + endif() + + endforeach() + endforeach() + + list(LENGTH installed_LIBRARIES installed_libraries_size) + +endwhile() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0e8a324deb2..a04ed8a959b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,5 +74,3 @@ install( DESTINATION lib/cmake/QtCreator COMPONENT Devel EXCLUDE_FROM_ALL ) - -include(QtCreatorDeployment.cmake) diff --git a/src/QtCreatorDeployment.cmake b/src/QtCreatorDeployment.cmake deleted file mode 100644 index a24ab84c50a..00000000000 --- a/src/QtCreatorDeployment.cmake +++ /dev/null @@ -1,236 +0,0 @@ -# Dependencies component -if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16) - get_target_property(moc_binary Qt5::moc IMPORTED_LOCATION) - get_filename_component(moc_dir "${moc_binary}" DIRECTORY) - get_filename_component(qt5_base_dir "${moc_dir}/../" ABSOLUTE) - - if (MSVC AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - set(exclusion_mask PATTERN "*d.dll" EXCLUDE) - endif() - - if (WIN32) - set(qt5_plugin_dest_dir ${IDE_BIN_PATH}/plugins) - set(qt5_qml_dest_dir ${IDE_BIN_PATH}/qml) - elseif(APPLE) - set(qt5_plugin_dest_dir ${IDE_PLUGIN_PATH}) - set(qt5_qml_dest_dir ${IDE_DATA_PATH}/../Imports/qtquick2) - else() - set(qt5_plugin_dest_dir ${IDE_LIBRARY_BASE_PATH}/Qt/plugins) - set(qt5_qml_dest_dir ${IDE_LIBRARY_BASE_PATH}/Qt/qml) - endif() - - foreach(plugin - designer iconengines imageformats platforms platformthemes - printsupport qmltooling sqldrivers styles) - if(NOT EXISTS "${qt5_base_dir}/plugins/${plugin}") - continue() - endif() - install( - DIRECTORY "${qt5_base_dir}/plugins/${plugin}" - DESTINATION ${qt5_plugin_dest_dir} - COMPONENT Dependencies EXCLUDE_FROM_ALL - ${exclusion_mask} - ) - list(APPEND qt5_plugin_directories "${qt5_plugin_dest_dir}/${plugin}") - endforeach() - - install( - DIRECTORY "${qt5_base_dir}/qml/" - DESTINATION ${qt5_qml_dest_dir} - COMPONENT Dependencies EXCLUDE_FROM_ALL - PATTERN "qml/*" - ${exclusion_mask} - ) - - install(CODE " - function(create_qt_conf location base_dir) - get_filename_component(install_prefix \"\${CMAKE_INSTALL_PREFIX}\" ABSOLUTE) - file(RELATIVE_PATH qt_conf_binaries - \"\${install_prefix}/\${base_dir}\" - \"\${install_prefix}/${IDE_BIN_PATH}\" - ) - if (NOT qt_conf_binaries) - set(qt_conf_binaries .) - endif() - file(RELATIVE_PATH qt_conf_plugins - \"\${install_prefix}/\${base_dir}\" - \"\${install_prefix}/${qt5_plugin_dest_dir}\" - ) - file(RELATIVE_PATH qt_conf_qml - \"\${install_prefix}/\${base_dir}\" - \"\${install_prefix}/${qt5_qml_dest_dir}\" - ) - - file(WRITE \"\${CMAKE_INSTALL_PREFIX}/\${location}/qt.conf\" - \"[Paths]\n\" - \"Binaries=\${qt_conf_binaries}\n\" - \"Plugins=\${qt_conf_plugins}\n\" - \"Qml2Imports=\${qt_conf_qml}\n\" - ) - endfunction() - - if(APPLE) - create_qt_conf(\"${IDE_DATA_PATH}\" \"${IDE_DATA_PATH}/..\") - create_qt_conf(\"${IDE_LIBEXEC_PATH}\" \"${IDE_DATA_PATH}/..\") - else() - create_qt_conf(\"${IDE_APP_PATH}\" \"${IDE_APP_PATH}\") - create_qt_conf(\"${IDE_LIBEXEC_PATH}\" \"${IDE_LIBEXEC_PATH}\") - endif() - - " - COMPONENT Dependencies EXCLUDE_FROM_ALL - ) - - # Analyze the binaries and install missing dependencies if they are - # found the CMAKE_PREFIX_PATH e.g. Qt, Clang - install(CODE - " - if (MINGW AND CMAKE_CXX_COMPILER_ID MATCHES \"Clang\") - set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL objdump) - endif() - if (WIN32) - set(filter_regex PRE_EXCLUDE_REGEXES \"api-ms-.*|ext-ms-\.*\") - elseif (APPLE) - set(filter_regex PRE_EXCLUDE_REGEXES \"libiodbc\.*|libpq\.*\") - endif() - - get_filename_component(install_prefix \"\${CMAKE_INSTALL_PREFIX}\" ABSOLUTE) - - # Get the dependencies of Qt's plugins - foreach(plugin ${qt5_plugin_directories}) - file(GLOB plugin_files \"\${install_prefix}/\${plugin}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") - list(APPEND qt5_plugin_files \"\${plugin_files}\") - endforeach() - - # Get the qml module dependencies - file(GLOB_RECURSE qml_plugin_files \"\${install_prefix}/\${qt5_qml_dest_dir}/*/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") - list(APPEND qt5_plugin_files \"\${qml_plugin_files}\") - - set(installed_EXECUTABLES_NOT_PREFIXED \"${__QTC_INSTALLED_EXECUTABLES}\") - set(installed_LIBRARIES_NOT_PREFIXED \"${__QTC_INSTALLED_LIBRARIES}\") - set(installed_MODULES_NOT_PREFIXED \"${__QTC_INSTALLED_PLUGINS}\") - - foreach(binary_type EXECUTABLES LIBRARIES MODULES) - foreach(element IN LISTS installed_\${binary_type}_NOT_PREFIXED) - if (EXISTS \"\${install_prefix}/\${element}\") - list(APPEND installed_\${binary_type} \"\${install_prefix}/\${element}\") - endif() - endforeach() - endforeach() - - # Install first the dependencies, and in second step the dependencies - # from the installed dependencies e.g. libicu for libQt5Core on Linux. - foreach(step 1 2) - foreach(binary_type EXECUTABLES LIBRARIES MODULES) - list(LENGTH installed_\${binary_type} list_size) - if (NOT list_size EQUAL 0) - set(\${binary_type}_TO_ANALYZE \${binary_type} \"\${installed_\${binary_type}}\") - else() - set(\${binary_type}_TO_ANALYZE \"\") - endif() - endforeach() - - file(GET_RUNTIME_DEPENDENCIES - UNRESOLVED_DEPENDENCIES_VAR unresolved_deps - RESOLVED_DEPENDENCIES_VAR resolved_deps - CONFLICTING_DEPENDENCIES_PREFIX conflicts - \${EXECUTABLES_TO_ANALYZE} - \${LIBRARIES_TO_ANALYZE} - \${MODULES_TO_ANALYZE} - DIRECTORIES - \"\${install_prefix}/${IDE_BIN_PATH}\" - \"\${install_prefix}/${IDE_PLUGIN_PATH}\" - \"\${install_prefix}/${IDE_LIBRARY_PATH}\" - \"\${install_prefix}/${IDE_LIBRARY_BASE_PATH}/Qt/lib\" - \${filter_regex} - ) - - # Clear for second step - set(installed_EXECUTABLES \"\") - set(installed_LIBRARIES \"\") - set(installed_MODULES \"\${qt5_plugin_files}\") - - list(REMOVE_DUPLICATES unresolved_deps) - list(REMOVE_DUPLICATES resolved_deps) - - if (WIN32) - # Needed by QmlDesigner, QmlProfiler, but they are not referenced directly. - list(APPEND unresolved_deps libEGL.dll libGLESv2.dll) - endif() - - file(TO_CMAKE_PATH \"${CMAKE_PREFIX_PATH}\" prefix_path) - - # Add parent link directory paths. Needed for e.g. MinGW choco libstdc++-6.dll - if (WIN32) - foreach(path \"${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES}\") - get_filename_component(parent_path \"\${path}\" DIRECTORY) - list(APPEND prefix_path \"\${parent_path}\") - endforeach() - endif() - - # On Linux the Qt libraries might exist in the system, and they are passed - # as resolved, therefore scan the resolved dependencies too - foreach(so IN LISTS unresolved_deps resolved_deps) - - # Skip the installed dependencies - string(FIND \"\${so}\" \"\${install_prefix}\" found_prefix_path) - if (NOT found_prefix_path EQUAL -1) - continue() - endif() - - # On Linux get the name of the resolved Qt dependency, this would come from ldconfig - # with the full path on the system - if (NOT APPLE AND NOT WIN32) - get_filename_component(so \"\${so}\" NAME) - endif() - - if (APPLE) - string(REPLACE \"@rpath/\" \"\" so \"\${so}\") - get_filename_component(so_dir \"\${so}\" DIRECTORY) - endif() - - message(STATUS \"Dependency: \${so}\") - foreach(p IN LISTS prefix_path) - if (WIN32) - set(so_src \"\${p}/bin/\${so}\") - set(so_dest \"\${install_prefix}/${IDE_BIN_PATH}\") - elseif(APPLE) - set(so_src \"\${p}/lib/\${so}\") - set(so_dest \"\${install_prefix}/${IDE_LIBRARY_PATH}/\${so_dir}\") - else() - set(so_src \"\${p}/lib/\${so}\") - if (p STREQUAL \"${qt5_base_dir}\") - set(so_dest \"\${install_prefix}/${IDE_LIBRARY_BASE_PATH}/Qt/lib\") - else() - set(so_dest \"\${install_prefix}/${IDE_LIBRARY_PATH}\") - endif() - endif() - - message(STATUS \"Looking at: \${so_src}\") - if (EXISTS \"\${so_src}\") - file(INSTALL \"\${so_src}\" DESTINATION \"\${so_dest}\" FOLLOW_SYMLINK_CHAIN) - if (APPLE) - get_filename_component(so \"\${so}\" NAME) - endif() - list(APPEND installed_LIBRARIES \"\${so_dest}/\${so}\") - break() - endif() - - endforeach() - endforeach() - endforeach() - " - COMPONENT Dependencies EXCLUDE_FROM_ALL - ) - - if (MSVC) - set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP ON) - include(InstallRequiredsystemLibraries) - - install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} - DESTINATION ${IDE_APP_PATH} - COMPONENT Dependencies EXCLUDE_FROM_ALL - ) - endif() - -endif()