From 979a21dc4ee0c6f483c6b55e9242a153d659ab6f Mon Sep 17 00:00:00 2001 From: Fab Stz Date: Wed, 5 Oct 2022 17:26:42 +0200 Subject: [PATCH] Add support for MultiABI with custom install dir of the android-build Right now, "multi abi builds" of android projects works only if the android-build installation doesn't use custom install dirs (INSTALL_PREFIX, INSTALL_BINDIR...) At the same time, it fixes QTBUG-106533. The patches are the same as the ones in that bugreport. Add new items to android-*-deployment-settings.json: qtDataDirectory qtLibsDirectory qtLibExecsDirectory qtPluginsDirectory qtQmlDirectory Update androiddeployqt to be able to get files from their install location BTW (fixes QTBUG-106533): Install src/android/templates into INSTALL_DATADIR Install src/3rdparty/gradle into INSTALL_DATADIR Install src/android/java files into INSTALL_DATADIR Install all jars into INSTALL_DATADIR Add missing path to target_qt.conf Update target_qt.conf to have all path. Otherwise qmake wouldn't have the path when installing the android-build with custom install dirs like INSTALL_LIBDIR & friends Add support for a new cmake variable that can be set at build time of the android projects: QT_ANDROID_PATH_CMAKE_DIR_${abi} (Name chosen as brother of QT_HOST_PATH_CMAKE_DIR) Pick-to: 6.5 Fixes: QTBUG-106533 Fixes: QTBUG-107207 Change-Id: Ia3751362ab1b5f877ecafbe02f263feac167119c Reviewed-by: Qt CI Bot Reviewed-by: Alexandru Croitor --- cmake/QtAndroidHelpers.cmake | 2 +- cmake/QtQmakeHelpers.cmake | 13 ++ .../android/android_deployment_settings.prf | 5 + src/3rdparty/gradle/CMakeLists.txt | 2 +- src/android/jar/CMakeLists.txt | 4 +- src/android/java/CMakeLists.txt | 2 +- src/android/templates/CMakeLists.txt | 2 +- src/corelib/CMakeLists.txt | 2 +- src/corelib/Qt6AndroidMacros.cmake | 135 ++++++++++---- src/network/android/jar/CMakeLists.txt | 4 +- .../networkinformation/android/CMakeLists.txt | 4 +- src/tools/androiddeployqt/main.cpp | 164 +++++++++++++++--- 12 files changed, 270 insertions(+), 69 deletions(-) diff --git a/cmake/QtAndroidHelpers.cmake b/cmake/QtAndroidHelpers.cmake index 70200d675c..857a029991 100644 --- a/cmake/QtAndroidHelpers.cmake +++ b/cmake/QtAndroidHelpers.cmake @@ -252,7 +252,7 @@ function(qt_internal_android_dependencies target) # Module plugins if(module_plugin_types) foreach(plugin IN LISTS module_plugin_types) - string(APPEND file_contents "\n") + string(APPEND file_contents "\n") endforeach() endif() diff --git a/cmake/QtQmakeHelpers.cmake b/cmake/QtQmakeHelpers.cmake index 6e87afff3e..8154661953 100644 --- a/cmake/QtQmakeHelpers.cmake +++ b/cmake/QtQmakeHelpers.cmake @@ -128,6 +128,19 @@ Prefix=${prefix} string(APPEND content "[Paths] Prefix=${ext_prefix_relative_to_conf_file} +Documentation=${INSTALL_DOCDIR} +Headers=${INSTALL_INCLUDEDIR} +Libraries=${INSTALL_LIBDIR} +LibraryExecutables=${INSTALL_LIBEXECDIR} +Binaries=${INSTALL_BINDIR} +Plugins=${INSTALL_PLUGINSDIR} +QmlImports=${INSTALL_QMLDIR} +ArchData=${INSTALL_ARCHDATADIR} +Data=${INSTALL_DATADIR} +Translations=${INSTALL_TRANSLATIONSDIR} +Examples=${INSTALL_EXAMPLESDIR} +Tests=${INSTALL_TESTSDIR} +Settings=${INSTALL_SYSCONFDIR} HostPrefix=${host_prefix_relative_to_conf_file} HostBinaries=${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_BINDIR} HostLibraries=${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_LIBDIR} diff --git a/mkspecs/features/android/android_deployment_settings.prf b/mkspecs/features/android/android_deployment_settings.prf index d56d79960c..8d68defa97 100644 --- a/mkspecs/features/android/android_deployment_settings.prf +++ b/mkspecs/features/android/android_deployment_settings.prf @@ -8,6 +8,11 @@ contains(TEMPLATE, ".*app"):!build_pass { FILE_CONTENT += " \"description\": \"This file is generated by qmake to be read by androiddeployqt and should not be modified by hand.\"," FILE_CONTENT += " \"qt\": $$emitString($$[QT_INSTALL_PREFIX])," + FILE_CONTENT += " \"qtDataDirectory\": $$emitString($$relative_path($$[QT_INSTALL_DATA], $$[QT_INSTALL_PREFIX]))," + FILE_CONTENT += " \"qtLibExecsDirectory\": $$emitString($$relative_path($$[QT_INSTALL_LIBEXECS], $$[QT_INSTALL_PREFIX]))," + FILE_CONTENT += " \"qtLibsDirectory\": $$emitString($$relative_path($$[QT_INSTALL_LIBS], $$[QT_INSTALL_PREFIX]))," + FILE_CONTENT += " \"qtPluginsDirectory\": $$emitString($$relative_path($$[QT_INSTALL_PLUGINS], $$[QT_INSTALL_PREFIX]))," + FILE_CONTENT += " \"qtQmlDirectory\": $$emitString($$relative_path($$[QT_INSTALL_QML], $$[QT_INSTALL_PREFIX]))," # Settings from mkspecs/environment FILE_CONTENT += " \"sdk\": $$emitString($$ANDROID_SDK_ROOT)," diff --git a/src/3rdparty/gradle/CMakeLists.txt b/src/3rdparty/gradle/CMakeLists.txt index 3013eca1f9..3400c7e9c3 100644 --- a/src/3rdparty/gradle/CMakeLists.txt +++ b/src/3rdparty/gradle/CMakeLists.txt @@ -20,7 +20,7 @@ add_custom_target(Qt${QtBase_VERSION_MAJOR}GradleScripts ${gradle_wrapper_files} ) -qt_path_join(destination ${QT_INSTALL_DIR} "src/3rdparty/gradle") +qt_path_join(destination ${QT_INSTALL_DIR} ${INSTALL_DATADIR} "src/3rdparty/gradle") qt_copy_or_install( PROGRAMS diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt index f12c250e80..0c0a86d067 100644 --- a/src/android/jar/CMakeLists.txt +++ b/src/android/jar/CMakeLists.txt @@ -28,8 +28,10 @@ qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android # special case OUTPUT_DIR "${QT_BUILD_DIR}/jar" ) +qt_path_join(destination ${INSTALL_DATADIR} "jar") + install_jar(Qt${QtBase_VERSION_MAJOR}Android # special case - DESTINATION jar + DESTINATION ${destination} COMPONENT Devel ) diff --git a/src/android/java/CMakeLists.txt b/src/android/java/CMakeLists.txt index 8f0c0da542..598e374a03 100644 --- a/src/android/java/CMakeLists.txt +++ b/src/android/java/CMakeLists.txt @@ -48,7 +48,7 @@ add_custom_target(Qt${QtBase_VERSION_MAJOR}AndroidBindings ${strings_resouces} ) -qt_path_join(destination ${QT_INSTALL_DIR} "src/android/java") +qt_path_join(destination ${QT_INSTALL_DIR} ${INSTALL_DATADIR} "src/android/java") qt_copy_or_install(DIRECTORY ${resource_directories} DESTINATION "${destination}" diff --git a/src/android/templates/CMakeLists.txt b/src/android/templates/CMakeLists.txt index 3eba4611e8..b3728968fb 100644 --- a/src/android/templates/CMakeLists.txt +++ b/src/android/templates/CMakeLists.txt @@ -18,7 +18,7 @@ add_custom_target(Qt${QtBase_VERSION_MAJOR}AndroidTemplates "${CMAKE_CURRENT_SOURCE_DIR}/res/values/libs.xml" ) -qt_path_join(destination ${QT_INSTALL_DIR} "src/android/templates") +qt_path_join(destination ${QT_INSTALL_DIR} ${INSTALL_DATADIR} "src/android/templates") qt_copy_or_install(FILES ${template_files} DESTINATION "${destination}") diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index e4461e3d7c..cc6ca07feb 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -443,7 +443,7 @@ if(ANDROID) jar/Qt${QtBase_VERSION_MAJOR}Android.jar # special case ) set_property(TARGET Core APPEND PROPERTY QT_ANDROID_LIB_DEPENDENCIES - plugins/platforms/libplugins_platforms_qtforandroid.so + ${INSTALL_PLUGINSDIR}/platforms/libplugins_platforms_qtforandroid.so ) set_property(TARGET Core APPEND PROPERTY QT_ANDROID_PERMISSIONS android.permission.INTERNET android.permission.WRITE_EXTERNAL_STORAGE diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake index 48abb907a0..c16626f35b 100644 --- a/src/corelib/Qt6AndroidMacros.cmake +++ b/src/corelib/Qt6AndroidMacros.cmake @@ -133,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) @@ -197,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") @@ -967,8 +947,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. @@ -988,9 +975,85 @@ 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(QT_BUILDING_QT AND NOT QT_BUILD_STANDALONE_TESTS) + set(cmake_dir "${QT_CONFIG_BUILD_DIR}") + else() + set(cmake_dir "${prefix_path}/${QT6_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}Core/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) @@ -998,9 +1061,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() @@ -1138,9 +1201,7 @@ function(_qt_internal_configure_android_multiabi_target target) 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") + _qt_internal_get_android_abi_toolchain_path(qt_abi_toolchain_path ${abi}) ExternalProject_Add("qt_internal_android_${abi}" SOURCE_DIR "${CMAKE_SOURCE_DIR}" BINARY_DIR "${android_abi_build_dir}" @@ -1211,7 +1272,9 @@ 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_ CMake variable.\n") + " Android ABI using QT_PATH_ANDROID_ABI_ CMake variable with the value" + " of the installation prefix, and QT_ANDROID_PATH_CMAKE_DIR_ with" + " the location of the cmake directory for that ABI.\n") endif() list(JOIN android_abis ", " android_abis_string) diff --git a/src/network/android/jar/CMakeLists.txt b/src/network/android/jar/CMakeLists.txt index d4dd9d3d89..c02223fc56 100644 --- a/src/network/android/jar/CMakeLists.txt +++ b/src/network/android/jar/CMakeLists.txt @@ -13,8 +13,10 @@ qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetwork # special case OUTPUT_DIR "${QT_BUILD_DIR}/jar" ) +qt_path_join(destination ${INSTALL_DATADIR} "jar") + install_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetwork # special case - DESTINATION jar + DESTINATION ${destination} COMPONENT Devel ) diff --git a/src/plugins/networkinformation/android/CMakeLists.txt b/src/plugins/networkinformation/android/CMakeLists.txt index 7980ae9d76..f9ed4913e3 100644 --- a/src/plugins/networkinformation/android/CMakeLists.txt +++ b/src/plugins/networkinformation/android/CMakeLists.txt @@ -12,8 +12,10 @@ qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetworkInformationBackend OUTPUT_DIR "${QT_BUILD_DIR}/jar" ) +qt_path_join(destination ${INSTALL_DATADIR} "jar") + install_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetworkInformationBackend - DESTINATION jar + DESTINATION ${destination} COMPONENT Devel ) diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 787c2f405c..75a2ddd4d5 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -70,10 +70,18 @@ struct QtDependency struct QtInstallDirectoryWithTriple { - QtInstallDirectoryWithTriple(const QString &dir = QString(), const QString &t = QString()) : - qtInstallDirectory(dir), triple(t), enabled(false) {} + QtInstallDirectoryWithTriple(const QString &dir = QString(), + const QString &t = QString(), + const QHash &dirs = QHash() + ) : + qtInstallDirectory(dir), + qtDirectories(dirs), + triple(t), + enabled(false) + {} QString qtInstallDirectory; + QHash qtDirectories; QString triple; bool enabled; }; @@ -128,6 +136,12 @@ struct Options // Build paths QString qtInstallDirectory; + QHash qtDirectories; + QString qtDataDirectory; + QString qtLibsDirectory; + QString qtLibExecsDirectory; + QString qtPluginsDirectory; + QString qtQmlDirectory; QString qtHostDirectory; std::vector extraPrefixDirs; // Unlike 'extraPrefixDirs', the 'extraLibraryDirs' key doesn't expect the 'lib' subfolder @@ -198,10 +212,17 @@ struct Options QString installLocation; // Per architecture collected information - void setCurrentQtArchitecture(const QString &arch, const QString &directory) + void setCurrentQtArchitecture(const QString &arch, + const QString &directory, + const QHash &directories) { currentArchitecture = arch; qtInstallDirectory = directory; + qtDataDirectory = directories["qtDataDirectory"_L1]; + qtLibsDirectory = directories["qtLibsDirectory"_L1]; + qtLibExecsDirectory = directories["qtLibExecsDirectory"_L1]; + qtPluginsDirectory = directories["qtPluginsDirectory"_L1]; + qtQmlDirectory = directories["qtQmlDirectory"_L1]; } typedef QPair BundledFile; QHash> bundledFiles; @@ -815,6 +836,66 @@ bool parseCmakeBoolean(const QJsonValue &value) || stringValue.toInt() > 0); } +bool readInputFileDirectory(Options *options, QJsonObject &jsonObject, const QString keyName) +{ + const QJsonValue qtDirectory = jsonObject.value(keyName); + if (qtDirectory.isUndefined()) { + for (auto it = options->architectures.constBegin(); it != options->architectures.constEnd(); ++it) { + if (keyName == "qtDataDirectory"_L1) { + options->architectures[it.key()].qtDirectories[keyName] = it.value().qtInstallDirectory; + break; + } else if (keyName == "qtLibsDirectory"_L1) { + options->architectures[it.key()].qtDirectories[keyName] = "lib"_L1; + break; + } else if (keyName == "qtLibExecsDirectory"_L1) { + options->architectures[it.key()].qtDirectories[keyName] = defaultLibexecDir(); + break; + } else if (keyName == "qtPluginsDirectory"_L1) { + options->architectures[it.key()].qtDirectories[keyName] = "plugins"_L1; + break; + } else if (keyName == "qtQmlDirectory"_L1) { + options->architectures[it.key()].qtDirectories[keyName] = "qml"_L1; + break; + } + } + return true; + } + + if (qtDirectory.isObject()) { + const QJsonObject object = qtDirectory.toObject(); + for (auto it = object.constBegin(); it != object.constEnd(); ++it) { + if (it.value().isUndefined()) { + fprintf(stderr, + "Invalid '%s' record in deployment settings: %s\n", + qPrintable(keyName), + qPrintable(it.value().toString())); + return false; + } + if (it.value().isNull()) + continue; + if (!options->architectures.contains(it.key())) { + fprintf(stderr, "Architecture %s unknown (%s).", qPrintable(it.key()), + qPrintable(options->architectures.keys().join(u','))); + return false; + } + options->architectures[it.key()].qtDirectories[keyName] = it.value().toString(); + } + } else if (qtDirectory.isString()) { + // Format for Qt < 6 or when using the tool with Qt >= 6 but in single arch. + // We assume Qt > 5.14 where all architectures are in the same directory. + const QString directory = qtDirectory.toString(); + options->architectures["arm64-v8a"_L1].qtDirectories[keyName] = directory; + options->architectures["armeabi-v7a"_L1].qtDirectories[keyName] = directory; + options->architectures["x86"_L1].qtDirectories[keyName] = directory; + options->architectures["x86_64"_L1].qtDirectories[keyName] = directory; + } else { + fprintf(stderr, "Invalid format for %s in json file %s.\n", + qPrintable(keyName), qPrintable(options->inputFileName)); + return false; + } + return true; +} + bool readInputFile(Options *options) { QFile file(options->inputFileName); @@ -871,7 +952,8 @@ bool readInputFile(Options *options) const QJsonObject object = qtInstallDirectory.toObject(); for (auto it = object.constBegin(); it != object.constEnd(); ++it) { if (it.value().isUndefined()) { - fprintf(stderr, "Invalid architecture: %s\n", + fprintf(stderr, + "Invalid 'qt' record in deployment settings: %s\n", qPrintable(it.value().toString())); return false; } @@ -899,6 +981,14 @@ bool readInputFile(Options *options) return false; } } + + if (!readInputFileDirectory(options, jsonObject, "qtDataDirectory"_L1) || + !readInputFileDirectory(options, jsonObject, "qtLibsDirectory"_L1) || + !readInputFileDirectory(options, jsonObject, "qtLibExecsDirectory"_L1) || + !readInputFileDirectory(options, jsonObject, "qtPluginsDirectory"_L1) || + !readInputFileDirectory(options, jsonObject, "qtQmlDirectory"_L1)) + return false; + { const QJsonValue qtHostDirectory = jsonObject.value("qtHostDir"_L1); if (!qtHostDirectory.isUndefined()) { @@ -1220,13 +1310,15 @@ void cleanAndroidFiles(const Options &options) if (!options.androidSourceDirectory.isEmpty()) cleanTopFolders(options, QDir(options.androidSourceDirectory), options.outputDirectory); - cleanTopFolders(options, QDir(options.qtInstallDirectory + "/src/android/templates"_L1), + cleanTopFolders(options, + QDir(options.qtInstallDirectory + u'/' + + options.qtDataDirectory + "/src/android/templates"_L1), options.outputDirectory); } bool copyAndroidTemplate(const Options &options, const QString &androidTemplate, const QString &outDirPrefix = QString()) { - QDir sourceDirectory(options.qtInstallDirectory + androidTemplate); + QDir sourceDirectory(options.qtInstallDirectory + u'/' + options.qtDataDirectory + androidTemplate); if (!sourceDirectory.exists()) { fprintf(stderr, "Cannot find template directory %s\n", qPrintable(sourceDirectory.absolutePath())); return false; @@ -1244,7 +1336,8 @@ bool copyAndroidTemplate(const Options &options, const QString &androidTemplate, bool copyGradleTemplate(const Options &options) { - QDir sourceDirectory(options.qtInstallDirectory + "/src/3rdparty/gradle"_L1); + QDir sourceDirectory(options.qtInstallDirectory + u'/' + + options.qtDataDirectory + "/src/3rdparty/gradle"_L1); if (!sourceDirectory.exists()) { fprintf(stderr, "Cannot find template directory %s\n", qPrintable(sourceDirectory.absolutePath())); return false; @@ -1369,7 +1462,8 @@ bool copyAndroidExtraResources(Options *options) } QDir resourceDir(extraResource); - QString assetsDir = options->outputDirectory + "/assets/"_L1 + resourceDir.dirName() + u'/'; + QString assetsDir = options->outputDirectory + "/assets/"_L1 + + resourceDir.dirName() + u'/'; QString libsDir = options->outputDirectory + "/libs/"_L1 + options->currentArchitecture + u'/'; const QStringList files = allFilesInside(resourceDir, resourceDir); @@ -1706,6 +1800,21 @@ static QString absoluteFilePath(const Options *options, const QString &relativeF if (QFile::exists(path)) return path; } + + if (relativeFileName.endsWith("-android-dependencies.xml"_L1)) { + return options->qtInstallDirectory + u'/' + options->qtLibsDirectory + + u'/' + relativeFileName; + } + + if (relativeFileName.startsWith("jar/"_L1)) { + return options->qtInstallDirectory + u'/' + options->qtDataDirectory + + u'/' + relativeFileName; + } + + if (relativeFileName.startsWith("lib/"_L1)) { + return options->qtInstallDirectory + u'/' + options->qtLibsDirectory + + u'/' + relativeFileName.mid(sizeof("lib/") - 1); + } return options->qtInstallDirectory + u'/' + relativeFileName; } @@ -1739,8 +1848,9 @@ QList findFilesRecursively(const Options &options, const QString & if (info.exists()) return findFilesRecursively(options, info, prefix + u'/'); } - QFileInfo info(options.qtInstallDirectory + u'/' + fileName); - return findFilesRecursively(options, info, options.qtInstallDirectory + u'/'); + QFileInfo info(options.qtInstallDirectory + "/"_L1 + fileName); + QFileInfo rootPath(options.qtInstallDirectory + "/"_L1); + return findFilesRecursively(options, info, rootPath.absolutePath() + u'/'); } bool readAndroidDependencyXml(Options *options, @@ -1748,7 +1858,7 @@ bool readAndroidDependencyXml(Options *options, QSet *usedDependencies, QSet *remainingDependencies) { - QString androidDependencyName = absoluteFilePath(options, "/lib/%1-android-dependencies.xml"_L1.arg(moduleName)); + QString androidDependencyName = absoluteFilePath(options, "%1-android-dependencies.xml"_L1.arg(moduleName)); QFile androidDependencyFile(androidDependencyName); if (androidDependencyFile.exists()) { @@ -1774,6 +1884,7 @@ bool readAndroidDependencyXml(Options *options, QString file = reader.attributes().value("file"_L1).toString(); const QList fileNames = findFilesRecursively(*options, file); + for (const QtDependency &fileName : fileNames) { if (usedDependencies->contains(fileName.absolutePath)) continue; @@ -1951,8 +2062,8 @@ bool scanImports(Options *options, QSet *usedDependencies) if (!options->qmlImportScannerBinaryPath.isEmpty()) { qmlImportScanner = options->qmlImportScannerBinaryPath; } else { - qmlImportScanner = execSuffixAppended(options->qtInstallDirectory + u'/' - + defaultLibexecDir() + "/qmlimportscanner"_L1); + qmlImportScanner = execSuffixAppended(options->qtLibExecsDirectory + + "/qmlimportscanner"_L1); } QStringList importPaths; @@ -1961,7 +2072,7 @@ bool scanImports(Options *options, QSet *usedDependencies) // lacks a qml directory. We don't want to pass it as an import path if it doesn't exist // because it will cause qmlimportscanner to fail. // This also covers the case when only qtbase is installed in a regular Qt build. - const QString mainImportPath = options->qtInstallDirectory + "/qml"_L1; + const QString mainImportPath = options->qtInstallDirectory + u'/' + options->qtQmlDirectory; if (QFile::exists(mainImportPath)) importPaths += shellQuote(mainImportPath); @@ -2215,8 +2326,7 @@ bool createRcc(const Options &options) if (!options.rccBinaryPath.isEmpty()) { rcc = options.rccBinaryPath; } else { - rcc = execSuffixAppended(options.qtInstallDirectory + u'/' + defaultLibexecDir() + - "/rcc"_L1); + rcc = execSuffixAppended(options.qtLibExecsDirectory + "/rcc"_L1); } if (!QFile::exists(rcc)) { @@ -2409,12 +2519,8 @@ bool copyQtFiles(Options *options) QString destinationFileName; bool isSharedLibrary = qtDependency.relativePath.endsWith(".so"_L1); if (isSharedLibrary) { - QString garbledFileName; - if (QDir::fromNativeSeparators(qtDependency.relativePath).startsWith("lib/"_L1)) { - garbledFileName = qtDependency.relativePath.mid(sizeof("lib/") - 1); - } else { - garbledFileName = qtDependency.relativePath.mid(qtDependency.relativePath.lastIndexOf(u'/') + 1); - } + QString garbledFileName = qtDependency.relativePath.mid( + qtDependency.relativePath.lastIndexOf(u'/') + 1); destinationFileName = libsDirectory + options->currentArchitecture + u'/' + garbledFileName; } else if (QDir::fromNativeSeparators(qtDependency.relativePath).startsWith("jar/"_L1)) { destinationFileName = libsDirectory + qtDependency.relativePath.mid(sizeof("jar/") - 1); @@ -2615,11 +2721,17 @@ bool buildAndroidProject(const Options &options) gradleProperties["android.bundle.enableUncompressedNativeLibs"] = "false"; gradleProperties["buildDir"] = "build"; - gradleProperties["qtAndroidDir"] = (options.qtInstallDirectory + "/src/android/java"_L1).toUtf8(); + gradleProperties["qtAndroidDir"] = + (options.qtInstallDirectory + u'/' + options.qtDataDirectory + + "/src/android/java"_L1) + .toUtf8(); // The following property "qt5AndroidDir" is only for compatibility. // Projects using a custom build.gradle file may use this variable. // ### Qt7: Remove the following line - gradleProperties["qt5AndroidDir"] = (options.qtInstallDirectory + "/src/android/java"_L1).toUtf8(); + gradleProperties["qt5AndroidDir"] = + (options.qtInstallDirectory + u'/' + options.qtDataDirectory + + "/src/android/java"_L1) + .toUtf8(); gradleProperties["androidCompileSdkVersion"] = options.androidPlatform.split(u'-').last().toLocal8Bit(); gradleProperties["qtMinSdkVersion"] = options.minSdkVersion; gradleProperties["qtTargetSdkVersion"] = options.targetSdkVersion; @@ -3131,7 +3243,9 @@ int main(int argc, char *argv[]) for (auto it = options.architectures.constBegin(); it != options.architectures.constEnd(); ++it) { if (!it->enabled) continue; - options.setCurrentQtArchitecture(it.key(), it.value().qtInstallDirectory); + options.setCurrentQtArchitecture(it.key(), + it.value().qtInstallDirectory, + it.value().qtDirectories); // All architectures have a copy of the gradle files but only one set needs to be copied. if (!androidTemplatetCopied && options.build && !options.auxMode && !options.copyDependenciesOnly) {