CMake: Set RPATH of deployed plugins on Linux
When deploying into some directory structure where CMAKE_INSTALL_LIBDIR is different from Qt's lib dir, we need to set the RPATH of installed plugins such that Qt libraries are found. We do this using CMake's undocumented file(RPATH_SET) command and pray that this command is safe to use across current and future CMake versions. For CMake versions < 3.21, we use patchelf, which must be installed on the host system. The adjustment of rpaths can be turned on explicitly by setting QT_DEPLOY_FORCE_ADJUST_RPATHS to ON. The usage of patchelf can be forced by setting QT_DEPLOY_USE_PATCHELF to ON regardless of the CMake version. Change-Id: I62ced496b4c12bf6d46735d2af7ff35130148acb Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
parent
5ca714318c
commit
5430fb2243
@ -111,6 +111,50 @@ function(_qt_internal_re_escape out_var str)
|
||||
set(${out_var} ${regex} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_qt_internal_set_rpath)
|
||||
if(NOT CMAKE_HOST_UNIX OR CMAKE_HOST_APPLE)
|
||||
message(WARNING "_qt_internal_set_rpath is not implemented on this platform.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(no_value_options "")
|
||||
set(single_value_options FILE NEW_RPATH)
|
||||
set(multi_value_options "")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${no_value_options}" "${single_value_options}" "${multi_value_options}"
|
||||
)
|
||||
if(__QT_DEPLOY_USE_PATCHELF)
|
||||
message(STATUS "Setting runtime path of '${arg_FILE}' to '${arg_NEW_RPATH}'.")
|
||||
execute_process(
|
||||
COMMAND ${__QT_DEPLOY_PATCHELF_EXECUTABLE} --set-rpath "${arg_NEW_RPATH}" "${arg_FILE}"
|
||||
RESULT_VARIABLE process_result
|
||||
)
|
||||
if(NOT process_result EQUAL "0")
|
||||
if(process_result MATCHES "^[0-9]+$")
|
||||
message(FATAL_ERROR "patchelf failed with exit code ${process_result}.")
|
||||
else()
|
||||
message(FATAL_ERROR "patchelf failed: ${process_result}.")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# Warning: file(RPATH_SET) is CMake-internal API.
|
||||
file(RPATH_SET
|
||||
FILE "${arg_FILE}"
|
||||
NEW_RPATH "${arg_NEW_RPATH}"
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Store the platform-dependent $ORIGIN marker in out_var.
|
||||
function(_qt_internal_get_rpath_origin out_var)
|
||||
if(__QT_DEPLOY_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(rpath_origin "@loader_path")
|
||||
else()
|
||||
set(rpath_origin "$ORIGIN")
|
||||
endif()
|
||||
set(${out_var} ${rpath_origin} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_qt_internal_generic_deployqt)
|
||||
set(no_value_options
|
||||
NO_TRANSLATIONS
|
||||
@ -196,6 +240,11 @@ function(_qt_internal_generic_deployqt)
|
||||
FOLLOW_SYMLINK_CHAIN
|
||||
)
|
||||
|
||||
# Determine the runtime path origin marker if necessary.
|
||||
if(__QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH)
|
||||
_qt_internal_get_rpath_origin(rpath_origin)
|
||||
endif()
|
||||
|
||||
# Deploy the Qt plugins.
|
||||
foreach(file_path IN LISTS __QT_DEPLOY_PLUGINS)
|
||||
file(RELATIVE_PATH destination
|
||||
@ -205,6 +254,16 @@ function(_qt_internal_generic_deployqt)
|
||||
get_filename_component(destination "${destination}" DIRECTORY)
|
||||
string(PREPEND destination "${QT_DEPLOY_PREFIX}/${arg_PLUGINS_DIR}/")
|
||||
file(INSTALL ${file_path} DESTINATION ${destination})
|
||||
|
||||
if(__QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH)
|
||||
get_filename_component(file_name ${file_path} NAME)
|
||||
file(RELATIVE_PATH rel_lib_dir "${destination}"
|
||||
"${QT_DEPLOY_PREFIX}/${QT_DEPLOY_LIB_DIR}")
|
||||
_qt_internal_set_rpath(
|
||||
FILE "${destination}/${file_name}"
|
||||
NEW_RPATH "${rpath_origin}/${rel_lib_dir}"
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Deploy translations.
|
||||
|
@ -2452,6 +2452,31 @@ function(_qt_internal_setup_deploy_support)
|
||||
|
||||
_qt_internal_add_deploy_support("${CMAKE_CURRENT_LIST_DIR}/Qt6CoreDeploySupport.cmake")
|
||||
|
||||
# Check whether we will have to adjust the RPATH of plugins.
|
||||
if("${QT_DEPLOY_FORCE_ADJUST_RPATHS}" STREQUAL "")
|
||||
set(must_adjust_plugins_rpath "")
|
||||
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows"
|
||||
AND NOT CMAKE_INSTALL_LIBDIR STREQUAL QT6_INSTALL_LIBS)
|
||||
set(must_adjust_plugins_rpath ON)
|
||||
endif()
|
||||
else()
|
||||
set(must_adjust_plugins_rpath "${QT_DEPLOY_FORCE_ADJUST_RPATHS}")
|
||||
endif()
|
||||
|
||||
# Find the patchelf executable if necessary.
|
||||
if(must_adjust_plugins_rpath)
|
||||
if(CMAKE_VERSION VERSION_LESS "3.21")
|
||||
set(QT_DEPLOY_USE_PATCHELF ON)
|
||||
endif()
|
||||
if(QT_DEPLOY_USE_PATCHELF)
|
||||
find_program(QT_DEPLOY_PATCHELF_EXECUTABLE patchelf)
|
||||
if(NOT QT_DEPLOY_PATCHELF_EXECUTABLE)
|
||||
message(FATAL_ERROR "The patchelf executable could not be located. "
|
||||
"Please install patchelf or upgrade CMake to 3.21 or newer.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(GENERATE OUTPUT "${QT_DEPLOY_SUPPORT}" CONTENT
|
||||
"cmake_minimum_required(VERSION 3.16...3.21)
|
||||
|
||||
@ -2496,6 +2521,9 @@ set(__QT_DEPLOY_QT_INSTALL_BINS \"${QT6_INSTALL_BINS}\")
|
||||
set(__QT_DEPLOY_QT_INSTALL_PLUGINS \"${QT6_INSTALL_PLUGINS}\")
|
||||
set(__QT_DEPLOY_QT_INSTALL_TRANSLATIONS \"${QT6_INSTALL_TRANSLATIONS}\")
|
||||
set(__QT_DEPLOY_PLUGINS \"\")
|
||||
set(__QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH \"${must_adjust_plugins_rpath}\")
|
||||
set(__QT_DEPLOY_USE_PATCHELF \"${QT_DEPLOY_USE_PATCHELF}\")
|
||||
set(__QT_DEPLOY_PATCHELF_EXECUTABLE \"${QT_DEPLOY_PATCHELF_EXECUTABLE}\")
|
||||
|
||||
# Define the CMake commands to be made available during deployment.
|
||||
set(__qt_deploy_support_files
|
||||
|
@ -46,6 +46,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(cmake_usage_tests)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Building the CMake tests as part of a Qt prefix build + in-tree tests, currently doesn't work.
|
||||
# Each CMake test will fail with a message like
|
||||
@ -335,12 +336,16 @@ set(deploy_args
|
||||
NO_RUN_ENVIRONMENT_PLUGIN_PATH
|
||||
)
|
||||
|
||||
# For now, the test should only pass on Windows and macOS shared and static builds and fail on
|
||||
# other platforms, because there is no support for runtime dependency deployment
|
||||
# on those platforms.
|
||||
set(is_desktop_linux FALSE)
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT CMAKE_CROSSCOMPILING)
|
||||
set(is_desktop_linux TRUE)
|
||||
endif()
|
||||
|
||||
# For now, the test should only pass on Windows, macOS and desktop Linux shared and static builds
|
||||
# and fail on other platforms, because there is no support for runtime dependency deployment on
|
||||
# those platforms.
|
||||
# With static builds the runtime dependencies are just skipped, but the test should still pass.
|
||||
if(WIN32 OR (APPLE AND NOT IOS)
|
||||
OR (UNIX AND NOT APPLE AND NOT ANDROID AND NOT CMAKE_CROSSCOMPILING))
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR is_desktop_linux)
|
||||
_qt_internal_test_expect_pass(${deploy_args})
|
||||
else()
|
||||
_qt_internal_test_expect_fail(${deploy_args})
|
||||
|
@ -5,8 +5,6 @@ cmake_minimum_required(VERSION 3.16)
|
||||
project(deployment_api)
|
||||
enable_testing()
|
||||
|
||||
set(CMAKE_INSTALL_LIBDIR lib) ### temporary hack to make the test pass - remove in next commit!
|
||||
|
||||
find_package(Qt6 COMPONENTS REQUIRED Widgets Test)
|
||||
|
||||
qt6_standard_project_setup()
|
||||
|
Loading…
x
Reference in New Issue
Block a user