summaryrefslogtreecommitdiffstats
path: root/src/corelib/Qt6AndroidMacros.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/Qt6AndroidMacros.cmake')
-rw-r--r--src/corelib/Qt6AndroidMacros.cmake897
1 files changed, 729 insertions, 168 deletions
diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake
index 22e3ca614d..6218df1947 100644
--- a/src/corelib/Qt6AndroidMacros.cmake
+++ b/src/corelib/Qt6AndroidMacros.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generate deployment tool json
# Locate newest Android sdk build tools revision
@@ -17,6 +20,35 @@ function(_qt_internal_android_get_sdk_build_tools_revision out_var)
set(${out_var} "${android_build_tools_latest}" PARENT_SCOPE)
endfunction()
+# The function appends to the 'out_var' a 'json_property' that contains the 'tool' path. If 'tool'
+# target or its IMPORTED_LOCATION are not found the function displays warning, but is not failing
+# at the project configuring phase.
+function(_qt_internal_add_tool_to_android_deployment_settings out_var tool json_property target)
+ unset(tool_binary_path)
+ __qt_internal_get_tool_imported_location(tool_binary_path ${tool})
+ if("${tool_binary_path}" STREQUAL "")
+ # Fallback search for the tool in host bin and host libexec directories
+ find_program(tool_binary_path
+ NAMES ${tool} ${tool}.exe
+ PATHS
+ "${QT_HOST_PATH}/${QT6_HOST_INFO_BINDIR}"
+ "${QT_HOST_PATH}/${QT6_HOST_INFO_LIBEXECDIR}"
+ NO_DEFAULT_PATH
+ )
+ if(NOT tool_binary_path)
+ message(WARNING "Unable to locate ${tool}. Android package deployment of ${target}"
+ " target can be incomplete. Make sure the host Qt has ${tool} installed.")
+ return()
+ endif()
+ endif()
+
+ file(TO_CMAKE_PATH "${tool_binary_path}" tool_binary_path)
+ string(APPEND ${out_var}
+ " \"${json_property}\" : \"${tool_binary_path}\",\n")
+
+ set(${out_var} "${${out_var}}" PARENT_SCOPE)
+endfunction()
+
# Generate the deployment settings json file for a cmake target.
function(qt6_android_generate_deployment_settings target)
# Information extracted from mkspecs/features/android/android_deployment_settings.prf
@@ -41,6 +73,17 @@ function(qt6_android_generate_deployment_settings target)
_qt_is_android_generate_deployment_settings_called TRUE
)
+ get_target_property(android_executable_finalizer_called
+ ${target} _qt_android_executable_finalizer_called)
+
+ if(android_executable_finalizer_called)
+ # Don't show deprecation when called by our own function implementations.
+ else()
+ message(DEPRECATION
+ "Calling qt_android_generate_deployment_settings directly is deprecated since Qt 6.5. "
+ "Use qt_add_executable instead.")
+ endif()
+
get_target_property(target_type ${target} TYPE)
if (NOT "${target_type}" STREQUAL "MODULE_LIBRARY")
@@ -64,7 +107,7 @@ function(qt6_android_generate_deployment_settings target)
set(config_suffix "$<$<NOT:$<CONFIG:${first_config_type}>>:-$<CONFIG>>")
endif()
set(deploy_file
- "${target_binary_dir}/android-${target_output_name}-deployment-settings${config_suffix}.json")
+ "${target_binary_dir}/android-${target}-deployment-settings${config_suffix}.json")
set(file_contents "{\n")
# content begin
@@ -90,31 +133,7 @@ function(qt6_android_generate_deployment_settings target)
endif()
endif()
- set(abi_records "")
- get_target_property(qt_android_abis ${target} _qt_android_abis)
- if(NOT qt_android_abis)
- set(qt_android_abis "")
- endif()
- foreach(abi IN LISTS qt_android_abis)
- _qt_internal_get_android_abi_path(qt_abi_path ${abi})
- file(TO_CMAKE_PATH "${qt_abi_path}" qt_android_install_dir_native)
- list(APPEND abi_records "\"${abi}\": \"${qt_android_install_dir_native}\"")
- endforeach()
-
- # Required to build unit tests in developer build
- if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX)
- set(qt_android_install_dir "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}")
- else()
- set(qt_android_install_dir "${QT6_INSTALL_PREFIX}")
- endif()
- file(TO_CMAKE_PATH "${qt_android_install_dir}" qt_android_install_dir_native)
- list(APPEND abi_records "\"${CMAKE_ANDROID_ARCH_ABI}\": \"${qt_android_install_dir_native}\"")
-
- list(JOIN abi_records "," qt_android_install_dir_records)
- set(qt_android_install_dir_records "{${qt_android_install_dir_records}}")
-
- string(APPEND file_contents
- " \"qt\": ${qt_android_install_dir_records},\n")
+ _qt_internal_collect_qt_for_android_paths(file_contents)
# Android SDK path
file(TO_CMAKE_PATH "${ANDROID_SDK_ROOT}" android_sdk_root_native)
@@ -122,12 +141,16 @@ function(qt6_android_generate_deployment_settings target)
" \"sdk\": \"${android_sdk_root_native}\",\n")
# Android SDK Build Tools Revision
- get_target_property(android_sdk_build_tools ${target} QT_ANDROID_SDK_BUILD_TOOLS_REVISION)
- if (NOT android_sdk_build_tools)
- _qt_internal_android_get_sdk_build_tools_revision(android_sdk_build_tools)
- endif()
+ _qt_internal_android_get_sdk_build_tools_revision(android_sdk_build_tools)
+ set(android_sdk_build_tools_genex "")
+ string(APPEND android_sdk_build_tools_genex
+ "$<IF:$<BOOL:$<TARGET_PROPERTY:${target},QT_ANDROID_SDK_BUILD_TOOLS_REVISION>>,"
+ "$<TARGET_PROPERTY:${target},QT_ANDROID_SDK_BUILD_TOOLS_REVISION>,"
+ "${android_sdk_build_tools}"
+ ">"
+ )
string(APPEND file_contents
- " \"sdkBuildToolsRevision\": \"${android_sdk_build_tools}\",\n")
+ " \"sdkBuildToolsRevision\": \"${android_sdk_build_tools_genex}\",\n")
# Android NDK
file(TO_CMAKE_PATH "${CMAKE_ANDROID_NDK}" android_ndk_root_native)
@@ -150,6 +173,10 @@ function(qt6_android_generate_deployment_settings target)
string(APPEND file_contents
" \"ndk-host\": \"${ANDROID_NDK_HOST_SYSTEM_NAME}\",\n")
+ get_target_property(qt_android_abis ${target} _qt_android_abis)
+ if(NOT qt_android_abis)
+ set(qt_android_abis "")
+ endif()
set(architecture_record_list "")
foreach(abi IN LISTS qt_android_abis CMAKE_ANDROID_ARCH_ABI)
if(abi STREQUAL "x86")
@@ -170,16 +197,20 @@ function(qt6_android_generate_deployment_settings target)
" \"architectures\": { ${architecture_records} },\n")
# deployment dependencies
- _qt_internal_add_android_deployment_multi_value_property(file_contents "dependencies"
+ _qt_internal_add_android_deployment_multi_value_property(file_contents "deployment-dependencies"
${target} "QT_ANDROID_DEPLOYMENT_DEPENDENCIES" )
# Extra plugins
_qt_internal_add_android_deployment_multi_value_property(file_contents "android-extra-plugins"
- ${target} "QT_ANDROID_EXTRA_PLUGINS" )
+ ${target} "_qt_android_native_extra_plugins" )
# Extra libs
_qt_internal_add_android_deployment_multi_value_property(file_contents "android-extra-libs"
- ${target} "QT_ANDROID_EXTRA_LIBS" )
+ ${target} "_qt_android_native_extra_libs" )
+
+ # Alternative path to Qt libraries on target device
+ _qt_internal_add_android_deployment_property(file_contents "android-system-libs-prefix"
+ ${target} "QT_ANDROID_SYSTEM_LIBS_PREFIX")
# package source dir
_qt_internal_add_android_deployment_property(file_contents "android-package-source-directory"
@@ -201,25 +232,13 @@ function(qt6_android_generate_deployment_settings target)
_qt_internal_add_android_deployment_property(file_contents "android-target-sdk-version"
${target} "QT_ANDROID_TARGET_SDK_VERSION")
- # QML import paths
- if(NOT "${QT_QML_OUTPUT_DIRECTORY}" STREQUAL "")
- # Need to prepend the default qml module output directory to take precedence
- # over other qml import paths. By default QT_QML_OUTPUT_DIRECTORY is set to
- # ${CMAKE_BINARY_DIR}/android-qml for Android.
- get_target_property(native_qml_import_paths "${target}" _qt_native_qml_import_paths)
- list(PREPEND native_qml_import_paths "${QT_QML_OUTPUT_DIRECTORY}")
- set_property(TARGET "${target}" PROPERTY
- "_qt_native_qml_import_paths" "${native_qml_import_paths}")
- endif()
- _qt_internal_add_android_deployment_multi_value_property(file_contents "qml-import-paths"
- ${target} "_qt_native_qml_import_paths")
-
- # QML root paths
- file(TO_CMAKE_PATH "${target_source_dir}" native_target_source_dir)
- set_property(TARGET ${target} APPEND PROPERTY
- _qt_android_native_qml_root_paths "${native_target_source_dir}")
- _qt_internal_add_android_deployment_list_property(file_contents "qml-root-path"
- ${target} "_qt_android_native_qml_root_paths")
+ # should Qt shared libs be excluded from deployment
+ _qt_internal_add_android_deployment_property(file_contents "android-no-deploy-qt-libs"
+ ${target} "QT_ANDROID_NO_DEPLOY_QT_LIBS")
+
+ __qt_internal_collect_plugin_targets_from_dependencies("${target}" plugin_targets)
+ __qt_internal_collect_plugin_library_files("${target}" "${plugin_targets}" plugin_targets)
+ string(APPEND file_contents " \"android-deploy-plugins\":\"${plugin_targets}\",\n")
# App binary
string(APPEND file_contents
@@ -231,23 +250,15 @@ function(qt6_android_generate_deployment_settings target)
" \"android-application-arguments\": \"${QT_ANDROID_APPLICATION_ARGUMENTS}\",\n")
endif()
- # Override qmlimportscanner binary path
- set(qml_importscanner_binary_path "${QT_HOST_PATH}/${QT6_HOST_INFO_LIBEXECDIR}/qmlimportscanner")
- if (WIN32)
- string(APPEND qml_importscanner_binary_path ".exe")
+ if(COMMAND _qt_internal_generate_android_qml_deployment_settings)
+ _qt_internal_generate_android_qml_deployment_settings(file_contents ${target})
+ else()
+ string(APPEND file_contents
+ " \"qml-skip-import-scanning\": true,\n")
endif()
- file(TO_CMAKE_PATH "${qml_importscanner_binary_path}" qml_importscanner_binary_path_native)
- string(APPEND file_contents
- " \"qml-importscanner-binary\" : \"${qml_importscanner_binary_path_native}\",\n")
# Override rcc binary path
- set(rcc_binary_path "${QT_HOST_PATH}/${QT6_HOST_INFO_LIBEXECDIR}/rcc")
- if (WIN32)
- string(APPEND rcc_binary_path ".exe")
- endif()
- file(TO_CMAKE_PATH "${rcc_binary_path}" rcc_binary_path_native)
- string(APPEND file_contents
- " \"rcc-binary\" : \"${rcc_binary_path_native}\",\n")
+ _qt_internal_add_tool_to_android_deployment_settings(file_contents rcc "rcc-binary" "${target}")
# Extra prefix paths
foreach(prefix IN LISTS CMAKE_FIND_ROOT_PATH)
@@ -261,6 +272,12 @@ function(qt6_android_generate_deployment_settings target)
string(APPEND file_contents
" \"extraPrefixDirs\" : [ ${extra_prefix_list} ],\n")
+ # Create an empty target for the cases when we need to generate deployment setting but
+ # qt_finalize_project is never called.
+ if(NOT TARGET _qt_internal_apk_dependencies AND NOT QT_NO_COLLECT_BUILD_TREE_APK_DEPS)
+ add_custom_target(_qt_internal_apk_dependencies)
+ endif()
+
# Extra library paths that could be used as a dependency lookup path by androiddeployqt.
#
# Unlike 'extraPrefixDirs', the 'extraLibraryDirs' key doesn't expect the 'lib' subfolder
@@ -290,7 +307,7 @@ function(qt6_android_generate_deployment_settings target)
# content end
string(APPEND file_contents "}\n")
- file(GENERATE OUTPUT ${deploy_file} CONTENT ${file_contents})
+ file(GENERATE OUTPUT ${deploy_file} CONTENT "${file_contents}")
set_target_properties(${target}
PROPERTIES
@@ -305,6 +322,16 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endif()
function(qt6_android_apply_arch_suffix target)
+ get_target_property(called_from_qt_impl
+ ${target} _qt_android_apply_arch_suffix_called_from_qt_impl)
+ if(called_from_qt_impl)
+ # Don't show deprecation when called by our own function implementations.
+ else()
+ message(DEPRECATION
+ "Calling qt_android_apply_arch_suffix directly is deprecated since Qt 6.5. "
+ "Use qt_add_executable or qt_add_library instead.")
+ endif()
+
get_target_property(target_type ${target} TYPE)
if (target_type STREQUAL "SHARED_LIBRARY" OR target_type STREQUAL "MODULE_LIBRARY")
set_property(TARGET "${target}" PROPERTY SUFFIX "_${CMAKE_ANDROID_ARCH_ABI}.so")
@@ -321,21 +348,75 @@ endif()
# Add custom target to package the APK
function(qt6_android_add_apk_target target)
+ # Avoid calling qt6_android_add_apk_target twice
+ get_property(apk_targets GLOBAL PROPERTY _qt_apk_targets)
+ if("${target}" IN_LIST apk_targets)
+ return()
+ endif()
+
+ get_target_property(android_executable_finalizer_called
+ ${target} _qt_android_executable_finalizer_called)
+
+ if(android_executable_finalizer_called)
+ # Don't show deprecation when called by our own function implementations.
+ else()
+ message(DEPRECATION
+ "Calling qt_android_add_apk_target directly is deprecated since Qt 6.5. "
+ "Use qt_add_executable instead.")
+ endif()
+
get_target_property(deployment_file ${target} QT_ANDROID_DEPLOYMENT_SETTINGS_FILE)
if (NOT deployment_file)
message(FATAL_ERROR "Target ${target} is not a valid android executable target\n")
endif()
+ # Use genex to get path to the deployment settings, the above check only to confirm that
+ # qt6_android_add_apk_target is called on an android executable target.
+ string(JOIN "" deployment_file
+ "$<GENEX_EVAL:"
+ "$<TARGET_PROPERTY:${target},QT_ANDROID_DEPLOYMENT_SETTINGS_FILE>"
+ ">"
+ )
# Make global apk and aab targets depend on the current apk target.
+ if(TARGET aab)
+ add_dependencies(aab ${target}_make_aab)
+ endif()
if(TARGET apk)
add_dependencies(apk ${target}_make_apk)
- add_dependencies(aab ${target}_make_aab)
_qt_internal_create_global_apk_all_target_if_needed()
endif()
set(deployment_tool "${QT_HOST_PATH}/${QT6_HOST_INFO_BINDIR}/androiddeployqt")
+ # No need to use genex for the BINARY_DIR since it's read-only.
get_target_property(target_binary_dir ${target} BINARY_DIR)
- set(apk_final_dir "${target_binary_dir}/android-build")
+
+ if($CACHE{QT_USE_TARGET_ANDROID_BUILD_DIR})
+ set(apk_final_dir "${target_binary_dir}/android-build-${target}")
+ else()
+ if(QT_USE_TARGET_ANDROID_BUILD_DIR)
+ message(WARNING "QT_USE_TARGET_ANDROID_BUILD_DIR needs to be set in CACHE")
+ endif()
+
+ get_property(known_android_build GLOBAL PROPERTY _qt_internal_known_android_build_dir)
+ get_property(already_warned GLOBAL PROPERTY _qt_internal_already_warned_android_build_dir)
+ set(apk_final_dir "${target_binary_dir}/android-build")
+ if(NOT QT_SKIP_ANDROID_BUILD_DIR_CHECK AND "${apk_final_dir}" IN_LIST known_android_build
+ AND NOT "${apk_final_dir}" IN_LIST already_warned)
+ message(WARNING "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt contains multiple"
+ " Qt Android executable targets. This can lead to mixing of deployment artifacts"
+ " of targets defined there. Setting QT_USE_TARGET_ANDROID_BUILD_DIR=TRUE"
+ " allows building multiple executable targets within a single CMakeLists.txt."
+ " Note: This option is not supported by Qt Creator versions older than 13."
+ " Set QT_SKIP_ANDROID_BUILD_DIR_CHECK=TRUE to suppress this warning."
+ )
+ set_property(GLOBAL APPEND PROPERTY _qt_internal_already_warned_android_build_dir
+ "${apk_final_dir}")
+ else()
+ set_property(GLOBAL APPEND PROPERTY
+ _qt_internal_known_android_build_dir "${apk_final_dir}")
+ endif()
+ endif()
+
set(apk_file_name "${target}.apk")
set(dep_file_name "${target}.d")
set(apk_final_file_path "${apk_final_dir}/${apk_file_name}")
@@ -345,6 +426,10 @@ function(qt6_android_add_apk_target target)
set(extra_deps "")
+ if(QT_ENABLE_VERBOSE_DEPLOYMENT)
+ set(uses_terminal USES_TERMINAL)
+ endif()
+
# Plugins still might be added after creating the deployment targets.
if(NOT TARGET qt_internal_plugins)
add_custom_target(qt_internal_plugins)
@@ -356,14 +441,26 @@ function(qt6_android_add_apk_target target)
# in case DEPFILEs are not supported.
# Also the target is used to copy the library that belongs to ${target} when building multi-abi
# apk to the abi-specific directory.
+ _qt_internal_copy_file_if_different_command(copy_command
+ "$<TARGET_FILE:${target}>"
+ "${apk_final_dir}/${target_file_copy_relative_path}"
+ )
add_custom_target(${target}_prepare_apk_dir ALL
DEPENDS ${target} ${extra_deps}
- COMMAND ${CMAKE_COMMAND}
- -E copy_if_different $<TARGET_FILE:${target}>
- "${apk_final_dir}/${target_file_copy_relative_path}"
+ COMMAND ${copy_command}
COMMENT "Copying ${target} binary to apk folder"
+ ${uses_terminal}
)
+ set(sign_apk "")
+ if(QT_ANDROID_SIGN_APK)
+ set(sign_apk "--sign")
+ endif()
+ set(sign_aab "")
+ if(QT_ANDROID_SIGN_AAB)
+ set(sign_aab "--sign")
+ endif()
+
set(extra_args "")
if(QT_INTERNAL_NO_ANDROID_RCC_BUNDLE_CLEANUP)
list(APPEND extra_args "--no-rcc-bundle-cleanup")
@@ -372,14 +469,28 @@ function(qt6_android_add_apk_target target)
list(APPEND extra_args "--verbose")
endif()
- # The DEPFILE argument to add_custom_command is only available with Ninja or CMake>=3.20 and
- # make.
- set(has_depfile_support FALSE)
- if(CMAKE_GENERATOR MATCHES "Ninja" OR
- (CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles"))
- set(has_depfile_support TRUE)
+ if(QT_ANDROID_DEPLOY_RELEASE)
+ message(WARNING "QT_ANDROID_DEPLOY_RELEASE is not a valid Qt variable."
+ " Please set QT_ANDROID_DEPLOYMENT_TYPE to RELEASE instead.")
+ endif()
+ # Setting QT_ANDROID_DEPLOYMENT_TYPE to a value other than Release disables
+ # release package signing regardless of the build type.
+ if(QT_ANDROID_DEPLOYMENT_TYPE)
+ string(TOUPPER "${QT_ANDROID_DEPLOYMENT_TYPE}" deployment_type_upper)
+ if("${deployment_type_upper}" STREQUAL "RELEASE")
+ list(APPEND extra_args "--release")
+ endif()
+ elseif(NOT QT_BUILD_TESTS)
+ # Workaround for tests: do not set automatically --release flag if QT_BUILD_TESTS is set.
+ # Release package need to be signed. Signing is currently not supported by CI.
+ # What is more, also androidtestrunner is not working on release APKs,
+ # For example running "adb shell run-as" on release APK will finish with the error:
+ # run-as: Package '[PACKAGE-NAME]' is not debuggable
+ list(APPEND extra_args $<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>,$<CONFIG:MinSizeRel>>:--release>)
endif()
+ _qt_internal_check_depfile_support(has_depfile_support)
+
if(has_depfile_support)
cmake_policy(PUSH)
if(POLICY CMP0116)
@@ -405,9 +516,12 @@ function(qt6_android_add_apk_target target)
--depfile "${dep_file_path}"
--builddir "${relative_to_dir}"
${extra_args}
+ ${sign_apk}
COMMENT "Creating APK for ${target}"
DEPENDS "${target}" "${deployment_file}" ${extra_deps}
DEPFILE "${dep_file_path}"
+ VERBATIM
+ ${uses_terminal}
)
cmake_policy(POP)
@@ -421,7 +535,10 @@ function(qt6_android_add_apk_target target)
--output ${apk_final_dir}
--apk ${apk_final_file_path}
${extra_args}
+ ${sign_apk}
COMMENT "Creating APK for ${target}"
+ VERBATIM
+ ${uses_terminal}
)
endif()
@@ -435,8 +552,10 @@ function(qt6_android_add_apk_target target)
--output ${apk_final_dir}
--apk ${apk_final_file_path}
--aab
+ ${sign_aab}
${extra_args}
COMMENT "Creating AAB for ${target}"
+ ${uses_terminal}
)
if(QT_IS_ANDROID_MULTI_ABI_EXTERNAL_PROJECT)
@@ -453,6 +572,10 @@ function(qt6_android_add_apk_target target)
file(RELATIVE_PATH androiddeployqt_output_path "${CMAKE_BINARY_DIR}" "${apk_final_dir}")
set(androiddeployqt_output_path
"${QT_INTERNAL_ANDROID_MULTI_ABI_BINARY_DIR}/${androiddeployqt_output_path}")
+ _qt_internal_copy_file_if_different_command(copy_command
+ "$<TARGET_FILE:${target}>"
+ "${androiddeployqt_output_path}/${target_file_copy_relative_path}"
+ )
if(has_depfile_support)
set(deploy_android_deps_dir "${apk_final_dir}/${target}_deploy_android")
set(timestamp_file "${deploy_android_deps_dir}/timestamp")
@@ -461,9 +584,7 @@ function(qt6_android_add_apk_target target)
DEPENDS ${target} ${extra_deps}
COMMAND ${CMAKE_COMMAND} -E make_directory "${deploy_android_deps_dir}"
COMMAND ${CMAKE_COMMAND} -E touch "${timestamp_file}"
- COMMAND ${CMAKE_COMMAND}
- -E copy_if_different $<TARGET_FILE:${target}>
- "${androiddeployqt_output_path}/${target_file_copy_relative_path}"
+ COMMAND ${copy_command}
COMMAND ${deployment_tool}
--input ${deployment_file}
--output ${androiddeployqt_output_path}
@@ -473,27 +594,29 @@ function(qt6_android_add_apk_target target)
--builddir "${CMAKE_BINARY_DIR}"
COMMENT "Resolving ${CMAKE_ANDROID_ARCH_ABI} dependencies for the ${target} APK"
DEPFILE "${dep_file}"
+ VERBATIM
+ ${uses_terminal}
)
add_custom_target(qt_internal_${target}_copy_apk_dependencies
DEPENDS "${timestamp_file}")
else()
add_custom_target(qt_internal_${target}_copy_apk_dependencies
DEPENDS ${target} ${extra_deps}
- COMMAND ${CMAKE_COMMAND}
- -E copy_if_different $<TARGET_FILE:${target}>
- "${androiddeployqt_output_path}/${target_file_copy_relative_path}"
+ COMMAND ${copy_command}
COMMAND ${deployment_tool}
--input ${deployment_file}
--output ${androiddeployqt_output_path}
--copy-dependencies-only
${extra_args}
COMMENT "Resolving ${CMAKE_ANDROID_ARCH_ABI} dependencies for the ${target} APK"
+ ${uses_terminal}
)
endif()
endif()
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)
@@ -519,8 +642,8 @@ endfunction()
# The function collects all known non-imported shared libraries that are created in the build tree.
# It uses the CMake DEFER CALL feature if the CMAKE_VERSION is greater
-# than or equal to 3.18.
-# Note: Users that use cmake version less that 3.18 need to call qt_finalize_project
+# than or equal to 3.19.
+# Note: Users that use cmake version less that 3.19 need to call qt_finalize_project
# in the end of a project's top-level CMakeLists.txt.
function(_qt_internal_collect_apk_dependencies_defer)
# User opted-out the functionality
@@ -534,13 +657,14 @@ function(_qt_internal_collect_apk_dependencies_defer)
endif()
set_property(GLOBAL PROPERTY _qt_is_collect_apk_dependencies_defer_called TRUE)
- if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18")
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.19")
cmake_language(EVAL CODE "cmake_language(DEFER DIRECTORY \"${CMAKE_SOURCE_DIR}\"
CALL _qt_internal_collect_apk_dependencies)")
else()
# User don't want to see the warning
if(NOT QT_NO_WARN_BUILD_TREE_APK_DEPS)
- message(WARNING "CMake version you use is less than 3.18. APK dependencies, that are a"
+ message(WARNING
+ "The CMake version you use is less than 3.19. APK dependencies, that are a"
" part of the project tree, might not be collected correctly."
" Please call qt_finalize_project in the end of a project's top-level"
" CMakeLists.txt file to make sure that all the APK dependencies are"
@@ -551,8 +675,8 @@ function(_qt_internal_collect_apk_dependencies_defer)
endif()
endfunction()
-# The function collects shared libraries from the build system tree, that might be dependencies for
-# the main apk targets.
+# The function collects project-built shared libraries that might be dependencies for
+# the main apk targets. It stores their locations in a global custom target property.
function(_qt_internal_collect_apk_dependencies)
# User opted-out the functionality
if(QT_NO_COLLECT_BUILD_TREE_APK_DEPS)
@@ -567,7 +691,9 @@ function(_qt_internal_collect_apk_dependencies)
get_property(apk_targets GLOBAL PROPERTY _qt_apk_targets)
- _qt_internal_collect_buildsystem_shared_libraries(libs "${CMAKE_SOURCE_DIR}")
+ _qt_internal_collect_buildsystem_targets(libs
+ "${CMAKE_SOURCE_DIR}" INCLUDE SHARED_LIBRARY MODULE_LIBRARY)
+ list(REMOVE_DUPLICATES libs)
if(NOT TARGET qt_internal_plugins)
add_custom_target(qt_internal_plugins)
@@ -595,24 +721,77 @@ function(_qt_internal_collect_apk_dependencies)
)
endfunction()
-# The function recursively goes through the project subfolders and collects targets that supposed to
-# be shared libraries of any kind.
-function(_qt_internal_collect_buildsystem_shared_libraries out_var subdir)
+# 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(buildsystem_targets DIRECTORY ${subdir} BUILDSYSTEM_TARGETS)
- foreach(buildsystem_target IN LISTS buildsystem_targets)
- if(buildsystem_target AND TARGET ${buildsystem_target})
- get_target_property(target_type ${buildsystem_target} TYPE)
- if(target_type STREQUAL "SHARED_LIBRARY" OR target_type STREQUAL "MODULE_LIBRARY")
- list(APPEND result ${buildsystem_target})
+
+ 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(subdirs DIRECTORY "${subdir}" SUBDIRECTORIES)
- foreach(dir IN LISTS subdirs)
- _qt_internal_collect_buildsystem_shared_libraries(result_inner "${dir}")
- 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()
@@ -653,7 +832,7 @@ endfunction()
# The function converts the target property to a json record and appends it to the output
# variable.
function(_qt_internal_add_android_deployment_property out_var json_key target property)
- set(property_genex "$<TARGET_PROPERTY:${target},${property}>")
+ set(property_genex "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},${property}>>")
string(APPEND ${out_var}
"$<$<BOOL:${property_genex}>:"
" \"${json_key}\": \"${property_genex}\"\,\n"
@@ -697,17 +876,20 @@ function(_qt_internal_add_android_deployment_list_property out_var json_key)
endif()
set(property_genex
- "$<TARGET_PROPERTY:${target},${property}>"
+ "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},${property}>>"
)
set(add_quote_genex
"$<$<BOOL:${property_genex}>:\">"
)
+
+ # Add comma only if next property genex contains non-empty value.
+ set(add_comma_genex "$<$<BOOL:${property_genex}>:${add_comma_genex}>")
string(JOIN "" list_join_genex
"${list_join_genex}"
"${add_comma_genex}${add_quote_genex}"
"$<JOIN:"
- "$<GENEX_EVAL:${property_genex}>,"
- "\",\""
+ "${property_genex},"
+ "\"$<COMMA>\""
">"
"${add_quote_genex}"
)
@@ -725,12 +907,12 @@ endfunction()
# "android-extra-plugins": "plugin1,plugin2",
function(_qt_internal_add_android_deployment_multi_value_property out_var json_key target property)
set(property_genex
- "$<TARGET_PROPERTY:${target},${property}>"
+ "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},${property}>>"
)
string(JOIN "" list_join_genex
"$<JOIN:"
- "$<GENEX_EVAL:${property_genex}>,"
- ","
+ "${property_genex},"
+ "$<COMMA>"
">"
)
string(APPEND ${out_var}
@@ -746,20 +928,102 @@ endfunction()
# It doesn't overwrite public properties, but instead writes formatted values to internal
# properties.
function(_qt_internal_android_format_deployment_paths target)
- _qt_internal_android_format_deployment_path_property(${target}
- QT_QML_IMPORT_PATH _qt_native_qml_import_paths)
+ if(QT_BUILD_STANDALONE_TESTS OR QT_BUILDING_QT OR QT_INTERNAL_IS_STANDALONE_TEST)
+ set(android_deployment_paths_policy NEW)
+ else()
+ set(policy_path_properties
+ QT_QML_IMPORT_PATH
+ QT_QML_ROOT_PATH
+ QT_ANDROID_PACKAGE_SOURCE_DIR
+ QT_ANDROID_EXTRA_PLUGINS
+ QT_ANDROID_EXTRA_LIBS
+ )
- _qt_internal_android_format_deployment_path_property(${target}
- QT_QML_ROOT_PATH _qt_android_native_qml_root_paths)
+ # Check if any of paths contains the value and stop the evaluation if all properties are
+ # empty or -NOTFOUND
+ set(has_android_paths FALSE)
+ foreach(prop_name IN LISTS policy_path_properties)
+ get_target_property(prop_value ${target} ${prop_name})
+ if(prop_value)
+ set(has_android_paths TRUE)
+ break()
+ endif()
+ endforeach()
+ if(has_android_paths)
+ __qt_internal_setup_policy(QTP0002 "6.6.0"
+ "Target properties that specify android-specific paths may contain generator\
+ expressions but they must evaluate to valid JSON strings.\
+ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0002.html for policy details."
+ )
+ qt6_policy(GET QTP0002 android_deployment_paths_policy)
+ endif()
+ endif()
+ if(android_deployment_paths_policy STREQUAL "NEW")
+ # When building standalone tests or Qt itself we obligate developers to not use
+ # windows paths when setting QT_* properties below, so their values are used as is when
+ # generating deployment settings.
+ string(JOIN "" qml_root_path_genex
+ "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},QT_QML_ROOT_PATH>>"
+ "$<"
+ "$<AND:"
+ "$<BOOL:$<GENEX_EVAL:$<TARGET_PROPERTY:${target},QT_QML_ROOT_PATH>>>,"
+ "$<BOOL:$<GENEX_EVAL:$<TARGET_PROPERTY:${target},_qt_internal_qml_root_path>>>"
+ ">:;"
+ ">"
+ "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},_qt_internal_qml_root_path>>"
+ )
+ set_target_properties(${target} PROPERTIES
+ _qt_native_qml_import_paths
+ "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},QT_QML_IMPORT_PATH>>"
+ _qt_android_native_qml_root_paths
+ "${qml_root_path_genex}"
+ _qt_android_native_package_source_dir
+ "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},QT_ANDROID_PACKAGE_SOURCE_DIR>>"
+ _qt_android_native_extra_plugins
+ "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},QT_ANDROID_EXTRA_PLUGINS>>"
+ _qt_android_native_extra_libs
+ "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},QT_ANDROID_EXTRA_LIBS>>"
+ )
+ else()
+ # User projects still may use windows paths inside the QT_* properties below, with
+ # obligation to run the finalizer code.
+ _qt_internal_android_format_deployment_path_property(${target}
+ QT_QML_IMPORT_PATH _qt_native_qml_import_paths)
+
+ _qt_internal_android_format_deployment_path_property(${target}
+ QT_QML_ROOT_PATH _qt_android_native_qml_root_paths)
+
+ _qt_internal_android_format_deployment_path_property(${target}
+ _qt_internal_qml_root_path _qt_android_native_qml_root_paths APPEND)
- _qt_internal_android_format_deployment_path_property(${target}
- QT_ANDROID_PACKAGE_SOURCE_DIR _qt_android_native_package_source_dir)
+ _qt_internal_android_format_deployment_path_property(${target}
+ QT_ANDROID_PACKAGE_SOURCE_DIR _qt_android_native_package_source_dir)
+
+ _qt_internal_android_format_deployment_path_property(${target}
+ QT_ANDROID_EXTRA_PLUGINS _qt_android_native_extra_plugins)
+
+ _qt_internal_android_format_deployment_path_property(${target}
+ QT_ANDROID_EXTRA_LIBS _qt_android_native_extra_libs)
+ endif()
endfunction()
# The function converts the value of target property to JSON compatible path and writes the
# result to out_property. Property might be either single value, semicolon separated list or system
# path spec.
+# The APPEND argument controls the property is set. The argument should be added after all
+# the required arguments.
function(_qt_internal_android_format_deployment_path_property target property out_property)
+ set(should_append "")
+ if(ARGC EQUAL 4)
+ if("${ARGV3}" STREQUAL "APPEND")
+ set(should_append APPEND)
+ else()
+ message(FATAL_ERROR "Unexpected argument ${ARGV3}")
+ endif()
+ elseif(ARGC GREATER 4)
+ message(FATAL_ERROR "Unexpected arguments ${ARGN}")
+ endif()
+
get_target_property(_paths ${target} ${property})
if(_paths)
set(native_paths "")
@@ -767,7 +1031,7 @@ function(_qt_internal_android_format_deployment_path_property target property ou
file(TO_CMAKE_PATH "${_path}" _path)
list(APPEND native_paths "${_path}")
endforeach()
- set_target_properties(${target} PROPERTIES
+ set_property(TARGET ${target} ${should_append} PROPERTY
${out_property} "${native_paths}")
endif()
endfunction()
@@ -786,8 +1050,15 @@ endif()
# |__ android_armv7
# |__ android_x86
# |__ android_x86_64
-function(_qt_internal_get_android_abi_path out_path abi)
- if(DEFINED QT_PATH_ANDROID_ABI_${abi})
+function(_qt_internal_get_android_abi_prefix_path out_path abi)
+ if(CMAKE_ANDROID_ARCH_ABI STREQUAL abi)
+ # Required to build unit tests in developer build
+ if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX)
+ set(${out_path} "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}")
+ else()
+ set(${out_path} "${QT6_INSTALL_PREFIX}")
+ endif()
+ elseif(DEFINED QT_PATH_ANDROID_ABI_${abi})
get_filename_component(${out_path} "${QT_PATH_ANDROID_ABI_${abi}}" ABSOLUTE)
else()
# Map the ABI value to the Qt for Android folder.
@@ -807,9 +1078,87 @@ function(_qt_internal_get_android_abi_path out_path abi)
set(${out_path} "${${out_path}}" PARENT_SCOPE)
endfunction()
-# The function collects list of existing Qt for Android using _qt_internal_get_android_abi_path
-# and pre-defined set of known Android ABIs. The result is written to QT_DEFAULT_ANDROID_ABIS
-# cache variable.
+function(_qt_internal_get_android_abi_cmake_dir_path out_path abi)
+ if(DEFINED QT_ANDROID_PATH_CMAKE_DIR_${abi})
+ set(cmake_dir "${QT_ANDROID_PATH_CMAKE_DIR_${abi}}")
+ else()
+ _qt_internal_get_android_abi_prefix_path(prefix_path ${abi})
+ if((PROJECT_NAME STREQUAL "QtBase" OR QT_SUPERBUILD) AND QT_BUILDING_QT AND
+ NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_INTERNAL_IS_STANDALONE_TEST)
+ set(cmake_dir "${QT_CONFIG_BUILD_DIR}")
+ else()
+ string(TOUPPER "${QT_CMAKE_EXPORT_NAMESPACE}" export_namespace_upper)
+ set(cmake_dir "${prefix_path}/${${export_namespace_upper}_INSTALL_LIBS}/cmake")
+ endif()
+ endif()
+
+ set(${out_path} "${cmake_dir}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_get_android_abi_toolchain_path out_path abi)
+ set(toolchain_path "${QT_CMAKE_EXPORT_NAMESPACE}/qt.toolchain.cmake")
+ _qt_internal_get_android_abi_cmake_dir_path(cmake_dir ${abi})
+ get_filename_component(toolchain_path
+ "${cmake_dir}/${toolchain_path}" ABSOLUTE)
+ set(${out_path} "${toolchain_path}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_get_android_abi_subdir_path out_path subdir abi)
+ set(install_paths_path "${QT_CMAKE_EXPORT_NAMESPACE}/QtInstallPaths.cmake")
+ _qt_internal_get_android_abi_cmake_dir_path(cmake_dir ${abi})
+ include("${cmake_dir}/${install_paths_path}")
+ set(${out_path} "${${subdir}}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_collect_qt_for_android_paths out_var)
+ get_target_property(qt_android_abis ${target} _qt_android_abis)
+ if(NOT qt_android_abis)
+ set(qt_android_abis "")
+ endif()
+
+ set(custom_qt_paths data libexecs libs plugins qml)
+ foreach(type IN ITEMS prefix ${custom_qt_paths})
+ set(${type}_records "")
+ endforeach()
+
+ foreach(abi IN LISTS qt_android_abis CMAKE_ANDROID_ARCH_ABI)
+ _qt_internal_get_android_abi_prefix_path(qt_abi_prefix_path ${abi})
+ file(TO_CMAKE_PATH "${qt_abi_prefix_path}" qt_abi_prefix_path)
+ get_filename_component(qt_abi_prefix_path "${qt_abi_prefix_path}" ABSOLUTE)
+ list(APPEND prefix_records " \"${abi}\": \"${qt_abi_prefix_path}\"")
+ foreach(type IN ITEMS ${custom_qt_paths})
+ string(TOUPPER "${type}" upper_case_type)
+ _qt_internal_get_android_abi_subdir_path(qt_abi_path
+ QT6_INSTALL_${upper_case_type} ${abi})
+ list(APPEND ${type}_records
+ " \"${abi}\": \"${qt_abi_path}\"")
+ endforeach()
+ endforeach()
+
+ foreach(type IN ITEMS prefix ${custom_qt_paths})
+ list(JOIN ${type}_records ",\n" ${type}_records_string)
+ set(${type}_records_string "{\n${${type}_records_string}\n }")
+ endforeach()
+
+ string(APPEND ${out_var}
+ " \"qt\": ${prefix_records_string},\n")
+ string(APPEND ${out_var}
+ " \"qtDataDirectory\": ${data_records_string},\n")
+ string(APPEND ${out_var}
+ " \"qtLibExecsDirectory\": ${libexecs_records_string},\n")
+ string(APPEND ${out_var}
+ " \"qtLibsDirectory\": ${libs_records_string},\n")
+ string(APPEND ${out_var}
+ " \"qtPluginsDirectory\": ${plugins_records_string},\n")
+ string(APPEND ${out_var}
+ " \"qtQmlDirectory\": ${qml_records_string},\n")
+
+ set(${out_var} "${${out_var}}" PARENT_SCOPE)
+endfunction()
+
+# The function collects list of existing Qt for Android using
+# _qt_internal_get_android_abi_prefix_path and pre-defined set of known Android ABIs. The result is
+# written to QT_DEFAULT_ANDROID_ABIS cache variable.
# Note that QT_DEFAULT_ANDROID_ABIS is not intended to be set outside the function and will be
# rewritten.
function(_qt_internal_collect_default_android_abis)
@@ -817,9 +1166,9 @@ function(_qt_internal_collect_default_android_abis)
set(default_abis)
foreach(abi IN LISTS known_android_abis)
- _qt_internal_get_android_abi_path(qt_abi_path ${abi})
+ _qt_internal_get_android_abi_toolchain_path(qt_abi_toolchain_path ${abi})
# It's expected that Qt for Android contains ABI specific toolchain file.
- if(EXISTS "${qt_abi_path}/lib/cmake/${QT_CMAKE_EXPORT_NAMESPACE}/qt.toolchain.cmake"
+ if(EXISTS "${qt_abi_toolchain_path}"
OR CMAKE_ANDROID_ARCH_ABI STREQUAL abi)
list(APPEND default_abis ${abi})
endif()
@@ -835,6 +1184,91 @@ function(_qt_internal_collect_default_android_abis)
)
endfunction()
+# Returns a path to the timestamp file for the specific step of the multi-ABI Android project
+function(_qt_internal_get_android_abi_step_stampfile out project abi step)
+ get_target_property(build_dir ${project} _qt_android_build_directory)
+ get_property(is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+ if(is_multi)
+ set(${out} "${build_dir}/$<CONFIG>/${project}_${step}_stamp" PARENT_SCOPE)
+ else()
+ set(${out} "${build_dir}/${project}_${step}_stamp" PARENT_SCOPE)
+ endif()
+endfunction()
+
+# Creates the multi-ABI Android projects and assigns the JOB_POOL to them if it's possible
+function(_qt_internal_add_android_abi_project project abi)
+ add_custom_target(${project})
+
+ set(build_dir "${CMAKE_BINARY_DIR}/android_abi_builds/${abi}")
+ set_target_properties(${project} PROPERTIES
+ _qt_android_build_directory "${build_dir}"
+ )
+
+ file(MAKE_DIRECTORY "${build_dir}")
+ if(CMAKE_GENERATOR MATCHES "^Ninja")
+ set_property(GLOBAL APPEND PROPERTY JOB_POOLS _qt_android_${project}_pool=1)
+ endif()
+endfunction()
+
+# Adds the custom build step to the multi-ABI Android project
+function(_qt_internal_add_android_abi_step project abi step)
+ cmake_parse_arguments(arg "" "" "COMMAND;DEPENDS" ${ARGV})
+
+ if(NOT arg_COMMAND)
+ message(FATAL_ERROR "COMMAND is not set for ${project} step ${step} Android ABI ${abi}.")
+ endif()
+
+ set(dep_stamps "")
+ foreach(dep ${arg_DEPENDS})
+ _qt_internal_get_android_abi_step_stampfile(stamp ${project} ${abi} ${dep})
+ list(APPEND dep_stamps "${stamp}")
+ endforeach()
+
+ get_target_property(build_dir ${project} _qt_android_build_directory)
+
+ if(CMAKE_GENERATOR MATCHES "^Ninja")
+ set(add_to_pool JOB_POOL _qt_android_${project}_pool)
+ else()
+ set(add_to_pool "")
+ endif()
+
+ _qt_internal_get_android_abi_step_stampfile(stamp ${project} ${abi} ${step})
+ add_custom_command(OUTPUT "${stamp}"
+ COMMAND ${arg_COMMAND}
+ COMMAND "${CMAKE_COMMAND}" -E touch "${stamp}"
+ ${add_to_pool}
+ DEPENDS
+ ${dep_stamps}
+ WORKING_DIRECTORY
+ "${build_dir}"
+ VERBATIM
+ )
+ add_custom_target("${project}_${step}" DEPENDS "${stamp}")
+
+ get_target_property(known_steps ${project} _qt_android_abi_steps)
+ if(NOT CMAKE_GENERATOR MATCHES "^Ninja")
+ if(NOT QT_NO_WARN_ANDROID_MULTI_ABI_GENERATOR)
+ get_property(is_warned GLOBAL PROPERTY _qt_internal_warn_android_multi_abi_generator)
+ if(NOT is_warned)
+ set_property(GLOBAL PROPERTY _qt_internal_warn_android_multi_abi_generator TRUE)
+ message(WARNING "Building Multi-ABI Qt projects with the '${CMAKE_GENERATOR}'"
+ " generator has limitations. All targets from non-main ABI will be built"
+ " unconditionally. Please use the 'Ninja' or 'Ninja Multi-config' generators"
+ " with ninja build instead. Set QT_NO_WARN_ANDROID_MULTI_ABI_GENERATOR to"
+ " 'TRUE' to suppress this warning."
+ )
+ endif()
+ endif()
+ if(known_steps)
+ list(GET known_steps 0 first)
+ add_dependencies(${first} ${project}_${step})
+ endif()
+ endif()
+
+ list(PREPEND known_steps ${project}_${step})
+ set_target_properties(${project} PROPERTIES _qt_android_abi_steps "${known_steps}")
+endfunction()
+
# The function configures external projects for ABIs that target packages need to build with.
# Each target adds build step to the external project that is linked to the
# qt_internal_android_${abi}-${target}_build target in the primary ABI build tree.
@@ -866,10 +1300,84 @@ function(_qt_internal_configure_android_multiabi_target target)
)
endif()
+ get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
+ if(is_multi_config)
+ list(JOIN CMAKE_CONFIGURATION_TYPES "$<SEMICOLON>" escaped_configuration_types)
+ set(config_arg "-DCMAKE_CONFIGURATION_TYPES=${escaped_configuration_types}")
+ else()
+ set(config_arg "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
+ endif()
+
+ unset(extra_cmake_args)
+
+ # The flag is needed when building qt standalone tests only to avoid building
+ # qt repo itself
+ if(QT_BUILD_STANDALONE_TESTS)
+ list(APPEND extra_cmake_args "-DQT_BUILD_STANDALONE_TESTS=ON")
+ endif()
+
+ if(NOT QT_ADDITIONAL_PACKAGES_PREFIX_PATH STREQUAL "")
+ list(JOIN QT_ADDITIONAL_PACKAGES_PREFIX_PATH "$<SEMICOLON>" escaped_packages_prefix_path)
+ list(APPEND extra_cmake_args
+ "-DQT_ADDITIONAL_PACKAGES_PREFIX_PATH=${escaped_packages_prefix_path}")
+ endif()
+
+ if(NOT QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH STREQUAL "")
+ list(JOIN QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH "$<SEMICOLON>"
+ escaped_host_packages_prefix_path)
+ list(APPEND extra_cmake_args
+ "-DQT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH=${escaped_host_packages_prefix_path}")
+ endif()
+
+ if(ANDROID_SDK_ROOT)
+ list(APPEND extra_cmake_args "-DANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}")
+ endif()
+
+ # ANDROID_NDK_ROOT is invented by Qt and is what the qt toolchain file expects
+ if(ANDROID_NDK_ROOT)
+ list(APPEND extra_cmake_args "-DANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}")
+
+ # ANDROID_NDK is passed by Qt Creator and is also present in the android toolchain file.
+ elseif(ANDROID_NDK)
+ list(APPEND extra_cmake_args "-DANDROID_NDK_ROOT=${ANDROID_NDK}")
+ endif()
+
+ if(DEFINED QT_NO_PACKAGE_VERSION_CHECK)
+ list(APPEND extra_cmake_args "-DQT_NO_PACKAGE_VERSION_CHECK=${QT_NO_PACKAGE_VERSION_CHECK}")
+ endif()
+
+ if(DEFINED QT_HOST_PATH_CMAKE_DIR)
+ list(APPEND extra_cmake_args "-DQT_HOST_PATH_CMAKE_DIR=${QT_HOST_PATH_CMAKE_DIR}")
+ endif()
+
+ if(CMAKE_MAKE_PROGRAM)
+ list(APPEND extra_cmake_args "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}")
+ endif()
+
+ if(CMAKE_C_COMPILER_LAUNCHER)
+ list(JOIN CMAKE_C_COMPILER_LAUNCHER "$<SEMICOLON>"
+ compiler_launcher)
+ list(APPEND extra_cmake_args
+ "-DCMAKE_C_COMPILER_LAUNCHER=${compiler_launcher}")
+ endif()
+
+ if(CMAKE_CXX_COMPILER_LAUNCHER)
+ list(JOIN CMAKE_CXX_COMPILER_LAUNCHER "$<SEMICOLON>"
+ compiler_launcher)
+ list(APPEND extra_cmake_args
+ "-DCMAKE_CXX_COMPILER_LAUNCHER=${compiler_launcher}")
+ endif()
+
+ unset(user_cmake_args)
+ foreach(var IN LISTS QT_ANDROID_MULTI_ABI_FORWARD_VARS)
+ string(REPLACE ";" "$<SEMICOLON>" var_value "${${var}}")
+ list(APPEND user_cmake_args "-D${var}=${var_value}")
+ endforeach()
+
set(missing_qt_abi_toolchains "")
+ set(previous_copy_apk_dependencies_target ${target})
# Create external projects for each android ABI except the main one.
list(REMOVE_ITEM android_abis "${CMAKE_ANDROID_ARCH_ABI}")
- include(ExternalProject)
foreach(abi IN ITEMS ${android_abis})
if(NOT "${abi}" IN_LIST QT_DEFAULT_ANDROID_ABIS)
list(APPEND missing_qt_abi_toolchains ${abi})
@@ -877,57 +1385,68 @@ function(_qt_internal_configure_android_multiabi_target target)
continue()
endif()
- get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
- if(is_multi_config)
- list(JOIN CMAKE_CONFIGURATION_TYPES "$<SEMICOLON>" escaped_configuration_types)
- set(config_arg "-DCMAKE_CONFIGURATION_TYPES=${escaped_configuration_types}")
- else()
- set(config_arg "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
- endif()
-
- # The flag is needed when building qt standalone tests only to avoid building
- # qt repo itself
- if(QT_BUILD_STANDALONE_TESTS)
- list(APPEND extra_cmake_args "-DQT_BUILD_STANDALONE_TESTS=ON")
- endif()
-
- set(android_abi_build_dir "${CMAKE_BINARY_DIR}/android_abi_builds/${abi}")
get_property(abi_external_projects GLOBAL
PROPERTY _qt_internal_abi_external_projects)
if(NOT abi_external_projects
OR NOT "qt_internal_android_${abi}" IN_LIST abi_external_projects)
- _qt_internal_get_android_abi_path(qt_abi_path ${abi})
- set(qt_abi_toolchain_path
- "${qt_abi_path}/lib/cmake/${QT_CMAKE_EXPORT_NAMESPACE}/qt.toolchain.cmake")
- ExternalProject_Add("qt_internal_android_${abi}"
- SOURCE_DIR "${CMAKE_SOURCE_DIR}"
- BINARY_DIR "${android_abi_build_dir}"
- CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${qt_abi_toolchain_path}"
+ _qt_internal_add_android_abi_project(qt_internal_android_${abi} ${abi})
+
+ get_target_property(android_abi_build_dir qt_internal_android_${abi}
+ _qt_android_build_directory)
+ _qt_internal_get_android_abi_toolchain_path(qt_abi_toolchain_path ${abi})
+ _qt_internal_add_android_abi_step(qt_internal_android_${abi} ${abi} configure
+ COMMAND
+ "${CMAKE_COMMAND}"
+ "-G${CMAKE_GENERATOR}"
+ "-DCMAKE_TOOLCHAIN_FILE=${qt_abi_toolchain_path}"
+ "-DQT_HOST_PATH=${QT_HOST_PATH}"
"-DQT_IS_ANDROID_MULTI_ABI_EXTERNAL_PROJECT=ON"
"-DQT_INTERNAL_ANDROID_MULTI_ABI_BINARY_DIR=${CMAKE_BINARY_DIR}"
"${config_arg}"
"${extra_cmake_args}"
- EXCLUDE_FROM_ALL TRUE
- BUILD_COMMAND "" # avoid top-level build of external project
+ "${user_cmake_args}"
+ "-B" "${android_abi_build_dir}"
+ "-S" "${CMAKE_SOURCE_DIR}"
)
set_property(GLOBAL APPEND PROPERTY
_qt_internal_abi_external_projects "qt_internal_android_${abi}")
endif()
- ExternalProject_Add_Step("qt_internal_android_${abi}"
- "${target}_build"
- DEPENDEES configure
- # TODO: Remove this when the step will depend on DEPFILE generated by
- # androiddeployqt for the ${target}.
- ALWAYS TRUE
- EXCLUDE_FROM_MAIN TRUE
- COMMAND "${CMAKE_COMMAND}"
- "--build" "${android_abi_build_dir}"
- "--config" "$<CONFIG>"
- "--target" "qt_internal_${target}_copy_apk_dependencies"
+
+ get_target_property(android_abi_build_dir qt_internal_android_${abi}
+ _qt_android_build_directory)
+ _qt_internal_add_android_abi_step(qt_internal_android_${abi} ${abi} ${target}_build
+ DEPENDS
+ configure
+ COMMAND
+ "${CMAKE_COMMAND}"
+ --build "${android_abi_build_dir}"
+ --config $<CONFIG>
+ --target ${target}
+ )
+ add_dependencies(${target} "qt_internal_android_${abi}_${target}_build")
+
+ _qt_internal_add_android_abi_step(qt_internal_android_${abi} ${abi}
+ ${target}_copy_apk_dependencies
+ DEPENDS
+ ${target}_build
+ COMMAND
+ "${CMAKE_COMMAND}"
+ --build "${android_abi_build_dir}"
+ --config $<CONFIG>
+ --target qt_internal_${target}_copy_apk_dependencies
)
- ExternalProject_Add_StepTargets("qt_internal_android_${abi}"
- "${target}_build")
- add_dependencies(${target} "qt_internal_android_${abi}-${target}_build")
+ set(external_project_copy_target
+ "qt_internal_android_${abi}_${target}_copy_apk_dependencies")
+
+ # Need to build dependency chain between the
+ # qt_internal_android_${abi}-${target}_copy_apk_dependencies targets for all ABI's, to
+ # prevent parallel execution of androiddeployqt processes. We cannot use Ninja job pools
+ # here because it's not possible to define job pool for the step target in ExternalProject.
+ # All tricks with interlayer targets don't work, because we only can bind interlayer target
+ # to the job pool, but its dependencies can still be built in parallel.
+ add_dependencies(${previous_copy_apk_dependencies_target}
+ "${external_project_copy_target}")
+ set(previous_copy_apk_dependencies_target "${external_project_copy_target}")
endforeach()
if(missing_qt_abi_toolchains)
@@ -935,14 +1454,16 @@ function(_qt_internal_configure_android_multiabi_target target)
message(FATAL_ERROR "Cannot find toolchain files for the manually specified Android"
" ABIs: ${missing_qt_abi_toolchains_string}"
"\nNote that you also may manually specify the path to the required Qt for"
- " Android ABI using QT_PATH_ANDROID_ABI_<abi> CMake variable.\n")
+ " Android ABI using QT_PATH_ANDROID_ABI_<abi> CMake variable with the value"
+ " of the installation prefix, and QT_ANDROID_PATH_CMAKE_DIR_<abi> with"
+ " the location of the cmake directory for that ABI.\n")
endif()
list(JOIN android_abis ", " android_abis_string)
if(android_abis_string)
- set(android_abis_string "${CMAKE_ANDROID_ARCH_ABI}(default), ${android_abis_string}")
+ set(android_abis_string "${CMAKE_ANDROID_ARCH_ABI} (default), ${android_abis_string}")
else()
- set(android_abis_string "${CMAKE_ANDROID_ARCH_ABI}(default)")
+ set(android_abis_string "${CMAKE_ANDROID_ARCH_ABI} (default)")
endif()
if(NOT QT_NO_ANDROID_ABI_STATUS_MESSAGE)
message(STATUS "Configuring '${target}' for the following Android ABIs:"
@@ -950,3 +1471,43 @@ function(_qt_internal_configure_android_multiabi_target target)
endif()
set_target_properties(${target} PROPERTIES _qt_android_abis "${android_abis}")
endfunction()
+
+# The wrapper function that contains routines that need to be called to produce a valid Android
+# package for the executable 'target'. The function is added to the finalizer list of the Core
+# module and is executed implicitly when configuring user projects.
+function(_qt_internal_android_executable_finalizer target)
+ set_property(TARGET ${target} PROPERTY _qt_android_executable_finalizer_called TRUE)
+
+ _qt_internal_expose_android_package_source_dir_to_ide(${target})
+
+ _qt_internal_configure_android_multiabi_target("${target}")
+ qt6_android_generate_deployment_settings("${target}")
+ qt6_android_add_apk_target("${target}")
+endfunction()
+
+function(_qt_internal_expose_android_package_source_dir_to_ide target)
+ get_target_property(android_package_source_dir ${target} QT_ANDROID_PACKAGE_SOURCE_DIR)
+ if(android_package_source_dir)
+ get_target_property(target_source_dir ${target} SOURCE_DIR)
+ if(NOT IS_ABSOLUTE "${android_package_source_dir}")
+ string(JOIN "/" android_package_source_dir
+ "${target_source_dir}"
+ "${android_package_source_dir}"
+ )
+ endif()
+
+ if(EXISTS "${android_package_source_dir}")
+ file(GLOB_RECURSE android_package_sources
+ RELATIVE "${target_source_dir}"
+ "${android_package_source_dir}/*"
+ )
+ endif()
+
+ foreach(f IN LISTS android_package_sources)
+ _qt_internal_expose_source_file_to_ide(${target} "${f}")
+ endforeach()
+ endif()
+endfunction()
+
+set(QT_INTERNAL_ANDROID_TARGET_BUILD_DIR_SUPPORT ON CACHE INTERNAL
+ "Indicates that Qt supports per-target Android build directories")