CMake: Deploy runtime dependencies outside of the Qt prefix too

We restricted the runtime dependencies we deployed on Linux to libraries
within the Qt installation prefix.  This restriction was supposed to
prevent the deployment of all kinds of system libraries, which is most
likely not wanted.

However, the user might link against non-system libraries, and those
should be deployed.  The same holds for QML backend libraries that exist
outside the Qt installation prefix in the build directory of the
project.

Now, we restrict deployment to libraries that are not in default system
library directories.  This can be overridden with the new
qt_deploy_runtime_dependencies option POST_EXCLUDE_REGEXES.

We add the following options to qt_deploy_runtime_dependencies, which
are then forwarded to file(GET_RUNTIME_DEPENDENCIES):
PRE_INCLUDE_REGEXES, PRE_EXCLUDE_REGEXES, POST_INCLUDE_REGEXES,
POST_EXCLUDE_REGEXES, and POST_INCLUDE_FILES.

Change-Id: I99a98fd91218abedda270609d0bafbb7f3e0feeb
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
Joerg Bornemann 2022-09-29 16:55:46 +02:00
parent 5430fb2243
commit 1407e0fe50
4 changed files with 128 additions and 31 deletions

View File

@ -161,15 +161,21 @@ function(_qt_internal_generic_deployqt)
VERBOSE
)
set(single_value_options
EXECUTABLE
LIB_DIR
PLUGINS_DIR
)
set(multi_value_options
ADDITIONAL_EXECUTABLES
ADDITIONAL_LIBRARIES
ADDITIONAL_MODULES
set(file_GRD_options
EXECUTABLES
LIBRARIES
MODULES
PRE_INCLUDE_REGEXES
PRE_EXCLUDE_REGEXES
POST_INCLUDE_REGEXES
POST_EXCLUDE_REGEXES
POST_INCLUDE_FILES
POST_EXCLUDE_FILES
)
set(multi_value_options ${file_GRD_options})
cmake_parse_arguments(PARSE_ARGV 0 arg
"${no_value_options}" "${single_value_options}" "${multi_value_options}"
)
@ -179,7 +185,7 @@ function(_qt_internal_generic_deployqt)
endif()
# Make input file paths absolute
foreach(var IN ITEMS EXECUTABLE ADDITIONAL_EXECUTABLES ADDITIONAL_LIBRARIES ADDITIONAL_MODULES)
foreach(var IN ITEMS EXECUTABLES LIBRARIES MODULES)
string(PREPEND var arg_)
set(abspaths "")
foreach(path IN LISTS ${var})
@ -190,32 +196,31 @@ function(_qt_internal_generic_deployqt)
endforeach()
# We need to get the runtime dependencies of plugins too.
list(APPEND arg_ADDITIONAL_MODULES ${__QT_DEPLOY_PLUGINS})
list(APPEND arg_MODULES ${__QT_DEPLOY_PLUGINS})
set(file_args "")
if(arg_EXECUTABLE OR arg_ADDITIONAL_EXECUTABLES)
list(APPEND file_args EXECUTABLES ${arg_EXECUTABLE} ${arg_ADDITIONAL_EXECUTABLES})
# Forward the arguments that are exactly the same for file(GET_RUNTIME_DEPENDENCIES).
set(file_GRD_args "")
foreach(var IN LISTS file_GRD_options)
if(NOT "${arg_${var}}" STREQUAL "")
list(APPEND file_GRD_args ${var} ${arg_${var}})
endif()
if(arg_ADDITIONAL_LIBRARIES)
list(APPEND file_args LIBRARIES ${arg_ADDITIONAL_LIBRARIES})
endif()
if(arg_ADDITIONAL_MODULES)
list(APPEND file_args MODULES ${arg_ADDITIONAL_MODULES})
endif()
# Compile a list of regular expressions that represent the Qt installation prefixes.
set(prefix_regexes)
foreach(path IN LISTS __QT_DEPLOY_QT_INSTALL_PREFIX
__QT_DEPLOY_QT_ADDITIONAL_PACKAGES_PREFIX_PATH)
_qt_internal_re_escape(path_rex "${path}")
list(APPEND prefix_regexes "^${path_rex}")
endforeach()
# Get the runtime dependencies recursively, restricted to Qt's installation prefix.
# Compile a list of regular expressions that represent ignored library directories.
if("${arg_POST_EXCLUDE_REGEXES}" STREQUAL "")
set(regexes "")
foreach(path IN LISTS QT_DEPLOY_IGNORED_LIB_DIRS)
_qt_internal_re_escape(path_rex "${path}")
list(APPEND regexes "^${path_rex}")
endforeach()
if(regexes)
list(APPEND file_GRD_args POST_EXCLUDE_REGEXES ${regexes})
endif()
endif()
# Get the runtime dependencies recursively.
file(GET_RUNTIME_DEPENDENCIES
${file_args}
POST_INCLUDE_REGEXES ${prefix_regexes}
POST_EXCLUDE_REGEXES ".*"
${file_GRD_args}
RESOLVED_DEPENDENCIES_VAR resolved
UNRESOLVED_DEPENDENCIES_VAR unresolved
CONFLICTING_DEPENDENCIES_PREFIX conflicting
@ -294,6 +299,16 @@ function(qt6_deploy_runtime_dependencies)
PLUGINS_DIR
QML_DIR
)
set(file_GRD_options
# The following include/exclude options are only used if the "generic deploy tool" is
# used. The options are what file(GET_RUNTIME_DEPENDENCIES) supports.
PRE_INCLUDE_REGEXES
PRE_EXCLUDE_REGEXES
POST_INCLUDE_REGEXES
POST_EXCLUDE_REGEXES
POST_INCLUDE_FILES
POST_EXCLUDE_FILES
)
set(multi_value_options
# These ADDITIONAL_... options are based on what file(GET_RUNTIME_DEPENDENCIES)
# supports. We differentiate between the types of binaries so that we keep
@ -303,6 +318,7 @@ function(qt6_deploy_runtime_dependencies)
ADDITIONAL_EXECUTABLES
ADDITIONAL_LIBRARIES
ADDITIONAL_MODULES
${file_GRD_options}
)
cmake_parse_arguments(PARSE_ARGV 0 arg
"${no_value_options}" "${single_value_options}" "${multi_value_options}"
@ -404,12 +420,23 @@ function(qt6_deploy_runtime_dependencies)
if(__QT_DEPLOY_TOOL STREQUAL "GRD")
message(STATUS "Running generic Qt deploy tool on ${arg_EXECUTABLE}")
# Forward the ADDITIONAL_* arguments.
foreach(file_type EXECUTABLES LIBRARIES MODULES)
# Construct the EXECUTABLES, LIBRARIES and MODULES arguments.
list(APPEND tool_options EXECUTABLES ${arg_EXECUTABLE})
if(NOT "${arg_ADDITIONAL_EXECUTABLES}" STREQUAL "")
list(APPEND tool_options ${arg_ADDITIONAL_EXECUTABLES})
endif()
foreach(file_type LIBRARIES MODULES)
if("${arg_ADDITIONAL_${file_type}}" STREQUAL "")
continue()
endif()
list(APPEND tool_options ADDITIONAL_${file_type} ${arg_ADDITIONAL_${file_type}})
list(APPEND tool_options ${file_type} ${arg_ADDITIONAL_${file_type}})
endforeach()
# Forward the arguments that are exactly the same for file(GET_RUNTIME_DEPENDENCIES).
foreach(var IN LISTS file_GRD_options)
if(NOT "${arg_${var}}" STREQUAL "")
list(APPEND tool_options ${var} ${arg_${var}})
endif()
endforeach()
if(arg_NO_TRANSLATIONS)

View File

@ -2452,6 +2452,24 @@ function(_qt_internal_setup_deploy_support)
_qt_internal_add_deploy_support("${CMAKE_CURRENT_LIST_DIR}/Qt6CoreDeploySupport.cmake")
set(deploy_ignored_lib_dirs "")
if(__QT_DEPLOY_TOOL STREQUAL "GRD" AND NOT "${QT6_INSTALL_PREFIX}" STREQUAL "")
# Set up the directories we want to ignore when running file(GET_RUNTIME_DEPENDENCIES).
# If the Qt prefix is the root of one of those directories, don't ignore that directory.
# For example, if Qt's installation prefix is /usr, then we don't want to ignore /usr/lib.
foreach(link_dir IN LISTS CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES)
file(RELATIVE_PATH relative_dir "${QT6_INSTALL_PREFIX}" "${link_dir}")
if(relative_dir STREQUAL "")
# The Qt prefix is exactly ${link_dir}.
continue()
endif()
if(IS_ABSOLUTE "${relative_dir}" OR relative_dir MATCHES "^\\.\\./")
# The Qt prefix is outside of ${link_dir}.
list(APPEND deploy_ignored_lib_dirs "${link_dir}")
endif()
endforeach()
endif()
# Check whether we will have to adjust the RPATH of plugins.
if("${QT_DEPLOY_FORCE_ADJUST_RPATHS}" STREQUAL "")
set(must_adjust_plugins_rpath "")
@ -2503,6 +2521,9 @@ endif()
if(QT_DEPLOY_PREFIX STREQUAL \"\")
set(QT_DEPLOY_PREFIX .)
endif()
if(NOT QT_DEPLOY_IGNORED_LIB_DIRS)
set(QT_DEPLOY_IGNORED_LIB_DIRS \"${deploy_ignored_lib_dirs}\")
endif()
# These are internal implementation details. They may be removed at any time.
set(__QT_DEPLOY_SYSTEM_NAME \"${CMAKE_SYSTEM_NAME}\")

View File

@ -241,3 +241,32 @@ This variable is not meaningful when deploying on macOS or Windows.
\sa QT_DEPLOY_SUPPORT, QT_DEPLOY_PREFIX, QT_DEPLOY_BIN_DIR, QT_DEPLOY_LIB_DIR,
QT_DEPLOY_PLUGINS_DIR, QT_DEPLOY_QML_DIR
*/
/*!
\page cmake-variable-QT_DEPLOY_IGNORED_LIB_DIRS.html
\ingroup cmake-variables-qtcore
\title QT_DEPLOY_IGNORED_LIB_DIRS
\target cmake-variable-QT_DEPLOY_IGNORED_LIB_DIRS
\summary {Directories that are excluded from runtime dependencies search}
\include cmake-deploy-var-usage.qdocinc
\cmakevariablesince 6.5
\preliminarycmakevariable
This variable contains a list of directories that are not taken into account
when searching for runtime dependencies with \l{qt_deploy_runtime_dependencies}.
Projects may alter this variable before calling
\l{qt_deploy_runtime_dependencies} to control from which directory runtime
dependencies are deployed.
This variable is ignored if the \c{POST_EXCLUDE_REGEXES} option is specified in
the \l{qt_deploy_runtime_dependencies} call.
This variable is not meaningful when deploying on macOS or Windows.
\sa qt_deploy_runtime_dependencies
*/

View File

@ -39,6 +39,12 @@ qt_deploy_runtime_dependencies(
[NO_OVERWRITE]
[NO_APP_STORE_COMPLIANCE]
[NO_TRANSLATIONS]
[PRE_INCLUDE_REGEXES regexes...]
[PRE_EXCLUDE_REGEXES regexes...]
[POST_INCLUDE_REGEXES regexes...]
[POST_EXCLUDE_REGEXES regexes...]
[POST_INCLUDE_FILES files...]
[POST_EXCLUDE_FILES files...]
)
\endcode
@ -119,6 +125,20 @@ inhibit this behavior, specify \c{NO_TRANSLATIONS}. Use
\l{qt6_deploy_translations}{qt_deploy_translations} to deploy translations in a
customized way.
On Linux, deploying runtime dependencies is based on CMake's
\c{file(GET_RUNTIME_DEPENDENCIES)} command. The options \c{PRE_INCLUDE_REGEXES},
\c{PRE_EXCLUDE_REGEXES}, \c{POST_INCLUDE_REGEXES}, \c{POST_EXCLUDE_REGEXES},
\c{POST_INCLUDE_FILES}, and \c{POST_EXCLUDE_FILES} are only meaningful in this
context and are forwarded unaltered to \c{file(GET_RUNTIME_DEPENDENCIES)}. See
the documentation of that command for details.
On Linux, runtime dependencies that are located in system library directories
are not deployed by default. If \c{POST_EXCLUDE_REGEXES} is specified, this
automatic exclusion is not performed.
The default value of \c{POST_EXCLUDE_REGEXES} is constructed from the value of
\l{QT_DEPLOY_IGNORED_LIB_DIRS}.
\sa {qt6_generate_deploy_app_script}{qt_generate_deploy_app_script()},
qt_deploy_qt_conf(), qt_deploy_qml_imports()