From 979a21dc4ee0c6f483c6b55e9242a153d659ab6f Mon Sep 17 00:00:00 2001 From: Fab Stz Date: Wed, 5 Oct 2022 17:26:42 +0200 Subject: 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 --- src/tools/androiddeployqt/main.cpp | 164 +++++++++++++++++++++++++++++++------ 1 file changed, 139 insertions(+), 25 deletions(-) (limited to 'src/tools/androiddeployqt/main.cpp') 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) { -- cgit v1.2.3