diff --git a/cmake/QtPlatformAndroid.cmake b/cmake/QtPlatformAndroid.cmake index ba5c86d9185..dd76b1e81e3 100644 --- a/cmake/QtPlatformAndroid.cmake +++ b/cmake/QtPlatformAndroid.cmake @@ -129,6 +129,15 @@ define_property(TARGET "This variable can be used to specify a directory where additions and modifications can be made to the default Android package template. The androiddeployqt tool will copy the application template from Qt into the build directory, and then it will copy the contents of the ANDROID_PACKAGE_SOURCE_DIR on top of this, overwriting any existing files. The update step where parts of the source files are modified automatically to reflect your other settings is then run on the resulting merged package. If you, for instance, want to make a custom AndroidManifest.xml for your application, then place this directly into the folder specified in this variable. You can also add custom Java files in ANDROID_PACKAGE_SOURCE_DIR/src." ) +define_property(TARGET + PROPERTY + QT_ANDROID_APPLICATION_ARGUMENTS + BRIEF_DOCS + "This variable can be used to specify command-line arguments to the Android app." + FULL_DOCS + "Specifies extra command-line arguments to the Android app using the AndroidManifest.xml with the tag android.app.arguments." +) + define_property(TARGET PROPERTY QT_ANDROID_DEPLOYMENT_SETTINGS_FILE @@ -138,9 +147,224 @@ define_property(TARGET " " ) +# Generate deployment tool json +function(qt_android_generate_deployment_settings target) + # Information extracted from mkspecs/features/android/android_deployment_settings.prf + if (NOT TARGET ${target}) + message(SEND_ERROR "${target} is not a cmake target") + return() + endif() + + get_target_property(target_type ${target} TYPE) + + if (NOT "${target_type}" STREQUAL "MODULE_LIBRARY") + message(SEND_ERROR "QT_ANDROID_GENERATE_DEPLOYMENT_SETTINGS only works on Module targets") + return() + endif() + + get_target_property(target_source_dir ${target} SOURCE_DIR) + get_target_property(target_binary_dir ${target} BINARY_DIR) + get_target_property(target_output_name ${target} OUTPUT_NAME) + if (NOT target_output_name) + set(target_output_name ${target}) + endif() + set(deploy_file "${target_binary_dir}/android-lib${target_output_name}.so-deployment-settings.json") + + set(file_contents "{\n") + # content begin + string(APPEND file_contents + " \"description\": \"This file is generated by cmake to be read by androiddeployqt and should not be modified by hand.\",\n") + + # Host Qt Android install path + if (NOT QT_BUILDING_QT) + set(file_check "${Qt6_DIR}/plugins/platforms/android/libqtforandroid_${CMAKE_ANDROID_ARCH_ABI}.so") + if (NOT EXISTS ${file_check}) + message(SEND_ERROR "Detected Qt installation does not contain libqtforandroid.so. This is most likely due to the installation not being a build of Qt for Android. Please update your settings.") + return() + endif() + set(qt_android_install_dir ${Qt6_Dir}) + else() + # Building from source, use the same install prefix + set(qt_android_install_dir ${CMAKE_INSTALL_PREFIX}) + endif() + + file(TO_NATIVE_PATH "${qt_android_install_dir}" qt_android_install_dir_native) + string(APPEND file_contents + " \"qt\": \"${qt_android_install_dir_native}\",\n") + + # Android SDK path + file(TO_NATIVE_PATH "${ANDROID_SDK_ROOT}" android_sdk_root_native) + string(APPEND file_contents + " \"sdk\": \"${android_sdk_root_native}\",\n") + + # Android SDK Build Tools Revision + string(APPEND file_contents + " \"sdkBuildToolsRevision\": \"${QT_ANDROID_SDK_BUILD_TOOLS_VERSION}\",\n") + + # Android NDK + file(TO_NATIVE_PATH "${ANDROID_NDK}" android_ndk_root_native) + string(APPEND file_contents + " \"ndk\": \"${android_ndk_root_native}\",\n") + + # Setup LLVM toolchain + string(APPEND file_contents + " \"toolchain-prefix\": \"llvm\",\n") + string(APPEND file_contents + " \"tool-prefix\": \"llvm\",\n") + string(APPEND file_contents + " \"useLLVM\": true,\n") + + # NDK Toolchain Version + string(APPEND file_contents + " \"toolchain-version\": \"${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}\",\n") + + # NDK Host + string(APPEND file_contents + " \"ndk-host\": \"${ANDROID_NDK_HOST_SYSTEM_NAME}\",\n") + + if (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86") + set(arch_value "i686-linux-android") + elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64") + set(arch_value "x86_64-linux-android") + elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") + set(arch_value "aarch64-linux-android") + else() + set(arch_value "arm-linux-androideabi") + endif() + + # Architecture + string(APPEND file_contents + " \"architectures\": { \"${CMAKE_ANDROID_ARCH_ABI}\" : \"${arch_value}\" },\n") + + # deployment dependencies + get_target_property(android_deployment_dependencies + ${target} QT_ANDROID_DEPLOYMENT_DEPENDENCIES) + if (android_deployment_dependencies) + list(JOIN android_deployment_dependencies "," android_deployment_dependencies) + string(APPEND file_contents + " \"deployment-dependencies\": \"${android_deployment_dependencies}\",\n") + endif() + + # Extra plugins + get_target_property(android_extra_plugins + ${target} QT_ANDROID_EXTRA_PLUGINS) + if (android_extra_plugins) + list(JOIN android_extra_plugins "," android_extra_plugins) + string(APPEND file_contents + " \"android-extra-plugins\": \"${android_extra_plugins}\",\n") + endif() + + # Extra libs + get_target_property(android_extra_libs + ${target} QT_ANDROID_EXTRA_LIBS) + if (android_extra_libs) + list(JOIN android_extra_libs "," android_extra_libs) + string(APPEND file_contents + " \"android-extra-libs\": \"${android_extra_libs}\",\n") + endif() + + # package source dir + get_target_property(android_package_source_dir + ${target} QT_ANDROID_PACKAGE_SOURCE_DIR) + if (android_package_source_dir) + file(TO_NATIVE_PATH "${android_package_source_dir}" android_package_source_dir_native) + string(APPEND file_contents + " \"android-package-source-directory\": \"${android_package_source_dir_native}\",\n") +endif() + + #TODO: ANDROID_VERSION_NAME, doesn't seem to be used? + + #TODO: ANDROID_VERSION_CODE, doesn't seem to be used? + + get_target_property(qml_import_path ${target} QT_QML_IMPORT_PATH) + if (qml_import_path) + file(TO_NATIVE_PATH "${qml_import_path}" qml_import_path_native) + string(APPEND file_contents + " \"qml-import-path\": \"${qml_import_path_native}\",\n") + endif() + + get_target_property(qml_root_path ${target} QT_QML_ROOT_PATH) + if(NOT qml_root_path) + set(qml_root_path "${target_source_dir}") + endif() + file(TO_NATIVE_PATH "${qml_root_path}" qml_root_path_native) + string(APPEND file_contents + " \"qml-root-path\": \"${qml_root_path_native}\",\n") + + # App binary + string(APPEND file_contents + " \"application-binary\": \"${target_output_name}\",\n") + + # App command-line arguments + string(APPEND file_contents + " \"android-application-arguments\": \"${QT_ANDROID_APPLICATION_ARGUMENTS}\",\n") + + # Override qmlimportscanner binary path + set(qml_importscanner_binary_path "${QT_HOST_PATH}/bin/qmlimportscanner") + if (WIN32) + string(APPEND qml_importscanner_binary_path ".exe") + endif() + file(TO_NATIVE_PATH "${qml_importscanner_binary_path}" qml_importscanner_binary_path_native) + string(APPEND file_contents + " \"qml-importscanner-binary\" : \"${qml_importscanner_binary_path_native}\",\n") + + # Last item in json file + + # base location of stdlibc++, will be suffixed by androiddeploy qt + set(android_ndk_stdlib_base_path + "${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/" + ) + string(APPEND file_contents + " \"stdcpp-path\": \"${android_ndk_stdlib_base_path}\"\n") + + # content end + string(APPEND file_contents "}\n") + + file(WRITE ${deploy_file} ${file_contents}) + + set_target_properties(${target} + PROPERTIES + QT_ANDROID_DEPLOYMENT_SETTINGS_FILE ${deploy_file} + ) +endfunction() + +function(qt_android_apply_arch_suffix target) + 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") + elseif (target_type STREQUAL "STATIC_LIBRARY") + set_property(TARGET "${target}" PROPERTY SUFFIX "_${CMAKE_ANDROID_ARCH_ABI}.a") + endif() +endfunction() + +# Add custom target to package the APK +function(qt_android_add_apk_target target) + 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() + + set(deployment_tool "${QT_HOST_PATH}/bin/androiddeployqt") + set(apk_dir "$/android-build") + add_custom_target(${target}_prepare_apk_dir + DEPENDS ${target} + COMMAND ${CMAKE_COMMAND} + -E copy $ + "${apk_dir}/libs/${CMAKE_ANDROID_ARCH_ABI}/$" + COMMENT "Copying ${target} binarty to apk folder" + ) + + add_custom_target(${target}_make_apk + DEPENDS ${target}_prepare_apk_dir + COMMAND ${deployment_tool} + --input ${deployment_file} + --output ${apk_dir} + COMMENT "Creating APK for ${target}" + ) +endfunction() + # Add a test for Android which will be run by the android test runner tool function(qt_android_add_test target) - set(deployment_tool "${QT_HOST_PATH}/bin/androiddeployqt") set(test_runner "${QT_HOST_PATH}/bin/androidtestrunner") diff --git a/mkspecs/features/android/android_deployment_settings.prf b/mkspecs/features/android/android_deployment_settings.prf index 573fa1bdfef..23caae2f4dd 100644 --- a/mkspecs/features/android/android_deployment_settings.prf +++ b/mkspecs/features/android/android_deployment_settings.prf @@ -70,6 +70,9 @@ contains(TEMPLATE, ".*app"):!build_pass:!android-embedded { } unset(qml_import_paths) + !isEmpty(ANDROID_APPLICATION_ARGUMENTS): \ + FILE_CONTENT += " \"android-application-arguments\": $$emitString($$ANDROID_APPLICATION_ARGUMENTS)," + isEmpty(QML_ROOT_PATH): \ QML_ROOT_PATH = $$_PRO_FILE_PWD_ FILE_CONTENT += " \"qml-root-path\": $$emitString($$QML_ROOT_PATH)," diff --git a/qmake/doc/src/qmake-manual.qdoc b/qmake/doc/src/qmake-manual.qdoc index 2631abe3aae..9335d27b137 100644 --- a/qmake/doc/src/qmake-manual.qdoc +++ b/qmake/doc/src/qmake-manual.qdoc @@ -937,6 +937,19 @@ Specifies the Android API level number. For more information, see \l{Android: Build Numbers}{Android Build Numbers}. + \target ANDROID_APPLICATION_ARGUMENTS + \section1 ANDROID_APPLICATION_ARGUMENTS + + \note This variable applies only to Android targets. + + Specifies extra command-line arguments to the Android app using the + \c AndroidManifest.xml with the tag "android.app.arguments". This takes + a string of arguments: + + \badcode + ANDROID_APPLICATION_ARGUMENTS = "arg1 arg2 arg3" + \endcode + \target ANDROID_BUNDLED_JAR_DEPENDENCIES \section1 ANDROID_BUNDLED_JAR_DEPENDENCIES diff --git a/src/android/templates/AndroidManifest.xml b/src/android/templates/AndroidManifest.xml index 092cf165450..62c6da5a32e 100644 --- a/src/android/templates/AndroidManifest.xml +++ b/src/android/templates/AndroidManifest.xml @@ -17,7 +17,7 @@ - + diff --git a/src/corelib/Qt5AndroidSupport.cmake b/src/corelib/Qt5AndroidSupport.cmake index 662dd87c1f2..cb908a29c6d 100644 --- a/src/corelib/Qt5AndroidSupport.cmake +++ b/src/corelib/Qt5AndroidSupport.cmake @@ -42,6 +42,7 @@ if (NOT ${PROJECT_NAME}-MultiAbiBuild) [=[{ "_description": "This file is created by CMake to be read by androiddeployqt and should not be modified by hand.", "application-binary": "@QT_ANDROID_APPLICATION_BINARY@", + "application-arguments": "@QT_ANDROID_APPLICATION_ARGUMENTS@", "architectures": { @QT_ANDROID_ARCHITECTURES@ }, @@ -99,6 +100,7 @@ if (NOT ${PROJECT_NAME}-MultiAbiBuild) generate_json_variable_list(ANDROID_DEPLOYMENT_DEPENDENCIES "deployment-dependencies") generate_json_variable_list(ANDROID_EXTRA_PLUGINS "android-extra-plugins") generate_json_variable(ANDROID_PACKAGE_SOURCE_DIR "android-package-source-directory") + generate_json_variable(ANDROID_APPLICATION_ARGUMENTS "android-application-arguments") generate_json_variable(ANDROID_VERSION_CODE "android-version-code") generate_json_variable(ANDROID_VERSION_NAME "android-version-name") generate_json_variable_list(ANDROID_EXTRA_LIBS "android-extra-libs") diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 94155546087..621d5554f09 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -167,6 +167,7 @@ struct Options QString outputDirectory; QString inputFileName; QString applicationBinary; + QString applicationArguments; QString rootPath; QStringList qmlImportPaths; QStringList qrcFiles; @@ -884,6 +885,14 @@ bool readInputFile(Options *options) options->androidSourceDirectory = androidSourcesDirectory.toString(); } + { + const QJsonValue applicationArguments = jsonObject.value(QLatin1String("android-application-arguments")); + if (!applicationArguments.isUndefined()) + options->applicationArguments = applicationArguments.toString(); + else + options->applicationArguments = QStringLiteral(""); + } + { const QJsonValue androidVersionName = jsonObject.value(QLatin1String("android-version-name")); if (!androidVersionName.isUndefined()) @@ -1430,6 +1439,7 @@ bool updateAndroidManifest(Options &options) QHash replacements; replacements[QStringLiteral("-- %%INSERT_APP_NAME%% --")] = options.applicationBinary; + replacements[QStringLiteral("-- %%INSERT_APP_ARGUMENTS%% --")] = options.applicationArguments; replacements[QStringLiteral("-- %%INSERT_APP_LIB_NAME%% --")] = options.applicationBinary; replacements[QStringLiteral("-- %%INSERT_LOCAL_JARS%% --")] = options.localJars.join(QLatin1Char(':')); replacements[QStringLiteral("-- %%INSERT_INIT_CLASSES%% --")] = options.initClasses.join(QLatin1Char(':'));