summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2022-08-18 16:54:30 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2022-09-06 13:24:09 +0200
commit6abaff810a41e6e4490a47ff4ea8d50308bdba3b (patch)
tree53b3b560b072694d3416c13bb14647a7c90f6603 /src
parent8cbe8e8266cbff1554965315593c208a5816f444 (diff)
CMake: Collect IMPORTED library dependencies for android deployment
Previously _qt_internal_collect_apk_dependencies() only collected targets that were built by the current project and informed androiddeployqt their directory paths for deployment consideration. The libraries would be bundled into the apk based on whether the application links to any of those libraries directly, e.g. via target_link_libraries. CMake 3.21 added a new feature that allows us to query all IMPORTED targets in a directory scope. Using this information we can now tell androiddeployqt about these libraries as well, of course only when a new enough CMake version is used. Introduce _qt_internal_collect_apk_imported_dependencies_defer to do that. In contrast to _qt_internal_collect_apk_dependencies(), the IMPORTED libraries are collected by recursively looking at a target's directory scope and its parent directories, rather than child subdirectories. Also, the collection / deferral is done when the target's directory scope has finished processing, rather than when the root project has finished processing. Why? IMPORTED libraries are usually non-global, which means we can't refer to them in parent scopes using functions like get_target_property, the targets don't exist in those scopes. Note that the code only covers IMPORTED target libraries. It does not cover INTERFACE libraries, plain library names or file paths specified in target_link_libraries. It also doesn't account for loadable libraries (dlopen()ed ones). In such cases, projects still need to manually specify the library paths in the executable's QT_ANDROID_EXTRA_LIBS target property. To prevent the deployment json file from being extra long when looking for IMPORTED libraries, filter out paths to Qt modules or plugins by checking for the _qt_package_version property. These are handled via a different code path in anddroiddeployqt, so there's no reason to duplicate the information. Added documentation for how to opt out of this new behavior. Amends d20f4ae706559fb7de8db9dd4845f7ce3936061a [ChangeLog][CMake][Android] Imported targets are now considered during Android deployment when using CMake 3.21+. Fixes: QTBUG-94714 Fixes: QTBUG-105165 Change-Id: I9f01cdc4e63004e4768027e412899e441e72bf04 Reviewed-by: Jörg Bornemann <joerg.bornemann@qt.io> Reviewed-by: Paul Wicking <paul.wicking@qt.io> Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/corelib/Qt6AndroidMacros.cmake76
-rw-r--r--src/corelib/doc/src/cmake/cmake-configure-variables.qdoc27
-rw-r--r--src/corelib/doc/src/cmake/qt_finalize_project.qdoc1
3 files changed, 104 insertions, 0 deletions
diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake
index 5750437594..fdc4981c3f 100644
--- a/src/corelib/Qt6AndroidMacros.cmake
+++ b/src/corelib/Qt6AndroidMacros.cmake
@@ -529,6 +529,7 @@ function(qt6_android_add_apk_target target)
set_property(GLOBAL APPEND PROPERTY _qt_apk_targets ${target})
_qt_internal_collect_apk_dependencies_defer()
+ _qt_internal_collect_apk_imported_dependencies_defer("${target}")
endfunction()
function(_qt_internal_create_global_android_targets)
@@ -654,6 +655,81 @@ function(_qt_internal_collect_buildsystem_shared_libraries out_var subdir)
set(${out_var} "${result}" PARENT_SCOPE)
endfunction()
+# This function collects all imported shared libraries that might be dependencies for
+# the main apk targets. The actual collection is deferred until the target's directory scope
+# is processed.
+# The function requires CMake 3.21 or later.
+function(_qt_internal_collect_apk_imported_dependencies_defer target)
+ # User opted-out of the functionality.
+ if(QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS)
+ return()
+ endif()
+
+ get_target_property(target_source_dir "${target}" SOURCE_DIR)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.21")
+ cmake_language(EVAL CODE "cmake_language(DEFER DIRECTORY \"${target_source_dir}\"
+ CALL _qt_internal_collect_apk_imported_dependencies \"${target}\")")
+ endif()
+endfunction()
+
+# This function collects imported shared libraries that might be dependencies for
+# the main apk targets. It stores their locations on a custom target property for the given target.
+# The function requires CMake 3.21 or later.
+function(_qt_internal_collect_apk_imported_dependencies target)
+ # User opted-out the functionality
+ if(QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS)
+ return()
+ endif()
+
+ get_target_property(target_source_dir "${target}" SOURCE_DIR)
+ _qt_internal_collect_imported_shared_libraries_recursive(libs "${target_source_dir}")
+ list(REMOVE_DUPLICATES libs)
+
+ foreach(lib IN LISTS libs)
+ list(APPEND extra_library_dirs "$<TARGET_FILE_DIR:${lib}>")
+ endforeach()
+
+ set_property(TARGET "${target}" APPEND PROPERTY
+ _qt_android_extra_library_dirs "${extra_library_dirs}"
+ )
+endfunction()
+
+# This function recursively walks the current directory and its parent directories to collect
+# imported shared library targets.
+# The recursion goes upwards instead of downwards because imported targets are usually not global,
+# and we can't call get_target_property() on a target which is not available in the current
+# directory or parent scopes.
+# We also can't cache parent directories because the imported targets in a parent directory
+# might change in-between collection calls.
+# The function requires CMake 3.21 or later.
+function(_qt_internal_collect_imported_shared_libraries_recursive out_var subdir)
+ set(result "")
+
+ get_directory_property(imported_targets DIRECTORY "${subdir}" IMPORTED_TARGETS)
+ foreach(imported_target IN LISTS imported_targets)
+ get_target_property(target_type "${imported_target}" TYPE)
+ if(target_type STREQUAL "SHARED_LIBRARY" OR target_type STREQUAL "MODULE_LIBRARY")
+ # If the target has the _qt_package_version property set, it means it's an
+ # 'official' qt target like a module or plugin, so we don't want to add it
+ # to the list of extra paths to scan for in androiddeployqt, because they are
+ # already handled via the regular 'qt' code path in the androiddeployqt.
+ # Thus this will pick up only non-qt 3rd party targets.
+ get_target_property(qt_package_version "${imported_target}" _qt_package_version)
+ if(NOT qt_package_version)
+ list(APPEND result "${imported_target}")
+ endif()
+ endif()
+ endforeach()
+
+ get_directory_property(parent_dir DIRECTORY "${subdir}" PARENT_DIRECTORY)
+ if(parent_dir)
+ _qt_internal_collect_imported_shared_libraries_recursive(result_inner "${parent_dir}")
+ endif()
+
+ list(APPEND result ${result_inner})
+ set(${out_var} "${result}" PARENT_SCOPE)
+endfunction()
+
# This function allows deciding whether apks should be built as part of the ALL target at first
# add_executable call point, rather than when the 'apk' target is created as part of the
# find_package(Core) call.
diff --git a/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc b/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc
index 4b759c649b..bef29a6a94 100644
--- a/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc
+++ b/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc
@@ -225,6 +225,33 @@ resolving dependencies between libraries.
Set \c QT_NO_COLLECT_BUILD_TREE_APK_DEPS to \c TRUE to disable this behavior.
\sa {qt6_finalize_project}{qt_finalize_project()}
+\sa {cmake-variable-QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS}{QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS}
+*/
+
+/*!
+\page cmake-variable-QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS.html
+\ingroup cmake-variables-qtcore
+
+\title QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS
+\target cmake-variable-QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS
+
+\summary {Prevents collecting of imported targets during Android deployment.}
+
+\cmakevariablesince 6.5
+\preliminarycmakevariable
+\cmakevariableandroidonly
+
+When using CMake version 3.21 or later, the build system collects the locations of
+imported shared library targets that might be relevant for deployment.
+The collected targets are those that are reachable from the the directory scope
+of the currently processed executable target. That includes the target's source directory
+scope and its parents.
+The collected locations are passed to \l androiddeployqt for deployment consideration when
+resolving dependencies between libraries.
+Set \c QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS to \c TRUE to disable this behavior.
+
+\sa {qt6_finalize_project}{qt_finalize_project()}
+\sa {cmake-variable-QT_NO_COLLECT_BUILD_TREE_APK_DEPS}{QT_NO_COLLECT_BUILD_TREE_APK_DEPS}
*/
/*!
diff --git a/src/corelib/doc/src/cmake/qt_finalize_project.qdoc b/src/corelib/doc/src/cmake/qt_finalize_project.qdoc
index 5cd6a9fdaf..beee18ed80 100644
--- a/src/corelib/doc/src/cmake/qt_finalize_project.qdoc
+++ b/src/corelib/doc/src/cmake/qt_finalize_project.qdoc
@@ -49,4 +49,5 @@ function:
\snippet cmake-macros/examples.cmake qt_finalize_project_manual
\sa {cmake-variable-QT_NO_COLLECT_BUILD_TREE_APK_DEPS}{QT_NO_COLLECT_BUILD_TREE_APK_DEPS}
+\sa {cmake-variable-QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS}{QT_NO_COLLECT_IMPORTED_TARGET_APK_DEPS}
*/