diff options
Diffstat (limited to 'src/tools/androiddeployqt/main.cpp')
-rw-r--r-- | src/tools/androiddeployqt/main.cpp | 1143 |
1 files changed, 563 insertions, 580 deletions
diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 10bbd59bfb..6cc9f6102c 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -91,7 +91,7 @@ FILE *openProcess(const QString &command) #if defined(Q_OS_WIN32) QString processedCommand = QLatin1Char('\"') + command + QLatin1Char('\"'); #else - QString processedCommand = command; + const QString& processedCommand = command; #endif return popen(processedCommand.toLocal8Bit().constData(), QT_POPEN_READ); @@ -99,7 +99,7 @@ FILE *openProcess(const QString &command) struct QtDependency { - QtDependency(QString rpath, QString apath) : relativePath(rpath), absolutePath(apath) {} + QtDependency(const QString &rpath, const QString &apath) : relativePath(rpath), absolutePath(apath) {} bool operator==(const QtDependency &other) const { @@ -121,15 +121,15 @@ struct Options , auxMode(false) , deploymentMechanism(Bundled) , releasePackage(false) - , digestAlg(QLatin1String("SHA1")) - , sigAlg(QLatin1String("SHA1withRSA")) + , digestAlg(QLatin1String("SHA-256")) + , sigAlg(QLatin1String("SHA256withRSA")) , internalSf(false) , sectionsOnly(false) , protectedAuthenticationPath(false) , jarSigner(false) - , gdbServer(Auto) , installApk(false) , uninstallApk(false) + , qmlImportScannerBinaryPath() {} enum DeploymentMechanism @@ -150,7 +150,6 @@ struct Options bool generateAssetsFileList; bool build; bool auxMode; - bool stripLibraries = true; ActionTimer timer; // External tools @@ -175,22 +174,24 @@ struct Options // lib c++ path QString stdCppPath; - QString stdCppName = QStringLiteral("gnustl_shared"); + QString stdCppName = QStringLiteral("c++_shared"); // Build information QString androidPlatform; - QString architecture; - QString toolchainVersion; + QHash<QString, QString> architectures; + QString currentArchitecture; QString toolchainPrefix; - QString toolPrefix; - bool useLLVM = false; QString ndkHost; + bool buildAAB = false; + // Package information DeploymentMechanism deploymentMechanism; QString packageName; QStringList extraLibs; + QHash<QString, QStringList> archExtraLibs; QStringList extraPlugins; + QHash<QString, QStringList> archExtraPlugins; // Signing information bool releasePackage; @@ -211,23 +212,37 @@ struct Options bool jarSigner; QString apkPath; - // Gdbserver - TriState gdbServer; - // Installation information bool installApk; bool uninstallApk; QString installLocation; - // Collected information + // Per architecture collected information + void clear(const QString &arch) + { + currentArchitecture = arch; + } typedef QPair<QString, QString> BundledFile; - QList<BundledFile> bundledFiles; - QList<QtDependency> qtDependencies; - QStringList localLibs; + QHash<QString, QList<BundledFile>> bundledFiles; + QHash<QString, QList<QtDependency>> qtDependencies; + QHash<QString, QStringList> localLibs; + bool usesOpenGL = false; + + // Per package collected information QStringList localJars; QStringList initClasses; QStringList permissions; QStringList features; + + // Override qml import scanner path + QString qmlImportScannerBinaryPath; +}; + +static const QHash<QByteArray, QByteArray> elfArchitecures = { + {"aarch64", "arm64-v8a"}, + {"arm", "armeabi-v7a"}, + {"i386", "x86"}, + {"x86_64", "x86_64"} }; // Copy-pasted from qmake/library/ioutil.cpp @@ -250,7 +265,7 @@ static QString shellQuoteUnix(const QString &arg) }; // 0-32 \'"$`<>|;&(){}*?#!~[] if (!arg.length()) - return QString::fromLatin1("\"\""); + return QLatin1String("\"\""); QString ret(arg); if (hasSpecialChars(ret, iqm)) { @@ -273,7 +288,7 @@ static QString shellQuoteWin(const QString &arg) }; if (!arg.length()) - return QString::fromLatin1("\"\""); + return QLatin1String("\"\""); QString ret(arg); if (hasSpecialChars(ret, iqm)) { @@ -301,6 +316,59 @@ static QString shellQuote(const QString &arg) return shellQuoteUnix(arg); } +QString architecureFromName(const QString &name) +{ + QRegExp architecture(QStringLiteral(".*_(.*)\\.so")); + if (!architecture.exactMatch(name)) + return {}; + return architecture.capturedTexts().last(); +} + +QString fileArchitecture(const Options &options, const QString &path) +{ + auto arch = architecureFromName(path); + if (!arch.isEmpty()) + return arch; + + QString readElf = QLatin1String("%1/toolchains/%2/prebuilt/%3/bin/llvm-readobj").arg(options.ndkPath, + options.toolchainPrefix, + options.ndkHost); +#if defined(Q_OS_WIN32) + readElf += QLatin1String(".exe"); +#endif + + if (!QFile::exists(readElf)) { + fprintf(stderr, "Command does not exist: %s\n", qPrintable(readElf)); + return {}; + } + + readElf = QLatin1String("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(path)); + + FILE *readElfCommand = openProcess(readElf); + if (!readElfCommand) { + fprintf(stderr, "Cannot execute command %s\n", qPrintable(readElf)); + return {}; + } + + char buffer[512]; + while (fgets(buffer, sizeof(buffer), readElfCommand) != nullptr) { + QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer)); + QString library; + line = line.trimmed(); + if (line.startsWith("Arch: ")) { + auto it = elfArchitecures.find(line.mid(6)); + pclose(readElfCommand); + return it != elfArchitecures.constEnd() ? QString::fromLatin1(it.value()) : QString{}; + } + } + pclose(readElfCommand); + return {}; +} + +bool checkArchitecture(const Options &options, const QString &fileName) +{ + return fileArchitecture(options, fileName) == options.currentArchitecture; +} void deleteMissingFiles(const Options &options, const QDir &srcDir, const QDir &dstDir) { @@ -350,7 +418,10 @@ Options parseOptions() options.helpRequested = true; else options.inputFileName = arguments.at(++i); - } else if (argument.compare(QLatin1String("--no-build"), Qt::CaseInsensitive) == 0) { + } else if (argument.compare(QLatin1String("--aab"), Qt::CaseInsensitive) == 0) { + options.buildAAB = true; + options.build = true; + } else if (options.buildAAB && argument.compare(QLatin1String("--no-build"), Qt::CaseInsensitive) == 0) { options.build = false; } else if (argument.compare(QLatin1String("--install"), Qt::CaseInsensitive) == 0) { options.installApk = true; @@ -388,10 +459,6 @@ Options parseOptions() options.installLocation = arguments.at(++i); } else if (argument.compare(QLatin1String("--release"), Qt::CaseInsensitive) == 0) { options.releasePackage = true; - } else if (argument.compare(QLatin1String("--gdbserver"), Qt::CaseInsensitive) == 0) { - options.gdbServer = Options::True; - } else if (argument.compare(QLatin1String("--no-gdbserver"), Qt::CaseInsensitive) == 0) { - options.gdbServer = Options::False; } else if (argument.compare(QLatin1String("--jdk"), Qt::CaseInsensitive) == 0) { if (i + 1 == arguments.size()) options.helpRequested = true; @@ -462,13 +529,13 @@ Options parseOptions() options.generateAssetsFileList = false; } else if (argument.compare(QLatin1String("--aux-mode"), Qt::CaseInsensitive) == 0) { options.auxMode = true; - } else if (argument.compare(QLatin1String("--no-strip"), Qt::CaseInsensitive) == 0) { - options.stripLibraries = false; + } else if (argument.compare(QLatin1String("--qml-importscanner-binary"), Qt::CaseInsensitive) == 0) { + options.qmlImportScannerBinaryPath = arguments.at(++i).trimmed(); } } if (options.inputFileName.isEmpty()) - options.inputFileName = QString::fromLatin1("android-lib%1.so-deployment-settings.json").arg(QDir::current().dirName()); + options.inputFileName = QLatin1String("android-lib%1.so-deployment-settings.json").arg(QDir::current().dirName()); options.timing = qEnvironmentVariableIsSet("ANDROIDDEPLOYQT_TIMING_OUTPUT"); @@ -497,6 +564,7 @@ void printHelp() " --deployment <mechanism>: Supported deployment mechanisms:\n" " bundled (default): Include Qt files in stand-alone package.\n" " ministro: Use the Ministro service to manage Qt files.\n" + " --aab: Build an Android App Bundle.\n" " --no-build: Do not build the package, it is useful to just install\n" " a package previously built.\n" " --install: Installs apk to device/emulator. By default this step is\n" @@ -533,10 +601,6 @@ void printHelp() " --protected: Keystore has protected authentication path.\n" " --jarsigner: Force jarsigner usage, otherwise apksigner will be\n" " used if available.\n" - " --gdbserver: Adds the gdbserver to the package. By default the gdbserver\n" - " is bundled for debug pacakges.\n" - " --no-gdbserver: Prevents the gdbserver from being added to the package\n" - " By default the gdbserver is bundled for debug pacakges.\n" " --jdk <path/to/jdk>: Used to find the jarsigner tool when used\n" " in combination with the --release argument. By default,\n" " an attempt is made to detect the tool using the JAVA_HOME and\n" @@ -549,8 +613,11 @@ void printHelp() " --aux-mode: Operate in auxiliary mode. This will only copy the\n" " dependencies into the build directory and update the XML templates.\n" " The project will not be built or installed.\n" - " --no-strip: Do not strip debug symbols from libraries.\n" " --apk <path/where/to/copy/the/apk>: Path where to copy the built apk.\n" + " --qml-importscanner-binary <path/to/qmlimportscanner>: Override the\n" + " default qmlimportscanner binary path. By default the\n" + " qmlimportscanner binary is located using the Qt directory\n" + " specified in the input file.\n" " --help: Displays this information.\n\n", qPrintable(QCoreApplication::arguments().at(0)) ); @@ -580,9 +647,10 @@ bool alwaysOverwritableFile(const QString &fileName) || fileName.endsWith(QLatin1String("/src/org/qtproject/qt5/android/bindings/QtActivity.java"))); } + bool copyFileIfNewer(const QString &sourceFileName, const QString &destinationFileName, - bool verbose, + const Options &options, bool forceOverwrite = false) { if (QFile::exists(destinationFileName)) { @@ -592,7 +660,7 @@ bool copyFileIfNewer(const QString &sourceFileName, if (!forceOverwrite && sourceFileInfo.lastModified() <= destinationFileInfo.lastModified() && !alwaysOverwritableFile(destinationFileName)) { - if (verbose) + if (options.verbose) fprintf(stdout, " -- Skipping file %s. Same or newer file already in place.\n", qPrintable(sourceFileName)); return true; } else { @@ -611,11 +679,10 @@ bool copyFileIfNewer(const QString &sourceFileName, if (!QFile::exists(destinationFileName) && !QFile::copy(sourceFileName, destinationFileName)) { fprintf(stderr, "Failed to copy %s to %s.\n", qPrintable(sourceFileName), qPrintable(destinationFileName)); return false; - } else if (verbose) { + } else if (options.verbose) { fprintf(stdout, " -- Copied %s\n", qPrintable(destinationFileName)); fflush(stdout); } - return true; } @@ -708,7 +775,7 @@ QString packageNameFromAndroidManifest(const QString &androidManifestPath) reader.attributes().value(QLatin1String("package")).toString()); } } - return QString(); + return {}; } bool readInputFile(Options *options) @@ -750,13 +817,13 @@ bool readInputFile(Options *options) { - const QJsonValue value = jsonObject.value(QStringLiteral("sdkBuildToolsRevision")); + const QJsonValue value = jsonObject.value(QLatin1String("sdkBuildToolsRevision")); if (!value.isUndefined()) options->sdkBuildToolsVersion = value.toString(); } { - const QJsonValue qtInstallDirectory = jsonObject.value(QStringLiteral("qt")); + const QJsonValue qtInstallDirectory = jsonObject.value(QLatin1String("qt")); if (qtInstallDirectory.isUndefined()) { fprintf(stderr, "No Qt directory in json file %s\n", qPrintable(options->inputFileName)); return false; @@ -773,13 +840,13 @@ bool readInputFile(Options *options) } { - const QJsonValue androidSourcesDirectory = jsonObject.value(QStringLiteral("android-package-source-directory")); + const QJsonValue androidSourcesDirectory = jsonObject.value(QLatin1String("android-package-source-directory")); if (!androidSourcesDirectory.isUndefined()) options->androidSourceDirectory = androidSourcesDirectory.toString(); } { - const QJsonValue androidVersionName = jsonObject.value(QStringLiteral("android-version-name")); + const QJsonValue androidVersionName = jsonObject.value(QLatin1String("android-version-name")); if (!androidVersionName.isUndefined()) options->versionName = androidVersionName.toString(); else @@ -787,7 +854,7 @@ bool readInputFile(Options *options) } { - const QJsonValue androidVersionCode = jsonObject.value(QStringLiteral("android-version-code")); + const QJsonValue androidVersionCode = jsonObject.value(QLatin1String("android-version-code")); if (!androidVersionCode.isUndefined()) options->versionCode = androidVersionCode.toString(); else @@ -795,55 +862,24 @@ bool readInputFile(Options *options) } { - const QJsonValue applicationBinary = jsonObject.value(QStringLiteral("application-binary")); - if (applicationBinary.isUndefined()) { - fprintf(stderr, "No application binary defined in json file.\n"); - return false; - } - options->applicationBinary = applicationBinary.toString(); - - if (!QFile::exists(options->applicationBinary)) { - fprintf(stderr, "Cannot find application binary %s.\n", qPrintable(options->applicationBinary)); + const QJsonObject targetArchitectures = jsonObject.value(QLatin1String("architectures")).toObject(); + if (targetArchitectures.isEmpty()) { + fprintf(stderr, "No target architecture defined in json file.\n"); return false; } - } - - { - const QJsonValue deploymentDependencies = jsonObject.value(QStringLiteral("deployment-dependencies")); - if (!deploymentDependencies.isUndefined()) { - QString deploymentDependenciesString = deploymentDependencies.toString(); - const auto dependencies = deploymentDependenciesString.splitRef(QLatin1Char(',')); - for (const QStringRef &dependency : dependencies) { - QString path = options->qtInstallDirectory + QLatin1Char('/') + dependency; - if (QFileInfo(path).isDir()) { - QDirIterator iterator(path, QDirIterator::Subdirectories); - while (iterator.hasNext()) { - iterator.next(); - if (iterator.fileInfo().isFile()) { - QString subPath = iterator.filePath(); - options->qtDependencies.append(QtDependency(subPath.mid(options->qtInstallDirectory.length() + 1), - subPath)); - } - } - } else { - options->qtDependencies.append(QtDependency(dependency.toString(), path)); - } + for (auto it = targetArchitectures.constBegin(); it != targetArchitectures.constEnd(); ++it) { + if (it.value().isUndefined()) { + fprintf(stderr, "Invalid architecure.\n"); + return false; } + if (it.value().isNull()) + continue; + options->architectures.insert(it.key(), it.value().toString()); } } - - { - const QJsonValue targetArchitecture = jsonObject.value(QStringLiteral("target-architecture")); - if (targetArchitecture.isUndefined()) { - fprintf(stderr, "No target architecture defined in json file.\n"); - return false; - } - options->architecture = targetArchitecture.toString(); - } - { - const QJsonValue ndk = jsonObject.value(QStringLiteral("ndk")); + const QJsonValue ndk = jsonObject.value(QLatin1String("ndk")); if (ndk.isUndefined()) { fprintf(stderr, "No NDK path defined in json file.\n"); return false; @@ -852,12 +888,7 @@ bool readInputFile(Options *options) } { - const QJsonValue value = jsonObject.value(QStringLiteral("useLLVM")); - options->useLLVM = value.toBool(false); - } - - { - const QJsonValue toolchainPrefix = jsonObject.value(QStringLiteral("toolchain-prefix")); + const QJsonValue toolchainPrefix = jsonObject.value(QLatin1String("toolchain-prefix")); if (toolchainPrefix.isUndefined()) { fprintf(stderr, "No toolchain prefix defined in json file.\n"); return false; @@ -866,26 +897,7 @@ bool readInputFile(Options *options) } { - const QJsonValue toolPrefix = jsonObject.value(QStringLiteral("tool-prefix")); - if (toolPrefix.isUndefined()) { - fprintf(stderr, "Warning: No tool prefix defined in json file.\n"); - options->toolPrefix = options->toolchainPrefix; - } else { - options->toolPrefix = toolPrefix.toString(); - } - } - - if (!options->useLLVM) { - const QJsonValue toolchainVersion = jsonObject.value(QStringLiteral("toolchain-version")); - if (toolchainVersion.isUndefined()) { - fprintf(stderr, "No toolchain version defined in json file.\n"); - return false; - } - options->toolchainVersion = toolchainVersion.toString(); - } - - { - const QJsonValue ndkHost = jsonObject.value(QStringLiteral("ndk-host")); + const QJsonValue ndkHost = jsonObject.value(QLatin1String("ndk-host")); if (ndkHost.isUndefined()) { fprintf(stderr, "No NDK host defined in json file.\n"); return false; @@ -893,52 +905,106 @@ bool readInputFile(Options *options) options->ndkHost = ndkHost.toString(); } - options->packageName = packageNameFromAndroidManifest(options->androidSourceDirectory + QLatin1String("/AndroidManifest.xml")); - if (options->packageName.isEmpty()) - options->packageName = cleanPackageName(QString::fromLatin1("org.qtproject.example.%1").arg(QFileInfo(options->applicationBinary).baseName().mid(sizeof("lib") - 1))); - { - const QJsonValue extraLibs = jsonObject.value(QStringLiteral("android-extra-libs")); + const QJsonValue extraLibs = jsonObject.value(QLatin1String("android-extra-libs")); if (!extraLibs.isUndefined()) options->extraLibs = extraLibs.toString().split(QLatin1Char(','), QString::SkipEmptyParts); } { - const QJsonValue extraPlugins = jsonObject.value(QStringLiteral("android-extra-plugins")); + const QJsonValue extraPlugins = jsonObject.value(QLatin1String("android-extra-plugins")); if (!extraPlugins.isUndefined()) options->extraPlugins = extraPlugins.toString().split(QLatin1Char(',')); } { - const QJsonValue stdcppPath = jsonObject.value(QStringLiteral("stdcpp-path")); + const QJsonValue stdcppPath = jsonObject.value(QLatin1String("stdcpp-path")); if (stdcppPath.isUndefined()) { fprintf(stderr, "No stdcpp-path defined in json file.\n"); return false; } options->stdCppPath = stdcppPath.toString(); - auto name = QFileInfo(options->stdCppPath).baseName(); - if (!name.startsWith(QLatin1String("lib"))) { - fprintf(stderr, "Invalid STD C++ library name.\n"); - return false; - } - options->stdCppName = name.mid(3); } { - const QJsonValue qmlRootPath = jsonObject.value(QStringLiteral("qml-root-path")); + const QJsonValue qmlRootPath = jsonObject.value(QLatin1String("qml-root-path")); if (!qmlRootPath.isUndefined()) options->rootPath = qmlRootPath.toString(); } { - const QJsonValue qmlImportPaths = jsonObject.value(QStringLiteral("qml-import-paths")); + const QJsonValue qmlImportPaths = jsonObject.value(QLatin1String("qml-import-paths")); if (!qmlImportPaths.isUndefined()) options->qmlImportPaths = qmlImportPaths.toString().split(QLatin1Char(',')); } + + { + const QJsonValue qmlImportScannerBinaryPath = jsonObject.value(QLatin1String("qml-importscanner-binary")); + if (!qmlImportScannerBinaryPath.isUndefined()) + options->qmlImportScannerBinaryPath = qmlImportScannerBinaryPath.toString(); + } + + { + const QJsonValue applicationBinary = jsonObject.value(QLatin1String("application-binary")); + if (applicationBinary.isUndefined()) { + fprintf(stderr, "No application binary defined in json file.\n"); + return false; + } + options->applicationBinary = applicationBinary.toString(); + if (options->build) { + for (auto it = options->architectures.constBegin(); it != options->architectures.constEnd(); ++it) { + if (!QFile::exists(QLatin1String("%1/libs/%2/lib%3_%2.so").arg(options->outputDirectory, it.key(), options->applicationBinary))) { + fprintf(stderr, "Cannot find application binary %s.\n", qPrintable(options->applicationBinary)); + return false; + } + } + } + } + + { + const QJsonValue deploymentDependencies = jsonObject.value(QLatin1String("deployment-dependencies")); + if (!deploymentDependencies.isUndefined()) { + QString deploymentDependenciesString = deploymentDependencies.toString(); + const auto dependencies = deploymentDependenciesString.splitRef(QLatin1Char(',')); + for (const QStringRef &dependency : dependencies) { + QString path = options->qtInstallDirectory + QLatin1Char('/') + dependency; + if (QFileInfo(path).isDir()) { + QDirIterator iterator(path, QDirIterator::Subdirectories); + while (iterator.hasNext()) { + iterator.next(); + if (iterator.fileInfo().isFile()) { + QString subPath = iterator.filePath(); + auto arch = fileArchitecture(*options, subPath); + if (!arch.isEmpty()) { + options->qtDependencies[arch].append(QtDependency(subPath.mid(options->qtInstallDirectory.length() + 1), + subPath)); + } else if (options->verbose) { + fprintf(stderr, "Skipping \"%s\", unknown architecture\n", qPrintable(subPath)); + fflush(stderr); + } + } + } + } else { + auto arch = fileArchitecture(*options, path); + if (!arch.isEmpty()) { + options->qtDependencies[arch].append(QtDependency(dependency.toString(), path)); + } else if (options->verbose) { + fprintf(stderr, "Skipping \"%s\", unknown architecture\n", qPrintable(path)); + fflush(stderr); + } + } + } + } + } + + options->packageName = packageNameFromAndroidManifest(options->androidSourceDirectory + QLatin1String("/AndroidManifest.xml")); + if (options->packageName.isEmpty()) + options->packageName = cleanPackageName(QLatin1String("org.qtproject.example.%1").arg(options->applicationBinary)); + return true; } -bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, bool verbose, bool forceOverwrite = false) +bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, const Options &options, bool forceOverwrite = false) { const QFileInfoList entries = sourceDirectory.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); for (const QFileInfo &entry : entries) { @@ -949,11 +1015,11 @@ bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, bo return false; } - if (!copyFiles(dir, QDir(destinationDirectory.path() + QLatin1String("/") + dir.dirName()), verbose, forceOverwrite)) + if (!copyFiles(dir, QDir(destinationDirectory.path() + QLatin1Char('/') + dir.dirName()), options, forceOverwrite)) return false; } else { QString destination = destinationDirectory.absoluteFilePath(entry.fileName()); - if (!copyFileIfNewer(entry.absoluteFilePath(), destination, verbose, forceOverwrite)) + if (!copyFileIfNewer(entry.absoluteFilePath(), destination, options, forceOverwrite)) return false; } } @@ -993,7 +1059,7 @@ bool copyAndroidTemplate(const Options &options, const QString &androidTemplate, return false; } - return copyFiles(sourceDirectory, QDir(outDir), options.verbose); + return copyFiles(sourceDirectory, QDir(outDir), options); } bool copyGradleTemplate(const Options &options) @@ -1010,7 +1076,7 @@ bool copyGradleTemplate(const Options &options) return false; } - return copyFiles(sourceDirectory, QDir(outDir), options.verbose); + return copyFiles(sourceDirectory, QDir(outDir), options); } bool copyAndroidTemplate(const Options &options) @@ -1041,38 +1107,42 @@ bool copyAndroidSources(const Options &options) return false; } - return copyFiles(sourceDirectory, QDir(options.outputDirectory), options.verbose, true); + return copyFiles(sourceDirectory, QDir(options.outputDirectory), options, true); } -bool copyAndroidExtraLibs(const Options &options) +bool copyAndroidExtraLibs(Options *options) { - if (options.extraLibs.isEmpty()) + if (options->extraLibs.isEmpty()) return true; - if (options.verbose) - fprintf(stdout, "Copying %d external libraries to package.\n", options.extraLibs.size()); + if (options->verbose) + fprintf(stdout, "Copying %d external libraries to package.\n", options->extraLibs.size()); - for (const QString &extraLib : options.extraLibs) { + for (const QString &extraLib : options->extraLibs) { QFileInfo extraLibInfo(extraLib); if (!extraLibInfo.exists()) { fprintf(stderr, "External library %s does not exist!\n", qPrintable(extraLib)); return false; } - + if (!checkArchitecture(*options, extraLibInfo.filePath())) { + if (options->verbose) + fprintf(stdout, "Skipping \"%s\", architecture mismatch.\n", qPrintable(extraLib)); + continue; + } if (!extraLibInfo.fileName().startsWith(QLatin1String("lib")) || extraLibInfo.suffix() != QLatin1String("so")) { fprintf(stderr, "The file name of external library %s must begin with \"lib\" and end with the suffix \".so\".\n", qPrintable(extraLib)); return false; } - - QString destinationFile(options.outputDirectory + QString destinationFile(options->outputDirectory + QLatin1String("/libs/") - + options.architecture + + options->currentArchitecture + QLatin1Char('/') + extraLibInfo.fileName()); - if (!copyFileIfNewer(extraLib, destinationFile, options.verbose)) + if (!copyFileIfNewer(extraLib, destinationFile, *options)) return false; + options->archExtraLibs[options->currentArchitecture] += extraLib; } return true; @@ -1093,15 +1163,15 @@ QStringList allFilesInside(const QDir& current, const QDir& rootDir) return result; } -bool copyAndroidExtraResources(const Options &options) +bool copyAndroidExtraResources(Options *options) { - if (options.extraPlugins.isEmpty()) + if (options->extraPlugins.isEmpty()) return true; - if (options.verbose) - fprintf(stdout, "Copying %d external resources to package.\n", options.extraPlugins.size()); + if (options->verbose) + fprintf(stdout, "Copying %d external resources to package.\n", options->extraPlugins.size()); - for (const QString &extraResource : options.extraPlugins) { + for (const QString &extraResource : options->extraPlugins) { QFileInfo extraResourceInfo(extraResource); if (!extraResourceInfo.exists() || !extraResourceInfo.isDir()) { fprintf(stderr, "External resource %s does not exist or not a correct directory!\n", qPrintable(extraResource)); @@ -1109,8 +1179,8 @@ bool copyAndroidExtraResources(const Options &options) } QDir resourceDir(extraResource); - QString assetsDir = options.outputDirectory + QStringLiteral("/assets/") + resourceDir.dirName() + QLatin1Char('/'); - QString libsDir = options.outputDirectory + QStringLiteral("/libs/") + options.architecture + QLatin1Char('/'); + QString assetsDir = options->outputDirectory + QLatin1String("/assets/") + resourceDir.dirName() + QLatin1Char('/'); + QString libsDir = options->outputDirectory + QLatin1String("/libs/") + options->currentArchitecture + QLatin1Char('/'); const QStringList files = allFilesInside(resourceDir, resourceDir); for (const QString &resourceFile : files) { @@ -1119,10 +1189,12 @@ bool copyAndroidExtraResources(const Options &options) if (!resourceFile.endsWith(QLatin1String(".so"))) { destinationFile = assetsDir + resourceFile; } else { - destinationFile = libsDir + QStringLiteral("/lib") + QString(resourceDir.dirName() + QLatin1Char('/') + resourceFile).replace(QLatin1Char('/'), QLatin1Char('_')); + if (!checkArchitecture(*options, originFile)) + continue; + destinationFile = libsDir + QLatin1String("/lib") + QString(resourceDir.dirName() + QLatin1Char('/') + resourceFile).replace(QLatin1Char('/'), QLatin1Char('_')); + options->archExtraPlugins[options->currentArchitecture] += resourceFile; } - - if (!copyFileIfNewer(originFile, destinationFile, options.verbose)) + if (!copyFileIfNewer(originFile, destinationFile, *options)) return false; } } @@ -1174,75 +1246,118 @@ bool updateFile(const QString &fileName, const QHash<QString, QString> &replacem } -bool updateLibsXml(const Options &options) +bool updateLibsXml(Options *options) { - if (options.verbose) + if (options->verbose) fprintf(stdout, " -- res/values/libs.xml\n"); - QString fileName = options.outputDirectory + QLatin1String("/res/values/libs.xml"); + QString fileName = options->outputDirectory + QLatin1String("/res/values/libs.xml"); if (!QFile::exists(fileName)) { fprintf(stderr, "Cannot find %s in prepared packaged. This file is required.\n", qPrintable(fileName)); return false; } - QString libsPath = QLatin1String("libs/") + options.architecture + QLatin1Char('/'); - - QString qtLibs = QLatin1String("<item>") + options.stdCppName + QLatin1String("</item>\n"); + QString qtLibs; QString bundledInLibs; QString bundledInAssets; - for (const Options::BundledFile &bundledFile : options.bundledFiles) { - if (bundledFile.second.startsWith(QLatin1String("lib/"))) { - QString s = bundledFile.second.mid(sizeof("lib/lib") - 1); - s.chop(sizeof(".so") - 1); - qtLibs += QString::fromLatin1("<item>%1</item>\n").arg(s); - } else if (bundledFile.first.startsWith(libsPath)) { - QString s = bundledFile.first.mid(libsPath.length()); - bundledInLibs += QString::fromLatin1("<item>%1:%2</item>\n") - .arg(s).arg(bundledFile.second); - } else if (bundledFile.first.startsWith(QLatin1String("assets/"))) { - QString s = bundledFile.first.mid(sizeof("assets/") - 1); - bundledInAssets += QString::fromLatin1("<item>%1:%2</item>\n") - .arg(s).arg(bundledFile.second); - } - } - - if (!options.extraPlugins.isEmpty()) { - for (const QString &extraRes : options.extraPlugins) { - QDir resourceDir(extraRes); - const QStringList files = allFilesInside(resourceDir, resourceDir); - for (const QString &file : files) { - QString destinationPath = resourceDir.dirName() + QLatin1Char('/') + file; - if (!file.endsWith(QLatin1String(".so"))) { - bundledInAssets += QStringLiteral("<item>%1:%1</item>\n") - .arg(destinationPath); - } else { - bundledInLibs += QStringLiteral("<item>lib%1:%2</item>\n") - .arg(QString(destinationPath).replace(QLatin1Char('/'), QLatin1Char('_'))) - .arg(destinationPath); + QString allLocalLibs; + QString extraLibs; + + for (auto it = options->architectures.constBegin(); it != options->architectures.constEnd(); ++it) { + QString libsPath = QLatin1String("libs/") + it.key() + QLatin1Char('/'); + + qtLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), options->stdCppName); + for (const Options::BundledFile &bundledFile : options->bundledFiles[it.key()]) { + if (bundledFile.second.startsWith(QLatin1String("lib/"))) { + QString s = bundledFile.second.mid(sizeof("lib/lib") - 1); + s.chop(sizeof(".so") - 1); + qtLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), s); + } else if (bundledFile.first.startsWith(libsPath)) { + QString s = bundledFile.first.mid(libsPath.length()); + bundledInLibs += QString::fromLatin1(" <item>%1;%2:%3</item>\n") + .arg(it.key(), s, bundledFile.second); + } else if (bundledFile.first.startsWith(QLatin1String("assets/"))) { + QString s = bundledFile.first.mid(sizeof("assets/") - 1); + bundledInAssets += QString::fromLatin1(" <item>%1:%2</item>\n") + .arg(s, bundledFile.second); + } + } + + if (!options->archExtraPlugins[it.key()].isEmpty()) { + for (const QString &extraRes : options->archExtraPlugins[it.key()]) { + QDir resourceDir(extraRes); + const QStringList files = allFilesInside(resourceDir, resourceDir); + for (const QString &file : files) { + QString destinationPath = resourceDir.dirName() + QLatin1Char('/') + file; + if (!file.endsWith(QLatin1String(".so"))) { + bundledInAssets += QLatin1String(" <item>%1:%1</item>\n") + .arg(destinationPath); + } else { + bundledInLibs += QLatin1String(" <item>%1;lib%2:%3</item>\n") + .arg(it.key(), + QString(destinationPath).replace(QLatin1Char('/'), QLatin1Char('_')), + destinationPath); + } } } } - } - QHash<QString, QString> replacements; - replacements[QLatin1String("<!-- %%INSERT_QT_LIBS%% -->")] = qtLibs; + if (!options->archExtraLibs[it.key()].isEmpty()) { + for (const QString &extraLib : options->archExtraLibs[it.key()]) { + QFileInfo extraLibInfo(extraLib); + QString name = extraLibInfo.fileName().mid(sizeof("lib") - 1); + name.chop(sizeof(".so") - 1); + extraLibs += QLatin1String(" <item>%1;%2").arg(it.key(), name); + } + } - if (options.deploymentMechanism == Options::Bundled) { - replacements[QLatin1String("<!-- %%INSERT_BUNDLED_IN_LIB%% -->")] = bundledInLibs; - replacements[QLatin1String("<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->")] = bundledInAssets; - } + QStringList localLibs; + localLibs = options->localLibs[it.key()]; + // If .pro file overrides dependency detection, we need to see which platform plugin they picked + if (localLibs.isEmpty()) { + QString plugin; + for (const QtDependency &qtDependency : options->qtDependencies[it.key()]) { + if (qtDependency.relativePath.endsWith(QLatin1String("libqtforandroid.so")) + || qtDependency.relativePath.endsWith(QLatin1String("libqtforandroidGL.so"))) { + if (!plugin.isEmpty() && plugin != qtDependency.relativePath) { + fprintf(stderr, "Both platform plugins libqtforandroid.so and libqtforandroidGL.so included in package. Please include only one.\n"); + return false; + } - QString extraLibs; - if (!options.extraLibs.isEmpty()) { - for (const QString extraLib : options.extraLibs) { - QFileInfo extraLibInfo(extraLib); - QString name = extraLibInfo.fileName().mid(sizeof("lib") - 1); - name.chop(sizeof(".so") - 1); + plugin = qtDependency.relativePath; + } + if (qtDependency.relativePath.contains(QLatin1String("libQt5OpenGL")) + || qtDependency.relativePath.contains(QLatin1String("libQt5Quick"))) { + options->usesOpenGL |= true; + break; + } + } + + if (plugin.isEmpty()) { + fflush(stdout); + fprintf(stderr, "No platform plugin, neither libqtforandroid.so or libqtforandroidGL.so, included in package. Please include one.\n"); + fflush(stderr); + return false; + } - extraLibs += QLatin1String("<item>") + name + QLatin1String("</item>\n"); + localLibs.append(plugin); + if (options->verbose) + fprintf(stdout, " -- Using platform plugin %s\n", qPrintable(plugin)); } + allLocalLibs += QLatin1String(" <item>%1;%2</item>\n").arg(it.key(), localLibs.join(QLatin1Char(':')) + .replace(QLatin1String("lib/"), QString{}) + .replace(QLatin1Char('/'), QLatin1Char('_'))); + } + + QHash<QString, QString> replacements; + replacements[QStringLiteral("<!-- %%INSERT_QT_LIBS%% -->")] += qtLibs.trimmed(); + replacements[QStringLiteral("<!-- %%INSERT_LOCAL_LIBS%% -->")] = allLocalLibs.trimmed(); + replacements[QStringLiteral("<!-- %%INSERT_EXTRA_LIBS%% -->")] = extraLibs.trimmed(); + + if (options->deploymentMechanism == Options::Bundled) { + replacements[QStringLiteral("<!-- %%INSERT_BUNDLED_IN_LIB%% -->")] += bundledInLibs.trimmed(); + replacements[QStringLiteral("<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->")] += bundledInAssets.trimmed(); } - replacements[QLatin1String("<!-- %%INSERT_EXTRA_LIBS%% -->")] = extraLibs; if (!updateFile(fileName, replacements)) return false; @@ -1256,7 +1371,7 @@ bool updateStringsXml(const Options &options) fprintf(stdout, " -- res/values/strings.xml\n"); QHash<QString, QString> replacements; - replacements[QStringLiteral("<!-- %%INSERT_APP_NAME%% -->")] = QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1); + replacements[QStringLiteral("<!-- %%INSERT_APP_NAME%% -->")] = options.applicationBinary; QString fileName = options.outputDirectory + QLatin1String("/res/values/strings.xml"); if (!QFile::exists(fileName)) { @@ -1268,7 +1383,7 @@ bool updateStringsXml(const Options &options) return false; } file.write(QByteArray("<?xml version='1.0' encoding='utf-8'?><resources><string name=\"app_name\" translatable=\"false\">") - .append(QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1).toLatin1()) + .append(options.applicationBinary.toLatin1()) .append("</string></resources>\n")); return true; } @@ -1284,71 +1399,34 @@ bool updateAndroidManifest(Options &options) if (options.verbose) fprintf(stdout, " -- AndroidManifest.xml \n"); - QStringList localLibs = options.localLibs; - - // If .pro file overrides dependency detection, we need to see which platform plugin they picked - if (localLibs.isEmpty()) { - QString plugin; - for (const QtDependency &qtDependency : qAsConst(options.qtDependencies)) { - if (qtDependency.relativePath.endsWith(QLatin1String("libqtforandroid.so")) - || qtDependency.relativePath.endsWith(QLatin1String("libqtforandroidGL.so"))) { - if (!plugin.isEmpty() && plugin != qtDependency.relativePath) { - fprintf(stderr, "Both platform plugins libqtforandroid.so and libqtforandroidGL.so included in package. Please include only one.\n"); - return false; - } - - plugin = qtDependency.relativePath; - } - } - - if (plugin.isEmpty()) { - fprintf(stderr, "No platform plugin, neither libqtforandroid.so or libqtforandroidGL.so, included in package. Please include one.\n"); - return false; - } - - localLibs.append(plugin); - if (options.verbose) - fprintf(stdout, " -- Using platform plugin %s\n", qPrintable(plugin)); - } - - bool usesGL = false; - for (const QtDependency &qtDependency : qAsConst(options.qtDependencies)) { - if (qtDependency.relativePath.endsWith(QLatin1String("libQt5OpenGL.so")) - || qtDependency.relativePath.endsWith(QLatin1String("libQt5Quick.so"))) { - usesGL = true; - break; - } - } - options.localJars.removeDuplicates(); options.initClasses.removeDuplicates(); QHash<QString, QString> replacements; - replacements[QLatin1String("-- %%INSERT_APP_NAME%% --")] = QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1); - replacements[QLatin1String("-- %%INSERT_APP_LIB_NAME%% --")] = QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1); - replacements[QLatin1String("-- %%INSERT_LOCAL_LIBS%% --")] = localLibs.join(QLatin1Char(':')); - replacements[QLatin1String("-- %%INSERT_LOCAL_JARS%% --")] = options.localJars.join(QLatin1Char(':')); - replacements[QLatin1String("-- %%INSERT_INIT_CLASSES%% --")] = options.initClasses.join(QLatin1Char(':')); - replacements[QLatin1String("-- %%INSERT_VERSION_NAME%% --")] = options.versionName; - replacements[QLatin1String("-- %%INSERT_VERSION_CODE%% --")] = options.versionCode; - replacements[QLatin1String("package=\"org.qtproject.example\"")] = QString::fromLatin1("package=\"%1\"").arg(options.packageName); - replacements[QLatin1String("-- %%BUNDLE_LOCAL_QT_LIBS%% --")] - = (options.deploymentMechanism == Options::Bundled) ? QString::fromLatin1("1") : QString::fromLatin1("0"); - replacements[QLatin1String("-- %%USE_LOCAL_QT_LIBS%% --")] - = (options.deploymentMechanism != Options::Ministro) ? QString::fromLatin1("1") : QString::fromLatin1("0"); + replacements[QStringLiteral("-- %%INSERT_APP_NAME%% --")] = options.applicationBinary; + 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(':')); + replacements[QStringLiteral("-- %%INSERT_VERSION_NAME%% --")] = options.versionName; + replacements[QStringLiteral("-- %%INSERT_VERSION_CODE%% --")] = options.versionCode; + replacements[QStringLiteral("package=\"org.qtproject.example\"")] = QLatin1String("package=\"%1\"").arg(options.packageName); + replacements[QStringLiteral("-- %%BUNDLE_LOCAL_QT_LIBS%% --")] + = (options.deploymentMechanism == Options::Bundled) ? QLatin1String("1") : QLatin1String("0"); + replacements[QStringLiteral("-- %%USE_LOCAL_QT_LIBS%% --")] + = (options.deploymentMechanism != Options::Ministro) ? QLatin1String("1") : QLatin1String("0"); QString permissions; for (const QString &permission : qAsConst(options.permissions)) - permissions += QString::fromLatin1(" <uses-permission android:name=\"%1\" />\n").arg(permission); - replacements[QLatin1String("<!-- %%INSERT_PERMISSIONS -->")] = permissions; + permissions += QLatin1String(" <uses-permission android:name=\"%1\" />\n").arg(permission); + replacements[QStringLiteral("<!-- %%INSERT_PERMISSIONS -->")] = permissions.trimmed(); QString features; for (const QString &feature : qAsConst(options.features)) - features += QStringLiteral(" <uses-feature android:name=\"%1\" android:required=\"false\" />\n").arg(feature); - if (usesGL) - features += QStringLiteral(" <uses-feature android:glEsVersion=\"0x00020000\" android:required=\"true\" />"); + features += QLatin1String(" <uses-feature android:name=\"%1\" android:required=\"false\" />\n").arg(feature); + if (options.usesOpenGL) + features += QLatin1String(" <uses-feature android:glEsVersion=\"0x00020000\" android:required=\"true\" />"); - replacements[QLatin1String("<!-- %%INSERT_FEATURES -->")] = features; + replacements[QStringLiteral("<!-- %%INSERT_FEATURES -->")] = features.trimmed(); QString androidManifestPath = options.outputDirectory + QLatin1String("/AndroidManifest.xml"); if (!updateFile(androidManifestPath, replacements)) @@ -1409,7 +1487,7 @@ bool updateAndroidFiles(Options &options) if (options.verbose) fprintf(stdout, "Updating Android package files with project settings.\n"); - if (!updateLibsXml(options)) + if (!updateLibsXml(&options)) return false; if (!updateAndroidManifest(options)) @@ -1466,7 +1544,7 @@ bool readAndroidDependencyXml(Options *options, QSet<QString> *usedDependencies, QSet<QString> *remainingDependencies) { - QString androidDependencyName = absoluteFilePath(options, QString::fromLatin1("/lib/%1-android-dependencies.xml").arg(moduleName)); + QString androidDependencyName = absoluteFilePath(options, QLatin1String("/lib/%1-android-dependencies.xml").arg(moduleName)); QFile androidDependencyFile(androidDependencyName); if (androidDependencyFile.exists()) { @@ -1505,7 +1583,7 @@ bool readAndroidDependencyXml(Options *options, if (options->verbose) fprintf(stdout, "Appending dependency from xml: %s\n", qPrintable(fileName.relativePath)); - options->qtDependencies.append(fileName); + options->qtDependencies[options->currentArchitecture].append(fileName); } } else if (reader.name() == QLatin1String("jar")) { int bundling = reader.attributes().value(QLatin1String("bundling")).toInt(); @@ -1513,7 +1591,7 @@ bool readAndroidDependencyXml(Options *options, if (bundling == (options->deploymentMechanism == Options::Bundled)) { QtDependency dependency(fileName, absoluteFilePath(options, fileName)); if (!usedDependencies->contains(dependency.absolutePath)) { - options->qtDependencies.append(dependency); + options->qtDependencies[options->currentArchitecture].append(dependency); usedDependencies->insert(dependency.absolutePath); } } @@ -1529,15 +1607,15 @@ bool readAndroidDependencyXml(Options *options, if (reader.attributes().hasAttribute(QLatin1String("replaces"))) { QString replaces = reader.attributes().value(QLatin1String("replaces")).toString(); for (int i=0; i<options->localLibs.size(); ++i) { - if (options->localLibs.at(i) == replaces) { - options->localLibs[i] = fileName; + if (options->localLibs[options->currentArchitecture].at(i) == replaces) { + options->localLibs[options->currentArchitecture][i] = fileName; break; } } } else if (!fileName.isEmpty()) { - options->localLibs.append(fileName); + options->localLibs[options->currentArchitecture].append(fileName); } - if (fileName.endsWith(QLatin1String(".so"))) { + if (fileName.endsWith(QLatin1String(".so")) && checkArchitecture(*options, fileName)) { remainingDependencies->insert(fileName); } } else if (reader.name() == QLatin1String("permission")) { @@ -1557,24 +1635,17 @@ bool readAndroidDependencyXml(Options *options, } else if (options->verbose) { fprintf(stdout, "No android dependencies for %s\n", qPrintable(moduleName)); } + options->permissions.removeDuplicates(); + options->features.removeDuplicates(); return true; } QStringList getQtLibsFromElf(const Options &options, const QString &fileName) { - QString readElf = options.ndkPath - + QLatin1String("/toolchains/") - + options.toolchainPrefix; - - if (!options.useLLVM) - readElf += QLatin1Char('-') + options.toolchainVersion; - - readElf += QLatin1String("/prebuilt/") - + options.ndkHost - + QLatin1String("/bin/") - + options.toolPrefix + - (options.useLLVM ? QLatin1String("-readobj") : QLatin1String("-readelf")); + QString readElf = QLatin1String("%1/toolchains/%2/prebuilt/%3/bin/llvm-readobj").arg(options.ndkPath, + options.toolchainPrefix, + options.ndkHost); #if defined(Q_OS_WIN32) readElf += QLatin1String(".exe"); #endif @@ -1584,14 +1655,11 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName) return QStringList(); } - if (options.useLLVM) - readElf = QString::fromLatin1("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(fileName)); - else - readElf = QString::fromLatin1("%1 -d -W %2").arg(shellQuote(readElf), shellQuote(fileName)); + readElf = QLatin1String("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(fileName)); FILE *readElfCommand = openProcess(readElf); if (!readElfCommand) { - fprintf(stderr, "Cannot execute command %s", qPrintable(readElf)); + fprintf(stderr, "Cannot execute command %s\n", qPrintable(readElf)); return QStringList(); } @@ -1599,22 +1667,25 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName) bool readLibs = false; char buffer[512]; - while (fgets(buffer, sizeof(buffer), readElfCommand) != 0) { + while (fgets(buffer, sizeof(buffer), readElfCommand) != nullptr) { QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer)); QString library; - if (options.useLLVM) { - line = line.trimmed(); - if (!readLibs) { - readLibs = line.startsWith("NeededLibraries"); - continue; + line = line.trimmed(); + if (!readLibs) { + if (line.startsWith("Arch: ")) { + auto it = elfArchitecures.find(line.mid(6)); + if (it == elfArchitecures.constEnd() || *it != options.currentArchitecture.toLatin1()) { + if (options.verbose) + fprintf(stdout, "Skipping \"%s\", architecture mismatch\n", qPrintable(fileName)); + return {}; + } } - if (!line.startsWith("lib")) - continue; - library = QString::fromLatin1(line); - } else if (line.contains("(NEEDED)") && line.contains("Shared library:")) { - const int pos = line.lastIndexOf('[') + 1; - library = QString::fromLatin1(line.mid(pos, line.length() - pos - 2)); + readLibs = line.startsWith("NeededLibraries"); + continue; } + if (!line.startsWith("lib")) + continue; + library = QString::fromLatin1(line); QString libraryName = QLatin1String("lib/") + library; if (QFile::exists(absoluteFilePath(&options, libraryName))) ret += libraryName; @@ -1653,7 +1724,7 @@ bool readDependenciesFromElf(Options *options, return false; } - options->qtDependencies.append(QtDependency(dependency, absoluteDependencyPath)); + options->qtDependencies[options->currentArchitecture].append(QtDependency(dependency, absoluteDependencyPath)); if (options->verbose) fprintf(stdout, "Appending dependency: %s\n", qPrintable(dependency)); dependenciesToCheck.append(dependency); @@ -1677,10 +1748,15 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies) if (options->verbose) fprintf(stdout, "Scanning for QML imports.\n"); - QString qmlImportScanner = options->qtInstallDirectory + QLatin1String("/bin/qmlimportscanner"); + QString qmlImportScanner; + if (!options->qmlImportScannerBinaryPath.isEmpty()) { + qmlImportScanner = options->qmlImportScannerBinaryPath; + } else { + options->qtInstallDirectory + QLatin1String("/bin/qmlimportscanner"); #if defined(Q_OS_WIN32) - qmlImportScanner += QLatin1String(".exe"); + qmlImportScanner += QLatin1String(".exe"); #endif + } if (!QFile::exists(qmlImportScanner)) { fprintf(stderr, "qmlimportscanner not found: %s\n", qPrintable(qmlImportScanner)); @@ -1702,9 +1778,8 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies) for (const QString &qmlImportPath : qAsConst(options->qmlImportPaths)) importPaths += shellQuote(qmlImportPath); - qmlImportScanner += QString::fromLatin1(" -rootPath %1 -importPath %2") - .arg(shellQuote(rootPath)) - .arg(importPaths.join(QLatin1Char(' '))); + qmlImportScanner += QLatin1String(" -rootPath %1 -importPath %2") + .arg(shellQuote(rootPath), importPaths.join(QLatin1Char(' '))); if (options->verbose) { fprintf(stdout, "Running qmlimportscanner with the following command: %s\n", @@ -1798,9 +1873,9 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies) // Put all imports in default import path in assets fileName.relativePath.prepend(QLatin1String("qml/")); - options->qtDependencies.append(fileName); + options->qtDependencies[options->currentArchitecture].append(fileName); - if (fileName.absolutePath.endsWith(QLatin1String(".so"))) { + if (fileName.absolutePath.endsWith(QLatin1String(".so")) && checkArchitecture(*options, fileName.absolutePath)) { QSet<QString> remainingDependencies; if (!readDependenciesFromElf(options, fileName.absolutePath, usedDependencies, &remainingDependencies)) return false; @@ -1819,7 +1894,7 @@ bool readDependencies(Options *options) fprintf(stdout, "Detecting dependencies of application.\n"); // Override set in .pro file - if (!options->qtDependencies.isEmpty()) { + if (!options->qtDependencies[options->currentArchitecture].isEmpty()) { if (options->verbose) fprintf(stdout, "\tDependencies explicitly overridden in .pro file. No detection needed.\n"); return true; @@ -1829,11 +1904,7 @@ bool readDependencies(Options *options) QSet<QString> remainingDependencies; // Add dependencies of application binary first - if (!readDependenciesFromElf(options, options->applicationBinary, &usedDependencies, &remainingDependencies)) - return false; - - // Jam in the dependencies of the platform plugin, since the application will crash without it - if (!readDependenciesFromElf(options, options->qtInstallDirectory + QLatin1String("/plugins/platforms/android/libqtforandroid.so"), &usedDependencies, &remainingDependencies)) + if (!readDependenciesFromElf(options, QLatin1String("%1/libs/%2/lib%3_%2.so").arg(options->outputDirectory, options->currentArchitecture, options->applicationBinary), &usedDependencies, &remainingDependencies)) return false; while (!remainingDependencies.isEmpty()) { @@ -1853,14 +1924,14 @@ bool readDependencies(Options *options) } } - QStringList::iterator it = options->localLibs.begin(); - while (it != options->localLibs.end()) { + QStringList::iterator it = options->localLibs[options->currentArchitecture].begin(); + while (it != options->localLibs[options->currentArchitecture].end()) { QStringList unmetDependencies; if (!goodToCopy(options, absoluteFilePath(options, *it), &unmetDependencies)) { fprintf(stdout, "Skipping %s due to unmet dependencies: %s\n", qPrintable(*it), qPrintable(unmetDependencies.join(QLatin1Char(',')))); - it = options->localLibs.erase(it); + it = options->localLibs[options->currentArchitecture].erase(it); } else { ++it; } @@ -1872,94 +1943,33 @@ bool readDependencies(Options *options) return true; } -bool stripFile(const Options &options, const QString &fileName) -{ - QString strip = options.ndkPath - + QLatin1String("/toolchains/") - + options.toolchainPrefix; - - if (!options.useLLVM) - strip += QLatin1Char('-') + options.toolchainVersion; - - strip += QLatin1String("/prebuilt/") - + options.ndkHost - + QLatin1String("/bin/") - + options.toolPrefix - + QLatin1String("-strip"); -#if defined(Q_OS_WIN32) - strip += QLatin1String(".exe"); -#endif - - if (!QFile::exists(strip)) { - fprintf(stderr, "Command does not exist: %s\n", qPrintable(strip)); - return false; - } - - if (options.useLLVM) - strip = QString::fromLatin1("%1 -strip-all %2").arg(shellQuote(strip), shellQuote(fileName)); - else - strip = QString::fromLatin1("%1 %2").arg(shellQuote(strip), shellQuote(fileName)); - - FILE *stripCommand = openProcess(strip); - if (stripCommand == 0) { - fprintf(stderr, "Cannot execute command %s", qPrintable(strip)); - return false; - } - - pclose(stripCommand); - - return true; -} - -bool stripLibraries(const Options &options) +bool containsApplicationBinary(Options *options) { - if (!options.stripLibraries) + if (!options->build) return true; - if (options.verbose) - fprintf(stdout, "Stripping libraries to minimize size.\n"); - - QString libraryPath = options.outputDirectory - + QLatin1String("/libs/") - + options.architecture; - const QStringList libraries = QDir(libraryPath).entryList(QDir::Files); - for (const QString &library : libraries) { - if (library.endsWith(QLatin1String(".so"))) { - if (!stripFile(options, libraryPath + QLatin1Char('/') + library)) - return false; - } - } - - - return true; -} - -bool containsApplicationBinary(const Options &options) -{ - if (options.verbose) + if (options->verbose) fprintf(stdout, "Checking if application binary is in package.\n"); - QFileInfo applicationBinary(options.applicationBinary); - QString destinationFileName = options.outputDirectory - + QLatin1String("/libs/") - + options.architecture - + QLatin1Char('/') - + applicationBinary.fileName(); + QFileInfo applicationBinary(options->applicationBinary); + QString applicationFileName = QLatin1String("lib%1_%2.so").arg(options->applicationBinary, + options->currentArchitecture); - if (!QFile::exists(destinationFileName)) { + QString applicationPath = QLatin1String("%1/libs/%2/%3").arg(options->outputDirectory, + options->currentArchitecture, + applicationFileName); + if (!QFile::exists(applicationPath)) { #if defined(Q_OS_WIN32) QLatin1String makeTool("mingw32-make"); // Only Mingw host builds supported on Windows currently #else QLatin1String makeTool("make"); #endif - fprintf(stderr, "Application binary is not in output directory: %s. Please run '%s install INSTALL_ROOT=%s' first.\n", - qPrintable(destinationFileName), + qPrintable(applicationFileName), qPrintable(makeTool), - qPrintable(options.outputDirectory)); + qPrintable(options->outputDirectory)); return false; } - return true; } @@ -1978,7 +1988,7 @@ FILE *runAdb(const Options &options, const QString &arguments) if (!options.installLocation.isEmpty()) installOption = QLatin1String(" -s ") + shellQuote(options.installLocation); - adb = QString::fromLatin1("%1%2 %3").arg(shellQuote(adb)).arg(installOption).arg(arguments); + adb = QLatin1String("%1%2 %3").arg(shellQuote(adb), installOption, arguments); if (options.verbose) fprintf(stdout, "Running command \"%s\"\n", adb.toLocal8Bit().constData()); @@ -1997,10 +2007,13 @@ bool goodToCopy(const Options *options, const QString &file, QStringList *unmetD if (!file.endsWith(QLatin1String(".so"))) return true; + if (!checkArchitecture(*options, file)) + return false; + bool ret = true; const auto libs = getQtLibsFromElf(*options, file); for (const QString &lib : libs) { - if (!options->qtDependencies.contains(QtDependency(lib, absoluteFilePath(options, lib)))) { + if (!options->qtDependencies[options->currentArchitecture].contains(QtDependency(lib, absoluteFilePath(options, lib)))) { ret = false; unmetDependencies->append(lib); } @@ -2025,12 +2038,12 @@ bool copyQtFiles(Options *options) if (!options->build) return true; + QString libsDirectory = QLatin1String("libs/"); // Copy other Qt dependencies - QString libDestinationDirectory = libsDirectory + options->architecture + QLatin1Char('/'); - QString assetsDestinationDirectory = QLatin1String("assets/--Added-by-androiddeployqt--/"); - for (const QtDependency &qtDependency : qAsConst(options->qtDependencies)) { + auto assetsDestinationDirectory = QLatin1String("assets/--Added-by-androiddeployqt--/"); + for (const QtDependency &qtDependency : qAsConst(options->qtDependencies[options->currentArchitecture])) { QString sourceFileName = qtDependency.absolutePath; QString destinationFileName; @@ -2039,12 +2052,9 @@ bool copyQtFiles(Options *options) if (qtDependency.relativePath.startsWith(QLatin1String("lib/"))) { garbledFileName = qtDependency.relativePath.mid(sizeof("lib/") - 1); } else { - garbledFileName = QLatin1String("lib") - + QString(qtDependency.relativePath).replace(QLatin1Char('/'), QLatin1Char('_')); - + garbledFileName = QString(qtDependency.relativePath).replace(QLatin1Char('/'), QLatin1Char('_')); } - destinationFileName = libDestinationDirectory + garbledFileName; - + destinationFileName = libsDirectory + options->currentArchitecture + QLatin1Char('/') + garbledFileName; } else if (qtDependency.relativePath.startsWith(QLatin1String("jar/"))) { destinationFileName = libsDirectory + qtDependency.relativePath.mid(sizeof("jar/") - 1); } else { @@ -2058,20 +2068,34 @@ bool copyQtFiles(Options *options) QStringList unmetDependencies; if (!goodToCopy(options, sourceFileName, &unmetDependencies)) { - fprintf(stdout, " -- Skipping %s. It has unmet dependencies: %s.\n", - qPrintable(sourceFileName), - qPrintable(unmetDependencies.join(QLatin1Char(',')))); + if (unmetDependencies.isEmpty()) { + if (options->verbose) { + fprintf(stdout, " -- Skipping %s, architecture mismatch.\n", + qPrintable(sourceFileName)); + } + } else { + if (unmetDependencies.isEmpty()) { + if (options->verbose) { + fprintf(stdout, " -- Skipping %s, architecture mismatch.\n", + qPrintable(sourceFileName)); + } + } else { + fprintf(stdout, " -- Skipping %s. It has unmet dependencies: %s.\n", + qPrintable(sourceFileName), + qPrintable(unmetDependencies.join(QLatin1Char(',')))); + } + } continue; } if (options->deploymentMechanism == Options::Bundled && !copyFileIfNewer(sourceFileName, options->outputDirectory + QLatin1Char('/') + destinationFileName, - options->verbose)) { + *options)) { return false; } - options->bundledFiles += qMakePair(destinationFileName, qtDependency.relativePath); + options->bundledFiles[options->currentArchitecture] += qMakePair(destinationFileName, qtDependency.relativePath); } return true; @@ -2120,7 +2144,7 @@ bool createAndroidProject(const Options &options) return false; } - QString androidTool = QString::fromLatin1("%1 update project --path %2 --target %3 --name QtApp") + QString androidTool = QLatin1String("%1 update project --path %2 --target %3 --name QtApp") .arg(shellQuote(androidToolExecutable)) .arg(shellQuote(options.outputDirectory)) .arg(shellQuote(options.androidPlatform)); @@ -2142,7 +2166,7 @@ bool createAndroidProject(const Options &options) if (options.verbose) fprintf(stdout, "Updating subproject %s\n", qPrintable(libraryProject)); - androidTool = QString::fromLatin1("%1 update lib-project --path %2 --target %3") + androidTool = QLatin1String("%1 update lib-project --path %2 --target %3") .arg(shellQuote(androidToolExecutable)) .arg(shellQuote(libraryProject)) .arg(shellQuote(options.androidPlatform)); @@ -2274,7 +2298,10 @@ bool buildAndroidProject(const Options &options) return false; } - QString commandLine = QString::fromLatin1("%1 --no-daemon %2").arg(shellQuote(gradlePath)).arg(options.releasePackage ? QLatin1String(" assembleRelease") : QLatin1String(" assembleDebug")); + QString commandLine = QLatin1String("%1 --no-daemon %2").arg(shellQuote(gradlePath), options.releasePackage ? QLatin1String(" assembleRelease") : QLatin1String(" assembleDebug")); + if (options.buildAAB) + commandLine += QLatin1String(" bundle"); + if (options.verbose) commandLine += QLatin1String(" --info"); @@ -2335,28 +2362,38 @@ bool uninstallApk(const Options &options) } enum PackageType { + AAB, UnsignedAPK, SignedAPK }; -QString apkPath(const Options &options, PackageType pt) +QString packagePath(const Options &options, PackageType pt) { QString path(options.outputDirectory); - path += QLatin1String("/build/outputs/apk/"); + path += QLatin1String("/build/outputs/%1/").arg(pt >= UnsignedAPK ? QStringLiteral("apk") : QStringLiteral("bundle")); QString buildType(options.releasePackage ? QLatin1String("release/") : QLatin1String("debug/")); if (QDir(path + buildType).exists()) path += buildType; path += QDir(options.outputDirectory).dirName() + QLatin1Char('-'); if (options.releasePackage) { path += QLatin1String("release-"); - if (pt == UnsignedAPK) - path += QLatin1String("un"); - path += QLatin1String("signed.apk"); + if (pt >= UnsignedAPK) { + if (pt == UnsignedAPK) + path += QLatin1String("un"); + path += QLatin1String("signed.apk"); + } else { + path.chop(1); + path += QLatin1String(".aab"); + } } else { path += QLatin1String("debug"); - if (pt == SignedAPK) - path += QLatin1String("-signed"); - path += QLatin1String(".apk"); + if (pt >= UnsignedAPK) { + if (pt == SignedAPK) + path += QLatin1String("-signed"); + path += QLatin1String(".apk"); + } else { + path += QLatin1String(".aab"); + } } return shellQuote(path); } @@ -2373,7 +2410,7 @@ bool installApk(const Options &options) FILE *adbCommand = runAdb(options, QLatin1String(" install -r ") - + apkPath(options, options.keyStore.isEmpty() ? UnsignedAPK + + packagePath(options, options.keyStore.isEmpty() ? UnsignedAPK : SignedAPK)); if (adbCommand == 0) return false; @@ -2399,7 +2436,7 @@ bool installApk(const Options &options) bool copyPackage(const Options &options) { fflush(stdout); - auto from = apkPath(options, options.keyStore.isEmpty() ? UnsignedAPK : SignedAPK); + auto from = packagePath(options, options.keyStore.isEmpty() ? UnsignedAPK : SignedAPK); QFile::remove(options.apkPath); return QFile::copy(from, options.apkPath); } @@ -2409,21 +2446,18 @@ bool copyStdCpp(Options *options) if (options->verbose) fprintf(stdout, "Copying STL library\n"); - if (!QFile::exists(options->stdCppPath)) { - fprintf(stderr, "STL library does not exist at %s\n", qPrintable(options->stdCppPath)); - return false; - } - - const QString destinationDirectory = options->outputDirectory - + QLatin1String("/libs/") + options->architecture; - - if (!copyFileIfNewer(options->stdCppPath, destinationDirectory + QLatin1String("/lib") - + options->stdCppName + QLatin1String(".so"), - options->verbose)) { + QString stdCppPath = QLatin1String("%1/%2/lib%3.so").arg(options->stdCppPath, options->architectures[options->currentArchitecture], options->stdCppName); + if (!QFile::exists(stdCppPath)) { + fprintf(stderr, "STL library does not exist at %s\n", qPrintable(stdCppPath)); + fflush(stdout); + fflush(stderr); return false; } - return true; + const QString destinationFile = QLatin1String("%1/libs/%2/lib%3.so").arg(options->outputDirectory, + options->currentArchitecture, + options->stdCppName); + return copyFileIfNewer(stdCppPath, destinationFile, *options); } bool jarSignerSignPackage(const Options &options) @@ -2437,9 +2471,9 @@ bool jarSignerSignPackage(const Options &options) jdkPath = QString::fromLocal8Bit(qgetenv("JAVA_HOME")); #if defined(Q_OS_WIN32) - QString jarSignerTool = QString::fromLatin1("jarsigner.exe"); + QString jarSignerTool = QLatin1String("jarsigner.exe"); #else - QString jarSignerTool = QString::fromLatin1("jarsigner"); + QString jarSignerTool = QLatin1String("jarsigner"); #endif if (jdkPath.isEmpty() || !QFile::exists(jdkPath + QLatin1String("/bin/") + jarSignerTool)) @@ -2452,29 +2486,29 @@ bool jarSignerSignPackage(const Options &options) return false; } - jarSignerTool = QString::fromLatin1("%1 -sigalg %2 -digestalg %3 -keystore %4") - .arg(shellQuote(jarSignerTool)).arg(shellQuote(options.sigAlg)).arg(shellQuote(options.digestAlg)).arg(shellQuote(options.keyStore)); + jarSignerTool = QLatin1String("%1 -sigalg %2 -digestalg %3 -keystore %4") + .arg(shellQuote(jarSignerTool), shellQuote(options.sigAlg), shellQuote(options.digestAlg), shellQuote(options.keyStore)); if (!options.keyStorePassword.isEmpty()) - jarSignerTool += QString::fromLatin1(" -storepass %1").arg(shellQuote(options.keyStorePassword)); + jarSignerTool += QLatin1String(" -storepass %1").arg(shellQuote(options.keyStorePassword)); if (!options.storeType.isEmpty()) - jarSignerTool += QString::fromLatin1(" -storetype %1").arg(shellQuote(options.storeType)); + jarSignerTool += QLatin1String(" -storetype %1").arg(shellQuote(options.storeType)); if (!options.keyPass.isEmpty()) - jarSignerTool += QString::fromLatin1(" -keypass %1").arg(shellQuote(options.keyPass)); + jarSignerTool += QLatin1String(" -keypass %1").arg(shellQuote(options.keyPass)); if (!options.sigFile.isEmpty()) - jarSignerTool += QString::fromLatin1(" -sigfile %1").arg(shellQuote(options.sigFile)); + jarSignerTool += QLatin1String(" -sigfile %1").arg(shellQuote(options.sigFile)); if (!options.signedJar.isEmpty()) - jarSignerTool += QString::fromLatin1(" -signedjar %1").arg(shellQuote(options.signedJar)); + jarSignerTool += QLatin1String(" -signedjar %1").arg(shellQuote(options.signedJar)); if (!options.tsaUrl.isEmpty()) - jarSignerTool += QString::fromLatin1(" -tsa %1").arg(shellQuote(options.tsaUrl)); + jarSignerTool += QLatin1String(" -tsa %1").arg(shellQuote(options.tsaUrl)); if (!options.tsaCert.isEmpty()) - jarSignerTool += QString::fromLatin1(" -tsacert %1").arg(shellQuote(options.tsaCert)); + jarSignerTool += QLatin1String(" -tsacert %1").arg(shellQuote(options.tsaCert)); if (options.internalSf) jarSignerTool += QLatin1String(" -internalsf"); @@ -2485,29 +2519,39 @@ bool jarSignerSignPackage(const Options &options) if (options.protectedAuthenticationPath) jarSignerTool += QLatin1String(" -protected"); - jarSignerTool += QString::fromLatin1(" %1 %2") - .arg(apkPath(options, UnsignedAPK)) - .arg(shellQuote(options.keyStoreAlias)); + auto signPackage = [&](const QString &file) { + fprintf(stdout, "Signing file %s\n", qPrintable(file)); + fflush(stdout); + auto command = jarSignerTool + QLatin1String(" %1 %2") + .arg(file) + .arg(shellQuote(options.keyStoreAlias)); - FILE *jarSignerCommand = openProcess(jarSignerTool); - if (jarSignerCommand == 0) { - fprintf(stderr, "Couldn't run jarsigner.\n"); - return false; - } + FILE *jarSignerCommand = openProcess(command); + if (jarSignerCommand == 0) { + fprintf(stderr, "Couldn't run jarsigner.\n"); + return false; + } - if (options.verbose) { - char buffer[512]; - while (fgets(buffer, sizeof(buffer), jarSignerCommand) != 0) - fprintf(stdout, "%s", buffer); - } + if (options.verbose) { + char buffer[512]; + while (fgets(buffer, sizeof(buffer), jarSignerCommand) != 0) + fprintf(stdout, "%s", buffer); + } - int errorCode = pclose(jarSignerCommand); - if (errorCode != 0) { - fprintf(stderr, "jarsigner command failed.\n"); - if (!options.verbose) - fprintf(stderr, " -- Run with --verbose for more information.\n"); + int errorCode = pclose(jarSignerCommand); + if (errorCode != 0) { + fprintf(stderr, "jarsigner command failed.\n"); + if (!options.verbose) + fprintf(stderr, " -- Run with --verbose for more information.\n"); + return false; + } + return true; + }; + + if (!signPackage(packagePath(options, UnsignedAPK))) + return false; + if (options.buildAAB && !signPackage(packagePath(options, AAB))) return false; - } QString zipAlignTool = options.sdkPath + QLatin1String("/tools/zipalign"); #if defined(Q_OS_WIN32) @@ -2525,11 +2569,11 @@ bool jarSignerSignPackage(const Options &options) } } - zipAlignTool = QString::fromLatin1("%1%2 -f 4 %3 %4") - .arg(shellQuote(zipAlignTool)) - .arg(options.verbose ? QString::fromLatin1(" -v") : QString()) - .arg(apkPath(options, UnsignedAPK)) - .arg(apkPath(options, SignedAPK)); + zipAlignTool = QLatin1String("%1%2 -f 4 %3 %4") + .arg(shellQuote(zipAlignTool), + options.verbose ? QLatin1String(" -v") : QLatin1String(), + packagePath(options, UnsignedAPK), + packagePath(options, SignedAPK)); FILE *zipAlignCommand = openProcess(zipAlignTool); if (zipAlignCommand == 0) { @@ -2541,7 +2585,7 @@ bool jarSignerSignPackage(const Options &options) while (fgets(buffer, sizeof(buffer), zipAlignCommand) != 0) fprintf(stdout, "%s", buffer); - errorCode = pclose(zipAlignCommand); + int errorCode = pclose(zipAlignCommand); if (errorCode != 0) { fprintf(stderr, "zipalign command failed.\n"); if (!options.verbose) @@ -2549,7 +2593,7 @@ bool jarSignerSignPackage(const Options &options) return false; } - return QFile::remove(apkPath(options, UnsignedAPK)); + return QFile::remove(packagePath(options, UnsignedAPK)); } bool signPackage(const Options &options) @@ -2580,11 +2624,11 @@ bool signPackage(const Options &options) } } - zipAlignTool = QString::fromLatin1("%1%2 -f 4 %3 %4") - .arg(shellQuote(zipAlignTool)) - .arg(options.verbose ? QString::fromLatin1(" -v") : QString()) - .arg(apkPath(options, UnsignedAPK)) - .arg(apkPath(options, SignedAPK)); + zipAlignTool = QLatin1String("%1%2 -f 4 %3 %4") + .arg(shellQuote(zipAlignTool), + options.verbose ? QLatin1String(" -v") : QLatin1String(), + packagePath(options, UnsignedAPK), + packagePath(options, SignedAPK)); FILE *zipAlignCommand = openProcess(zipAlignTool); if (zipAlignCommand == 0) { @@ -2604,23 +2648,23 @@ bool signPackage(const Options &options) return false; } - QString apkSignerCommandLine = QString::fromLatin1("%1 sign --ks %2") - .arg(shellQuote(apksignerTool)).arg(shellQuote(options.keyStore)); + QString apkSignerCommandLine = QLatin1String("%1 sign --ks %2") + .arg(shellQuote(apksignerTool), shellQuote(options.keyStore)); if (!options.keyStorePassword.isEmpty()) - apkSignerCommandLine += QString::fromLatin1(" --ks-pass pass:%1").arg(shellQuote(options.keyStorePassword)); + apkSignerCommandLine += QLatin1String(" --ks-pass pass:%1").arg(shellQuote(options.keyStorePassword)); if (!options.keyStoreAlias.isEmpty()) - apkSignerCommandLine += QString::fromLatin1(" --ks-key-alias %1").arg(shellQuote(options.keyStoreAlias)); + apkSignerCommandLine += QLatin1String(" --ks-key-alias %1").arg(shellQuote(options.keyStoreAlias)); if (!options.keyPass.isEmpty()) - apkSignerCommandLine += QString::fromLatin1(" --key-pass pass:%1").arg(shellQuote(options.keyPass)); + apkSignerCommandLine += QLatin1String(" --key-pass pass:%1").arg(shellQuote(options.keyPass)); if (options.verbose) apkSignerCommandLine += QLatin1String(" --verbose"); - apkSignerCommandLine += QString::fromLatin1(" %1") - .arg(apkPath(options, SignedAPK)); + apkSignerCommandLine += QLatin1String(" %1") + .arg(packagePath(options, SignedAPK)); auto apkSignerRunner = [&] { FILE *apkSignerCommand = openProcess(apkSignerCommandLine); @@ -2647,60 +2691,11 @@ bool signPackage(const Options &options) if (!apkSignerRunner()) return false; - apkSignerCommandLine = QString::fromLatin1("%1 verify --verbose %2") - .arg(shellQuote(apksignerTool)).arg(apkPath(options, SignedAPK)); + apkSignerCommandLine = QLatin1String("%1 verify --verbose %2") + .arg(shellQuote(apksignerTool), packagePath(options, SignedAPK)); // Verify the package and remove the unsigned apk - return apkSignerRunner() && QFile::remove(apkPath(options, UnsignedAPK)); -} - -bool copyGdbServer(const Options &options) -{ - if (options.verbose) - fprintf(stdout, "Copying gdbserver into package.\n"); - - QString architectureSubDirectory; - if (options.architecture == QLatin1String("arm64-v8a")) - architectureSubDirectory = QLatin1String("android-arm64"); - else if (options.architecture.startsWith(QLatin1String("arm"))) - architectureSubDirectory = QLatin1String("android-arm"); - else - architectureSubDirectory = QLatin1String("android-") + options.architecture; - - QString gdbServerBinary = options.ndkPath - + QLatin1String("/prebuilt/") - + architectureSubDirectory - + QLatin1String("/gdbserver/gdbserver"); - if (!QFile::exists(gdbServerBinary)) { - fprintf(stderr, "Cannot find gdbserver at %s.\n", qPrintable(gdbServerBinary)); - return false; - } - - QString gdbServerTarget = options.outputDirectory + QLatin1String("/libs/") + options.architecture; - - if (!copyFileIfNewer(gdbServerBinary, - gdbServerTarget + QLatin1String("/gdbserver"), - options.verbose) - || !copyFileIfNewer(gdbServerBinary, - gdbServerTarget + QLatin1String("/libgdbserver.so"), - options.verbose)) { - return false; - } - - QString addedByAndroidDeployQtPath = options.outputDirectory + QLatin1String("/assets/--Added-by-androiddeployqt--/"); - if (!QDir().mkpath(addedByAndroidDeployQtPath)) { - fprintf(stderr, "Failed to create directory '%s'", qPrintable(addedByAndroidDeployQtPath)); - return false; - } - QFile f(addedByAndroidDeployQtPath + QLatin1String("debugger.command")); - if (!f.open(QIODevice::WriteOnly)) { - fprintf(stderr, "Failed to create directory '%s'", qPrintable(addedByAndroidDeployQtPath)); - return false; - } - f.write("lib/libgdbserver.so --multi +"); - f.close(); - - return true; + return apkSignerRunner() && QFile::remove(packagePath(options, UnsignedAPK)); } bool generateAssetsFileList(const Options &options) @@ -2726,7 +2721,7 @@ bool generateAssetsFileList(const Options &options) const QString name = dirIterator.next().mid(assetsPath.length()); int slashIndex = name.lastIndexOf(QLatin1Char('/')); - QString pathName = slashIndex >= 0 ? name.left(slashIndex) : QString::fromLatin1("/"); + QString pathName = slashIndex >= 0 ? name.left(slashIndex) : QStringLiteral("/"); QString fileName = slashIndex >= 0 ? name.mid(pathName.length() + 1) : name; if (!fileName.isEmpty() && dirIterator.fileInfo().isDir() && !fileName.endsWith(QLatin1Char('/'))) @@ -2764,8 +2759,6 @@ enum ErrorCode CannotCopyGnuStl = 5, CannotCopyQtFiles = 6, CannotFindApplicationBinary = 7, - CannotCopyGdbServer = 8, - CannotStripLibraries = 9, CannotCopyAndroidExtraLibs = 10, CannotCopyAndroidSources = 11, CannotUpdateAndroidFiles = 12, @@ -2813,25 +2806,7 @@ int main(int argc, char *argv[]) : "No" ); - if (options.auxMode) { - if (!readDependencies(&options)) - return CannotReadDependencies; - if (!copyQtFiles(&options)) - return CannotCopyQtFiles; - if (!copyAndroidExtraResources(options)) - return CannotCopyAndroidExtraResources; - if (!copyAndroidExtraLibs(options)) - return CannotCopyAndroidExtraLibs; - if (!stripLibraries(options)) - return CannotStripLibraries; - if (!updateAndroidFiles(options)) - return CannotUpdateAndroidFiles; - if (options.generateAssetsFileList && !generateAssetsFileList(options)) - return CannotGenerateAssetsFileList; - return 0; - } - - if (options.build) { + if (options.build && !options.auxMode) { cleanAndroidFiles(options); if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Cleaned Android file\n", options.timer.elapsed()); @@ -2843,60 +2818,68 @@ int main(int argc, char *argv[]) fprintf(stdout, "[TIMING] %d ms: Copied Android template\n", options.timer.elapsed()); } - if (!readDependencies(&options)) - return CannotReadDependencies; + for (auto it = options.architectures.constBegin(); it != options.architectures.constEnd(); ++it) { + options.clear(it.key()); - if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Read dependencies\n", options.timer.elapsed()); - - if (options.deploymentMechanism != Options::Ministro && !copyStdCpp(&options)) - return CannotCopyGnuStl; + if (!readDependencies(&options)) + return CannotReadDependencies; - if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Copied GNU STL\n", options.timer.elapsed()); + if (Q_UNLIKELY(options.timing)) + fprintf(stdout, "[TIMING] %d ms: Read dependencies\n", options.timer.elapsed()); - if (!copyQtFiles(&options)) - return CannotCopyQtFiles; + if (!copyQtFiles(&options)) + return CannotCopyQtFiles; - if (options.build) { if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Copied Qt files\n", options.timer.elapsed()); - if (!containsApplicationBinary(options)) - return CannotFindApplicationBinary; + if (!copyAndroidExtraLibs(&options)) + return CannotCopyAndroidExtraLibs; if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Checked for application binary\n", options.timer.elapsed()); + fprintf(stdout, "[TIMING] %d ms: Copied extra libs\n", options.timer.elapsed()); - bool needToCopyGdbServer = options.gdbServer == Options::True - || (options.gdbServer == Options::Auto && !options.releasePackage); - if (needToCopyGdbServer && !copyGdbServer(options)) - return CannotCopyGdbServer; + if (!copyAndroidExtraResources(&options)) + return CannotCopyAndroidExtraResources; if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Copied GDB server\n", options.timer.elapsed()); + fprintf(stdout, "[TIMING] %d ms: Copied extra resources\n", options.timer.elapsed()); - if (!copyAndroidExtraLibs(options)) - return CannotCopyAndroidExtraLibs; + if (!options.auxMode) { + if (options.deploymentMechanism != Options::Ministro && !copyStdCpp(&options)) + return CannotCopyGnuStl; + + if (Q_UNLIKELY(options.timing)) + fprintf(stdout, "[TIMING] %d ms: Copied GNU STL\n", options.timer.elapsed()); + } + + if (!containsApplicationBinary(&options)) + return CannotFindApplicationBinary; if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Copied extra libs\n", options.timer.elapsed()); + fprintf(stdout, "[TIMING] %d ms: Checked for application binary\n", options.timer.elapsed()); - if (!copyAndroidExtraResources(options)) - return CannotCopyAndroidExtraResources; + if (options.deploymentMechanism != Options::Ministro) { + if (Q_UNLIKELY(options.timing)) + fprintf(stdout, "[TIMING] %d ms: Bundled Qt libs\n", options.timer.elapsed()); + } + } + + if (options.auxMode) { + if (!updateAndroidFiles(options)) + return CannotUpdateAndroidFiles; + if (options.generateAssetsFileList && !generateAssetsFileList(options)) + return CannotGenerateAssetsFileList; + return 0; + } + if (options.build) { if (!copyAndroidSources(options)) return CannotCopyAndroidSources; if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Copied android sources\n", options.timer.elapsed()); - if (!stripLibraries(options)) - return CannotStripLibraries; - - if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Stripped libraries\n", options.timer.elapsed()); - if (!updateAndroidFiles(options)) return CannotUpdateAndroidFiles; @@ -2936,7 +2919,7 @@ int main(int argc, char *argv[]) if (options.installApk) fprintf(stdout, " -- It can now be run from the selected device/emulator.\n"); - fprintf(stdout, " -- File: %s\n", qPrintable(apkPath(options, options.keyStore.isEmpty() ? UnsignedAPK + fprintf(stdout, " -- File: %s\n", qPrintable(packagePath(options, options.keyStore.isEmpty() ? UnsignedAPK : SignedAPK))); fflush(stdout); return 0; |