4#include <QCoreApplication>
7#include <QJsonDocument>
13#include <QXmlStreamReader>
14#include <QStandardPaths>
17#include <QElapsedTimer>
18#include <QRegularExpression>
29#if defined(Q_OS_WIN32)
35#define QT_POPEN_READ "rb"
38#define QT_POPEN_READ "r"
49#if defined(Q_OS_WIN32)
50 QString processedCommand = u
'\"' + command + u
'\"';
52 const QString& processedCommand = command;
54 struct Closer {
void operator()(FILE *proc)
const {
if (proc) (
void)pclose(proc); } };
55 using UP = std::unique_ptr<FILE, Closer>;
56 return UP{popen(processedCommand.toLocal8Bit().constData(),
QT_POPEN_READ)};
76 const QHash<QString, QString> &dirs = QHash<QString, QString>()
101 ,
sigAlg(
"SHA256withRSA"_L1)
220 const QHash<QString, QString> &directories)
248 {
"aarch64",
"arm64-v8a"},
249 {
"arm",
"armeabi-v7a"},
258 QSet<QString> *usedDependencies, QSet<QString> *remainingDependencies);
264 if (!
match.hasMatch())
266 return match.captured(1);
271#if defined(Q_OS_WIN32)
279#if defined(Q_OS_WIN32)
310 fprintf(stderr,
"Command does not exist: %s\n",
qPrintable(readElf));
317 if (!readElfCommand) {
318 fprintf(stderr,
"Cannot execute command %s\n",
qPrintable(readElf));
323 while (fgets(
buffer,
sizeof(
buffer), readElfCommand.get()) !=
nullptr) {
342 fprintf(stdout,
"Delete missing files %s %s\n",
qPrintable(srcDir.absolutePath()),
qPrintable(dstDir.absolutePath()));
349 if (
dst.fileName() ==
src.fileName()) {
358 fprintf(stdout,
"%s not found in %s, removing it.\n",
qPrintable(
dst.fileName()),
qPrintable(srcDir.absolutePath()));
388 options.
build =
true;
390 options.
build =
false;
413 }
else if (deploymentMechanism.
compare(
"unbundled"_L1,
417 fprintf(stderr,
"Unrecognized deployment mechanism: %s\n",
qPrintable(deploymentMechanism));
452 if (keyStore.
isEmpty() || storeAlias.isEmpty()) {
454 fprintf(stderr,
"Package signing path and alias values are not specified.\n");
457 "Using package signing path and alias values found from the "
458 "environment variables.\n");
468 fprintf(stderr,
"Package signing path and alias values are not "
474 fprintf(stdout,
"Using package signing store password found from the environment "
479 fprintf(stdout,
"Using package signing key password found from the environment "
535 }
else if (
argument.compare(
"--no-rcc-bundle-cleanup"_L1,
538 }
else if (
argument.compare(
"--copy-dependencies-only"_L1,
546 fprintf(stderr,
"Warning: Skipping %s, AAR packages are not installable.\n",
552 fprintf(stderr,
"Warning: Skipping -aab as --build-aar is present.\n");
556 fprintf(stderr,
"Warning: Skipping --sign, signing AAR packages is not supported.\n");
584Syntax: androiddeployqt --output <destination> [options]
586Creates an Android package in the build directory <destination> and
587builds it into an .apk file.
590 --input <inputfile>: Reads <inputfile> for options generated by
591 qmake. A default file name based on the current working
592 directory will be used if nothing else is specified.
594 --deployment <mechanism>: Supported deployment mechanisms:
595 bundled (default): Includes Qt files in stand-alone package.
596 unbundled: Assumes native libraries are present on the device
597 and does not include them in the APK.
599 --aab: Build an Android App Bundle.
601 --no-build: Do not build the package, it is useful to just install
602 a package previously built.
604 --install: Installs apk to device/emulator. By default this step is
605 not taken. If the application has previously been installed on
606 the device, it will be uninstalled first.
608 --reinstall: Installs apk to device/emulator. By default this step
609 is not taken. If the application has previously been installed on
610 the device, it will be overwritten, but its data will be left
613 --device [device ID]: Use specified device for deployment. Default
614 is the device selected by default by adb.
616 --android-platform <platform>: Builds against the given android
617 platform. By default, the highest available version will be
620 --release: Builds a package ready for release. By default, the
621 package will be signed with a debug key.
623 --sign <url/to/keystore> <alias>: Signs the package with the
624 specified keystore, alias and store password.
625 Optional arguments for use with signing:
626 --storepass <password>: Keystore password.
627 --storetype <type>: Keystore type.
628 --keypass <password>: Password for private key (if different
629 from keystore password.)
630 --sigfile <file>: Name of .SF/.DSA file.
631 --digestalg <name>: Name of digest algorithm. Default is
633 --sigalg <name>: Name of signature algorithm. Default is
635 --tsa <url>: Location of the Time Stamping Authority.
636 --tsacert <alias>: Public key certificate for TSA.
637 --internalsf: Include the .SF file inside the signature block.
638 --sectionsonly: Don't compute hash of entire manifest.
639 --protected: Keystore has protected authentication path.
640 --jarsigner: Deprecated, ignored.
642 NOTE: To conceal the keystore information, the environment variables
643 QT_ANDROID_KEYSTORE_PATH, and QT_ANDROID_KEYSTORE_ALIAS are used to
644 set the values keysotore and alias respectively.
645 Also the environment variables QT_ANDROID_KEYSTORE_STORE_PASS,
646 and QT_ANDROID_KEYSTORE_KEY_PASS are used to set the store and key
647 passwords respectively. This option needs only the --sign parameter.
649 --jdk <path/to/jdk>: Used to find the jarsigner tool when used
650 in combination with the --release argument. By default,
651 an attempt is made to detect the tool using the JAVA_HOME and
652 PATH environment variables, in that order.
654 --qml-import-paths: Specify additional search paths for QML
657 --verbose: Prints out information during processing.
659 --no-generated-assets-cache: Do not pregenerate the entry list for
660 the assets file engine.
662 --aux-mode: Operate in auxiliary mode. This will only copy the
663 dependencies into the build directory and update the XML templates.
664 The project will not be built or installed.
666 --apk <path/where/to/copy/the/apk>: Path where to copy the built apk.
668 --build-aar: Build an AAR package. This option skips --aab, --install,
669 --reinstall, and --sign options if they are provided.
671 --qml-importscanner-binary <path/to/qmlimportscanner>: Override the
672 default qmlimportscanner binary path. By default the
673 qmlimportscanner binary is located using the Qt directory
674 specified in the input file.
676 --depfile <path/to/depfile>: Output a dependency file.
678 --builddir <path/to/build/directory>: build directory. Necessary when
679 generating a depfile because ninja requires relative paths.
681 --no-rcc-bundle-cleanup: skip cleaning rcc bundle directory after
682 running androiddeployqt. This option simplifies debugging of
683 the resource bundle content, but it should not be used when deploying
684 a project, since it litters the 'assets' directory.
686 --copy-dependencies-only: resolve application dependencies and stop
687 deploying process after all libraries and resources that the
688 application depends on have been copied.
690 --help: Displays this information.
702 if (
s1.size() ==
s2.size())
705 return s1.size() >
s2.size();
712 return (
fileName.endsWith(
"/res/values/libs.xml"_L1)
713 ||
fileName.endsWith(
"/AndroidManifest.xml"_L1)
714 ||
fileName.endsWith(
"/res/values/strings.xml"_L1)
715 ||
fileName.endsWith(
"/src/org/qtproject/qt/android/bindings/QtActivity.java"_L1));
720 const QString &destinationFileName,
722 bool forceOverwrite =
false)
726 QFileInfo destinationFileInfo(destinationFileName);
727 QFileInfo sourceFileInfo(sourceFileName);
730 && sourceFileInfo.lastModified() <= destinationFileInfo.lastModified()
733 fprintf(stdout,
" -- Skipping file %s. Same or newer file already in place.\n",
qPrintable(sourceFileName));
737 fprintf(stderr,
"Can't remove old file: %s\n",
qPrintable(destinationFileName));
744 fprintf(stderr,
"Cannot make output directory for %s.\n",
qPrintable(destinationFileName));
749 fprintf(stderr,
"Failed to copy %s to %s.\n",
qPrintable(sourceFileName),
qPrintable(destinationFileName));
752 fprintf(stdout,
" -- Copied %s\n",
qPrintable(destinationFileName));
772 auto isComment = [](
const QByteArray &trimmed) {
773 return trimmed.startsWith(
"//") || trimmed.startsWith(
'*') || trimmed.startsWith(
"/*");
776 auto extractValue = [](
const QByteArray &trimmed) {
777 int idx = trimmed.indexOf(
'=');
780 idx = trimmed.indexOf(
' ');
783 return trimmed.mid(idx + 1).trimmed();
789 for (
const auto &
line : lines) {
791 if (isComment(trimmedLine))
793 if (trimmedLine.contains(
"useLegacyPackaging")) {
794 configs.setsLegacyPackaging =
true;
795 }
else if (trimmedLine.contains(
"compileSdkVersion androidCompileSdkVersion.toInteger()")) {
796 configs.usesIntegerCompileSdkVersion =
true;
797 }
else if (trimmedLine.contains(
"namespace")) {
807 auto isLegalChar = [] (
QChar c) ->
bool {
809 return (
ch >=
'0' &&
ch <=
'9') ||
810 (
ch >=
'A' &&
ch <=
'Z') ||
811 (
ch >=
'a' &&
ch <=
'z') ||
812 ch ==
'.' ||
ch ==
'_';
818 for (
QChar &
c : packageName) {
819 if (!isLegalChar(
c)) {
828 keywords <<
"abstract"_L1 <<
"continue"_L1 <<
"for"_L1
829 <<
"new"_L1 <<
"switch"_L1 <<
"assert"_L1
830 <<
"default"_L1 <<
"if"_L1 <<
"package"_L1
831 <<
"synchronized"_L1 <<
"boolean"_L1 <<
"do"_L1
832 <<
"goto"_L1 <<
"private"_L1 <<
"this"_L1
833 <<
"break"_L1 <<
"double"_L1 <<
"implements"_L1
834 <<
"protected"_L1 <<
"throw"_L1 <<
"byte"_L1
835 <<
"else"_L1 <<
"import"_L1 <<
"public"_L1
836 <<
"throws"_L1 <<
"case"_L1 <<
"enum"_L1
837 <<
"instanceof"_L1 <<
"return"_L1 <<
"transient"_L1
838 <<
"catch"_L1 <<
"extends"_L1 <<
"int"_L1
839 <<
"short"_L1 <<
"try"_L1 <<
"char"_L1
840 <<
"final"_L1 <<
"interface"_L1 <<
"static"_L1
841 <<
"void"_L1 <<
"class"_L1 <<
"finally"_L1
842 <<
"long"_L1 <<
"strictfp"_L1 <<
"volatile"_L1
843 <<
"const"_L1 <<
"float"_L1 <<
"native"_L1
844 <<
"super"_L1 <<
"while"_L1;
854 if (!word.isEmpty()) {
856 if ((
c >= u
'0' &&
c <= u
'9') ||
c == u
'_') {
879 QDir dir(sdkPath +
"/platforms"_L1);
881 fprintf(stderr,
"Directory %s does not exist\n",
qPrintable(
dir.absolutePath()));
887 fprintf(stderr,
"No platforms found in %s",
qPrintable(
dir.absolutePath()));
903 if (!packageName.
isEmpty() && packageName !=
"androidPackageName"_L1)
909 QXmlStreamReader reader(&androidManifestXml);
910 while (!reader.atEnd()) {
912 if (reader.isStartElement() && reader.name() ==
"manifest"_L1) {
913 QString packageName = reader.attributes().value(
"package"_L1).toString();
914 if (!packageName.
isEmpty() && packageName !=
"org.qtproject.example"_L1)
931 || stringValue.
toInt() > 0);
937 if (qtDirectory.isUndefined()) {
939 if (
keyName ==
"qtDataDirectory"_L1) {
942 }
else if (
keyName ==
"qtLibsDirectory"_L1) {
945 }
else if (
keyName ==
"qtLibExecsDirectory"_L1) {
948 }
else if (
keyName ==
"qtPluginsDirectory"_L1) {
951 }
else if (
keyName ==
"qtQmlDirectory"_L1) {
959 if (qtDirectory.isObject()) {
961 for (
auto it =
object.constBegin();
it !=
object.
constEnd(); ++
it) {
962 if (
it.value().isUndefined()) {
964 "Invalid '%s' record in deployment settings: %s\n",
969 if (
it.value().isNull())
972 fprintf(stderr,
"Architecture %s unknown (%s).",
qPrintable(
it.key()),
978 }
else if (qtDirectory.isString()) {
987 fprintf(stderr,
"Invalid format for %s in json file %s.\n",
1004 if (jsonDocument.isNull()) {
1012 QJsonValue sdkPath = jsonObject.value(
"sdk"_L1);
1026 fprintf(stderr,
"Warning: Android platform '%s' does not exist in SDK.\n",
1034 const QJsonValue value = jsonObject.value(
"sdkBuildToolsRevision"_L1);
1035 if (!
value.isUndefined())
1040 const QJsonValue qtInstallDirectory = jsonObject.value(
"qt"_L1);
1046 if (qtInstallDirectory.
isObject()) {
1048 for (
auto it =
object.constBegin();
it !=
object.
constEnd(); ++
it) {
1049 if (
it.value().isUndefined()) {
1051 "Invalid 'qt' record in deployment settings: %s\n",
1055 if (
it.value().isNull())
1060 }
else if (qtInstallDirectory.
isString()) {
1074 fprintf(stderr,
"Invalid format for Qt install prefixes in json file %s.\n",
1088 const QJsonValue qtHostDirectory = jsonObject.value(
"qtHostDir"_L1);
1093 fprintf(stderr,
"Invalid format for Qt host directory in json file %s.\n",
1101 const auto extraPrefixDirs = jsonObject.value(
"extraPrefixDirs"_L1).toArray();
1103 for (
const QJsonValue prefix : extraPrefixDirs) {
1109 const auto androidDeployPlugins = jsonObject.value(
"android-deploy-plugins"_L1).toString();
1114 const auto extraLibraryDirs = jsonObject.value(
"extraLibraryDirs"_L1).toArray();
1122 const QJsonValue androidSourcesDirectory = jsonObject.value(
"android-package-source-directory"_L1);
1123 if (!androidSourcesDirectory.isUndefined())
1128 const QJsonValue applicationArguments = jsonObject.value(
"android-application-arguments"_L1);
1136 const QJsonValue androidVersionName = jsonObject.value(
"android-version-name"_L1);
1137 if (!androidVersionName.isUndefined())
1138 options->
versionName = androidVersionName.toString();
1144 const QJsonValue androidVersionCode = jsonObject.value(
"android-version-code"_L1);
1145 if (!androidVersionCode.isUndefined())
1146 options->
versionCode = androidVersionCode.toString();
1152 const QJsonValue ver = jsonObject.value(
"android-min-sdk-version"_L1);
1158 const QJsonValue ver = jsonObject.value(
"android-target-sdk-version"_L1);
1165 if (targetArchitectures.isEmpty()) {
1166 fprintf(stderr,
"No target architecture defined in json file.\n");
1170 if (
it.value().isUndefined()) {
1171 fprintf(stderr,
"Invalid architecture.\n");
1174 if (
it.value().isNull())
1177 fprintf(stderr,
"Architecture %s unknown (%s).",
qPrintable(
it.key()),
1187 const QJsonValue ndk = jsonObject.value(
"ndk"_L1);
1188 if (ndk.isUndefined()) {
1189 fprintf(stderr,
"No NDK path defined in json file.\n");
1192 options->
ndkPath = ndk.toString();
1197 fprintf(stderr,
"Couldn't retrieve the NDK version from \"%s\".\n",
1205 const QJsonValue toolchainPrefix = jsonObject.value(
"toolchain-prefix"_L1);
1207 fprintf(stderr,
"No toolchain prefix defined in json file.\n");
1214 const QJsonValue ndkHost = jsonObject.value(
"ndk-host"_L1);
1216 fprintf(stderr,
"No NDK host defined in json file.\n");
1223 const QJsonValue extraLibs = jsonObject.value(
"android-extra-libs"_L1);
1229 const QJsonValue qmlSkipImportScanning = jsonObject.value(
"qml-skip-import-scanning"_L1);
1235 const QJsonValue extraPlugins = jsonObject.value(
"android-extra-plugins"_L1);
1242 jsonObject.value(
"android-system-libs-prefix"_L1);
1248 const QJsonValue noDeploy = jsonObject.value(
"android-no-deploy-qt-libs"_L1);
1249 if (!noDeploy.isUndefined()) {
1257 const QJsonValue stdcppPath = jsonObject.value(
"stdcpp-path"_L1);
1258 if (stdcppPath.isUndefined()) {
1259 fprintf(stderr,
"No stdcpp-path defined in json file.\n");
1266 const QJsonValue qmlRootPath = jsonObject.value(
"qml-root-path"_L1);
1267 if (qmlRootPath.isString()) {
1268 options->
rootPaths.push_back(qmlRootPath.toString());
1269 }
else if (qmlRootPath.isArray()) {
1270 auto qmlRootPaths = qmlRootPath.toArray();
1271 for (
auto path : qmlRootPaths) {
1272 if (
path.isString())
1281 const QJsonValue qmlImportPaths = jsonObject.value(
"qml-import-paths"_L1);
1287 const QJsonValue qmlImportScannerBinaryPath = jsonObject.value(
"qml-importscanner-binary"_L1);
1293 const QJsonValue rccBinaryPath = jsonObject.value(
"rcc-binary"_L1);
1299 const QJsonValue applicationBinary = jsonObject.value(
"application-binary"_L1);
1301 fprintf(stderr,
"No application binary defined in json file.\n");
1305 if (options->
build) {
1311 fprintf(stderr,
"Cannot find application binary in build dir %s.\n",
qPrintable(appBinaryPath));
1319 const QJsonValue androidPackageName = jsonObject.value(
"android-package-name"_L1);
1321 if (!extractedPackageName.isEmpty())
1323 else if (!androidPackageName.isUndefined())
1324 options->
packageName = androidPackageName.toString();
1331 fprintf(stderr,
"Warning: Package name contained illegal characters and was cleaned "
1338 const QJsonValue deploymentDependencies = jsonObject.value(
"deployment-dependencies"_L1);
1339 if (!deploymentDependencies.isUndefined()) {
1340 QString deploymentDependenciesString = deploymentDependencies.toString();
1341 const auto dependencies =
QStringView{deploymentDependenciesString}.
split(u
',');
1342 for (
const auto &dependency : dependencies) {
1347 if (dirEntry.isFile()) {
1350 if (!arch.isEmpty()) {
1353 }
else if (options->
verbose) {
1354 fprintf(stderr,
"Skipping \"%s\", unknown architecture\n",
qPrintable(
subPath));
1360 auto qtDependency = [options](
const QStringView &dependency,
1362 const auto installDir = options->
architectures[arch].qtInstallDirectory;
1369 if (!arch.isEmpty()) {
1370 options->
qtDependencies[arch].append(qtDependency(dependency, arch));
1371 }
else if (options->
verbose) {
1372 fprintf(stderr,
"Skipping \"%s\", unknown architecture\n",
qPrintable(
path));
1377 options->
qtDependencies[arch].append(qtDependency(dependency, arch));
1384 const QJsonValue qrcFiles = jsonObject.value(
"qrcFiles"_L1);
1388 const QJsonValue zstdCompressionFlag = jsonObject.value(
"zstdCompression"_L1);
1389 if (zstdCompressionFlag.isBool()) {
1406 if (
entry.isDir()) {
1408 if (!destinationDirectory.mkpath(
dir.dirName())) {
1409 fprintf(stderr,
"Cannot make directory %s in %s\n",
qPrintable(
dir.dirName()),
qPrintable(destinationDirectory.path()));
1413 if (!
copyFiles(
dir,
QDir(destinationDirectory.path() + u
'/' +
dir.dirName()), options, forceOverwrite))
1429 if (
dir.fileName() !=
"libs"_L1)
1448 if (!sourceDirectory.exists()) {
1449 fprintf(stderr,
"Cannot find template directory %s\n",
qPrintable(sourceDirectory.absolutePath()));
1467 if (!sourceDirectory.exists()) {
1468 fprintf(stderr,
"Cannot find template directory %s\n",
qPrintable(sourceDirectory.absolutePath()));
1484 fprintf(stdout,
"Copying Android package template.\n");
1504 fprintf(stdout,
"Copying Android sources from project.\n");
1507 if (!sourceDirectory.exists()) {
1523 fprintf(stdout,
"Copying %zd external libraries to package.\n",
size_t(options->
extraLibs.size()));
1526 fprintf(stdout,
"Skip copying of external libraries.\n");
1533 if (!extraLibInfo.exists()) {
1534 fprintf(stderr,
"External library %s does not exist!\n",
qPrintable(extraLib));
1539 fprintf(stdout,
"Skipping \"%s\", architecture mismatch.\n",
qPrintable(extraLib));
1542 if (!extraLibInfo.fileName().startsWith(
"lib"_L1) || extraLibInfo.suffix() !=
"so"_L1) {
1543 fprintf(stderr,
"The file name of external library %s must begin with \"lib\" and end with the suffix \".so\".\n",
1551 + extraLibInfo.fileName());
1584 fprintf(stdout,
"Copying %zd external resources to package.\n",
size_t(options->
extraPlugins.size()));
1587 QFileInfo extraResourceInfo(extraResource);
1588 if (!extraResourceInfo.exists() || !extraResourceInfo.isDir()) {
1589 fprintf(stderr,
"External resource %s does not exist or not a correct directory!\n",
qPrintable(extraResource));
1593 QDir resourceDir(extraResource);
1595 resourceDir.dirName() + u
'/';
1600 QString originFile(resourceDir.filePath(resourceFile));
1602 if (!resourceFile.endsWith(
".so"_L1)) {
1603 destinationFile = assetsDir + resourceFile;
1609 destinationFile = libsDir + resourceFile;
1632 bool hasReplacements =
false;
1635 if (
it.key() ==
it.value())
1642 hasReplacements =
true;
1649 if (hasReplacements) {
1667 fprintf(stdout,
" -- res/values/libs.xml\n");
1671 fprintf(stderr,
"Cannot find %s in prepared packaged. This file is required.\n",
qPrintable(
fileName));
1683 qtLibs +=
" <item>%1;%2</item>\n"_L1.
arg(
it.key(), options->
stdCppName);
1685 if (bundledFile.second.startsWith(
"lib/lib"_L1)) {
1686 if (!bundledFile.second.endsWith(
".so"_L1)) {
1688 "The bundled library %s doesn't end with .so. Android only supports "
1689 "versionless libraries ending with the .so suffix.\n",
1693 QString s = bundledFile.second.
mid(
sizeof(
"lib/lib") - 1);
1694 s.
chop(
sizeof(
".so") - 1);
1695 qtLibs +=
" <item>%1;%2</item>\n"_L1.arg(
it.key(),
s);
1702 if (extraLibInfo.fileName().startsWith(
"lib"_L1)) {
1703 if (!extraLibInfo.fileName().endsWith(
".so"_L1)) {
1705 "The library %s doesn't end with .so. Android only supports "
1706 "versionless libraries ending with the .so suffix.\n",
1712 extraLibs +=
" <item>%1;%2</item>\n"_L1.
arg(
it.key(),
name);
1720 if (localLibs.isEmpty()) {
1723 if (qtDependency.relativePath.contains(
"libplugins_platforms_qtforandroid_"_L1))
1724 plugin = qtDependency.relativePath;
1726 if (qtDependency.relativePath.contains(
1728 || qtDependency.relativePath.contains(
1734 if (plugin.isEmpty()) {
1736 fprintf(stderr,
"No platform plugin (libplugins_platforms_qtforandroid.so) included"
1737 " in the deployment. Make sure the app links to Qt Gui library.\n");
1742 localLibs.append(plugin);
1744 fprintf(stdout,
" -- Using platform plugin %s\n",
qPrintable(plugin));
1748 for (
auto &lib : localLibs) {
1749 if (lib.endsWith(
".so"_L1))
1750 lib = lib.mid(lib.lastIndexOf(u
'/') + 1);
1752 allLocalLibs +=
" <item>%1;%2</item>\n"_L1.arg(
it.key(), localLibs.join(u
':'));
1757 QHash<QString, QString> replacements;
1758 replacements[
QStringLiteral(
"<!-- %%INSERT_QT_LIBS%% -->")] += qtLibs.trimmed();
1759 replacements[
QStringLiteral(
"<!-- %%INSERT_LOCAL_LIBS%% -->")] = allLocalLibs.trimmed();
1762 replacements[
QStringLiteral(
"<!-- %%INSERT_INIT_CLASSES%% -->")] = initClasses;
1765 replacements[
QStringLiteral(
"<!-- %%BUNDLE_LOCAL_QT_LIBS%% -->")]
1767 replacements[
QStringLiteral(
"<!-- %%USE_LOCAL_QT_LIBS%% -->")] =
"1"_L1;
1768 replacements[
QStringLiteral(
"<!-- %%SYSTEM_LIBS_PREFIX%% -->")] =
1780 fprintf(stdout,
" -- res/values/strings.xml\n");
1782 QHash<QString, QString> replacements;
1788 fprintf(stdout,
" -- Create strings.xml since it's missing.\n");
1794 file.
write(
QByteArray(
"<?xml version='1.0' encoding='utf-8'?><resources><string name=\"app_name\" translatable=\"false\">")
1796 .append(
"</string></resources>\n"));
1809 fprintf(stdout,
" -- AndroidManifest.xml \n");
1811 QHash<QString, QString> replacements;
1821 permissions +=
" <uses-permission android:name=\"%1\" />\n"_L1.
arg(permission);
1826 features +=
" <uses-feature android:name=\"%1\" android:required=\"false\" />\n"_L1.
arg(feature);
1828 features +=
" <uses-feature android:glEsVersion=\"0x00020000\" android:required=\"true\" />"_L1;
1833 if (!
updateFile(androidManifestPath, replacements))
1837 bool checkOldAndroidLabelString =
false;
1838 QFile androidManifestXml(androidManifestPath);
1839 if (androidManifestXml.exists()) {
1841 fprintf(stderr,
"Cannot open %s for reading.\n",
qPrintable(androidManifestPath));
1845 QXmlStreamReader reader(&androidManifestXml);
1846 while (!reader.atEnd()) {
1849 if (reader.isStartElement()) {
1850 if (reader.name() ==
"uses-sdk"_L1) {
1851 if (reader.attributes().hasAttribute(
"android:minSdkVersion"_L1))
1852 if (reader.attributes().value(
"android:minSdkVersion"_L1).toInt() < 23) {
1853 fprintf(stderr,
"Invalid minSdkVersion version, minSdkVersion must be >= 23\n");
1856 }
else if ((reader.name() ==
"application"_L1 ||
1857 reader.name() ==
"activity"_L1) &&
1858 reader.attributes().hasAttribute(
"android:label"_L1) &&
1859 reader.attributes().value(
"android:label"_L1) ==
"@string/app_name"_L1) {
1860 checkOldAndroidLabelString =
true;
1861 }
else if (reader.name() ==
"meta-data"_L1) {
1862 const auto name = reader.attributes().value(
"android:name"_L1);
1863 const auto value = reader.attributes().value(
"android:value"_L1);
1864 if (
name ==
"android.app.lib_name"_L1 &&
value.contains(u
' ')) {
1865 fprintf(stderr,
"The Activity's android.app.lib_name should not contain"
1873 if (reader.hasError()) {
1878 fprintf(stderr,
"No android manifest file");
1882 if (checkOldAndroidLabelString)
1891 fprintf(stdout,
"Updating Android package files with project settings.\n");
1907 if (relativeFileName.startsWith(
"lib/"_L1)) {
1916 const QString path = prefix + u
'/' + relativeFileName;
1921 if (relativeFileName.endsWith(
"-android-dependencies.xml"_L1)) {
1928 u
'/' + relativeFileName;
1931 if (relativeFileName.startsWith(
"jar/"_L1)) {
1933 u
'/' + relativeFileName;
1936 if (relativeFileName.startsWith(
"lib/"_L1)) {
1938 u
'/' + relativeFileName.
mid(
sizeof(
"lib/") - 1);
1946 return QList<QtDependency>();
1949 QList<QtDependency>
ret;
1962 return QList<QtDependency>() <<
QtDependency(
info.absoluteFilePath().mid(rootPath.
size()),
info.absoluteFilePath());
1974 QList<QtDependency> deps;
1977 if (
info.exists()) {
1996 QSet<QString> &usedDependencies,
1997 QSet<QString> &remainingDependencies)
2000 if (usedDependencies.contains(
fileName.absolutePath))
2003 if (
fileName.absolutePath.endsWith(
".so"_L1)) {
2005 &remainingDependencies)) {
2006 fprintf(stdout,
"Skipping file dependency: %s\n",
2011 usedDependencies.insert(
fileName.absolutePath);
2014 fprintf(stdout,
"Appending file dependency: %s\n",
qPrintable(
fileName.relativePath));
2023 QSet<QString> *usedDependencies,
2024 QSet<QString> *remainingDependencies)
2028 QFile androidDependencyFile(androidDependencyName);
2029 if (androidDependencyFile.exists()) {
2031 fprintf(stdout,
"Reading Android dependencies for %s\n",
qPrintable(moduleName));
2034 fprintf(stderr,
"Cannot open %s for reading.\n",
qPrintable(androidDependencyName));
2038 QXmlStreamReader reader(&androidDependencyFile);
2039 while (!reader.atEnd()) {
2042 if (reader.isStartElement()) {
2043 if (reader.name() ==
"bundled"_L1) {
2044 if (!reader.attributes().hasAttribute(
"file"_L1)) {
2045 fprintf(stderr,
"Invalid android dependency file: %s\n",
qPrintable(androidDependencyName));
2049 QString file = reader.attributes().value(
"file"_L1).toString();
2051 if (reader.attributes().hasAttribute(
"type"_L1)
2052 && reader.attributes().value(
"type"_L1) ==
"plugin_dir"_L1
2059 *remainingDependencies);
2060 }
else if (reader.name() ==
"jar"_L1) {
2061 int bundling = reader.attributes().value(
"bundling"_L1).toInt();
2065 if (!usedDependencies->contains(dependency.
absolutePath)) {
2071 if (reader.attributes().hasAttribute(
"initClass"_L1)) {
2072 options->
initClasses.append(reader.attributes().value(
"initClass"_L1).toString());
2074 }
else if (reader.name() ==
"lib"_L1) {
2076 if (reader.attributes().hasAttribute(
"replaces"_L1)) {
2077 QString replaces = reader.attributes().value(
"replaces"_L1).toString();
2090 }
else if (reader.name() ==
"permission"_L1) {
2091 QString name = reader.attributes().value(
"name"_L1).toString();
2093 }
else if (reader.name() ==
"feature"_L1) {
2094 QString name = reader.attributes().value(
"name"_L1).toString();
2100 if (reader.hasError()) {
2104 }
else if (options->
verbose) {
2105 fprintf(stdout,
"No android dependencies for %s\n",
qPrintable(moduleName));
2108 options->
features.removeDuplicates();
2117 fprintf(stderr,
"Command does not exist: %s\n",
qPrintable(readElf));
2124 if (!readElfCommand) {
2125 fprintf(stderr,
"Cannot execute command %s\n",
qPrintable(readElf));
2131 bool readLibs =
false;
2133 while (fgets(
buffer,
sizeof(
buffer), readElfCommand.get()) !=
nullptr) {
2152 QString libraryName =
"lib/"_L1 + library;
2162 QSet<QString> *usedDependencies,
2163 QSet<QString> *remainingDependencies)
2170 for (
const QString &dep : dependencies)
2174 QList<QString> dependenciesToCheck;
2175 for (
const QString &dependency : dependencies) {
2176 if (usedDependencies->contains(dependency))
2180 usedDependencies->insert(dependency);
2182 absoluteDependencyPath,
2184 remainingDependencies)) {
2190 fprintf(stdout,
"Appending dependency: %s\n",
qPrintable(dependency));
2191 dependenciesToCheck.append(dependency);
2194 for (
const QString &dependency : std::as_const(dependenciesToCheck)) {
2195 QString qtBaseName = dependency.
mid(
sizeof(
"lib/lib") - 1);
2196 qtBaseName = qtBaseName.
left(qtBaseName.size() - (
sizeof(
".so") - 1));
2208 fprintf(stdout,
"Scanning for QML imports.\n");
2215 "/qmlimportscanner"_L1);
2232 importPaths +=
shellQuote(prefix +
"/qml"_L1);
2239 fprintf(stderr,
"Warning: QML import path %s does not exist.\n",
2244 bool qmlImportExists =
false;
2246 for (
const QString &
import : importPaths) {
2248 qmlImportExists =
true;
2255 if (!qmlImportExists) {
2256 fprintf(stderr,
"Warning: no 'qml' directory found under Qt install directory "
2257 "or import paths. Skipping QML dependency scanning.\n");
2262 fprintf(stderr,
"%s: qmlimportscanner not found at %s\n",
2263 qmlImportExists ?
"Error"_L1.data() :
"Warning"_L1.data(),
2268 for (
auto rootPath : options->
rootPaths) {
2271 if (!rootPath.endsWith(u
'/'))
2275 if (!rootPath.isEmpty())
2278 qmlImportScanner +=
" -rootPath %1"_L1.arg(
shellQuote(rootPath));
2281 if (!options->
qrcFiles.isEmpty()) {
2282 qmlImportScanner +=
" -qrcFiles"_L1;
2284 qmlImportScanner += u
' ' +
shellQuote(qrcFile);
2287 qmlImportScanner +=
" -importPath %1"_L1.arg(importPaths.join(u
' '));
2290 fprintf(stdout,
"Running qmlimportscanner with the following command: %s\n",
2291 qmlImportScanner.toLocal8Bit().constData());
2294 auto qmlImportScannerCommand =
openProcess(qmlImportScanner);
2295 if (qmlImportScannerCommand == 0) {
2296 fprintf(stderr,
"Couldn't run qmlimportscanner.\n");
2302 while (fgets(
buffer,
sizeof(
buffer), qmlImportScannerCommand.get()) !=
nullptr)
2306 if (jsonDocument.isNull()) {
2307 fprintf(stderr,
"Invalid json output from qmlimportscanner.\n");
2312 for (
int i=0;
i<jsonArray.count(); ++
i) {
2314 if (!
value.isObject()) {
2315 fprintf(stderr,
"Invalid format of qmlimportscanner output.\n");
2320 QString path =
object.value(
"path"_L1).toString();
2321 if (
path.isEmpty()) {
2322 fprintf(stderr,
"Warning: QML import could not be resolved in any of the import paths: %s\n",
2326 fprintf(stdout,
" -- Adding '%s' as QML dependency\n",
qPrintable(
path));
2331 if (!
info.exists()) {
2333 fprintf(stdout,
" -- Skipping because path does not exist.\n");
2346 fprintf(stdout,
" -- Skipping because path is in QML root path.\n");
2350 QString importPathOfThisImport;
2351 for (
const QString &importPath : std::as_const(importPaths)) {
2354 importPathOfThisImport = importPath;
2359 if (importPathOfThisImport.isEmpty()) {
2360 fprintf(stderr,
"Import found outside of import paths: %s.\n",
qPrintable(
info.absoluteFilePath()));
2364 importPathOfThisImport =
QDir(importPathOfThisImport).
absolutePath() + u
'/';
2365 QList<QtDependency> qmlImportsDependencies;
2366 auto collectQmlDependency = [&usedDependencies, &qmlImportsDependencies,
2367 &importPathOfThisImport](
const QString &filePath) {
2368 if (!usedDependencies->contains(filePath)) {
2369 usedDependencies->
insert(filePath);
2371 "qml/"_L1 + filePath.mid(importPathOfThisImport.size()),
2376 QString plugin =
object.value(
"plugin"_L1).toString();
2377 bool pluginIsOptional =
object.value(
"pluginIsOptional"_L1).toBool();
2379 path + u
'/' +
"lib"_L1 + plugin + u
'_'
2381 QString pluginFilePath = pluginFileInfo.absoluteFilePath();
2382 QSet<QString> remainingDependencies;
2385 &remainingDependencies)) {
2386 collectQmlDependency(pluginFilePath);
2387 }
else if (!pluginIsOptional) {
2389 fprintf(stdout,
" -- Skipping because the required plugin is missing.\n");
2394 if (qmldirFileInfo.exists()) {
2395 collectQmlDependency(qmldirFileInfo.absoluteFilePath());
2398 QString prefer =
object.value(
"prefer"_L1).toString();
2402 if (!prefer.startsWith(
":/"_L1)) {
2404 object.
value(
"components"_L1).toArray().toVariantList();
2405 qmlFiles.
append(
object.
value(
"scripts"_L1).toArray().toVariantList());
2406 bool qmlFilesMissing =
false;
2407 for (
const auto &qmlFileEntry : qmlFiles) {
2408 QFileInfo fileInfo(qmlFileEntry.toString());
2409 if (!fileInfo.
exists()) {
2410 qmlFilesMissing =
true;
2416 if (qmlFilesMissing) {
2419 " -- Skipping because the required qml files are missing.\n");
2434 for (
auto rootPath : options->
rootPaths) {
2444 fprintf(stdout,
"Running command '%s'\n",
qPrintable(command));
2448 fprintf(stderr,
"Cannot run command '%s'\n",
qPrintable(command));
2454 fprintf(stdout,
"%s",
buffer);
2465 if (!
QDir{
"%1/android_rcc_bundle"_L1.arg(assetsDir)}.
exists()) {
2466 fprintf(stdout,
"Skipping createRCC\n");
2471 fprintf(stdout,
"Create rcc bundle.\n");
2482 fprintf(stderr,
"rcc not found: %s\n",
qPrintable(rcc));
2487 fprintf(stderr,
"Cannot set current dir to: %s\n",
qPrintable(
"%1/android_rcc_bundle"_L1.arg(assetsDir)));
2491 bool res =
runCommand(options,
"%1 --project -o %2"_L1.arg(rcc,
shellQuote(
"%1/android_rcc_bundle.qrc"_L1.arg(assetsDir))));
2497 noZstd =
"--no-zstd"_L1;
2499 QFile::rename(
"%1/android_rcc_bundle.qrc"_L1.arg(assetsDir),
"%1/android_rcc_bundle/android_rcc_bundle.qrc"_L1.arg(assetsDir));
2501 res =
runCommand(options,
"%1 %2 %3 --binary -o %4 android_rcc_bundle.qrc"_L1.arg(rcc,
shellQuote(
"--root=/android_rcc_bundle/"_L1),
2503 shellQuote(
"%1/android_rcc_bundle.rcc"_L1.arg(assetsDir))));
2505 fprintf(stderr,
"Cannot set current dir to: %s\n",
qPrintable(currentDir));
2509 QFile::remove(
"%1/android_rcc_bundle.qrc"_L1.arg(assetsDir));
2518 fprintf(stdout,
"Detecting dependencies of application.\n");
2523 fprintf(stdout,
"\tDependencies explicitly overridden in .pro file. No detection needed.\n");
2527 QSet<QString> usedDependencies;
2528 QSet<QString> remainingDependencies;
2534 QList<QtDependency> pluginDeps;
2542 while (!remainingDependencies.isEmpty()) {
2545 remainingDependencies.erase(
start);
2553 fprintf(stdout,
"Skipping %s due to unmet dependencies: %s\n",
2563 fprintf(stdout,
"Skipping %s due to unmet dependencies: %s\n",
2580 if (!options->
build)
2584 fprintf(stdout,
"Checking if application binary is in package.\n");
2591 applicationFileName);
2593#if defined(Q_OS_WIN32)
2594 const auto makeTool =
"mingw32-make"_L1;
2596 const auto makeTool =
"make"_L1;
2598 fprintf(stderr,
"Application binary is not in output directory: %s. Please run '%s install INSTALL_ROOT=%s' first.\n",
2612 fprintf(stderr,
"Cannot find adb tool: %s\n",
qPrintable(adb));
2622 fprintf(stdout,
"Running command \"%s\"\n", adb.toLocal8Bit().constData());
2625 if (adbCommand == 0) {
2626 fprintf(stderr,
"Cannot start adb: %s\n",
qPrintable(adb));
2635 if (!
file.endsWith(
".so"_L1))
2643 for (
const QString &lib : libs) {
2646 unmetDependencies->append(lib);
2661 fprintf(stdout,
"Copying dependencies from Qt into the package build folder,"
2662 "skipping native libraries.\n");
2667 if (!options->
build)
2671 QString libsDirectory =
"libs/"_L1;
2674 auto assetsDestinationDirectory =
"assets/android_rcc_bundle/"_L1;
2676 QString sourceFileName = qtDependency.absolutePath;
2678 bool isSharedLibrary = qtDependency.relativePath.
endsWith(
".so"_L1);
2679 if (isSharedLibrary) {
2680 QString garbledFileName = qtDependency.relativePath.
mid(
2681 qtDependency.relativePath.lastIndexOf(u
'/') + 1);
2682 destinationFileName = libsDirectory + options->
currentArchitecture + u
'/' + garbledFileName;
2684 destinationFileName = libsDirectory + qtDependency.relativePath.
mid(
sizeof(
"jar/") - 1);
2686 destinationFileName = assetsDestinationDirectory + qtDependency.relativePath;
2690 fprintf(stderr,
"Source Qt file does not exist: %s.\n",
qPrintable(sourceFileName));
2695 if (!
goodToCopy(options, sourceFileName, &unmetDependencies)) {
2696 if (unmetDependencies.isEmpty()) {
2698 fprintf(stdout,
" -- Skipping %s, architecture mismatch.\n",
2702 fprintf(stdout,
" -- Skipping %s. It has unmet dependencies: %s.\n",
2731 if (equalSignIndex >= 0) {
2739 ret +=
info.canonicalFilePath();
2752#if defined(Q_OS_WIN32)
2801 QFile oldFile(oldPathStr);
2803 while (!oldFile.atEnd()) {
2805 QList<QByteArray> prop(
line.
split(
'='));
2806 if (prop.size() > 1) {
2807 GradleProperties::iterator
it =
properties.find(prop.at(0).trimmed());
2827#if defined(Q_OS_WIN32)
2828void checkAndWarnGradleLongPaths(
const QString &outputDirectory)
2833 QDir::Files,
F::Recursive)) {
2835 longFileNames.append(dirEntry.filePath());
2838 if (!longFileNames.isEmpty()) {
2840 "The maximum path length that can be processed by Gradle on Windows is %d characters.\n"
2841 "Consider moving your project to reduce its path length.\n"
2842 "The following files have too long paths:\n%s.\n",
2861 if (!gradleConfigs.setsLegacyPackaging)
2862 gradleProperties[
"android.bundle.enableUncompressedNativeLibs"] =
"false";
2864 gradleProperties[
"buildDir"] =
"build";
2865 gradleProperties[
"qtAndroidDir"] =
2867 "/src/android/java"_L1)
2872 gradleProperties[
"qt5AndroidDir"] =
2874 "/src/android/java"_L1)
2880 if (gradleConfigs.usesIntegerCompileSdkVersion) {
2885 sdkPlatformVersion = tmp;
2887 fprintf(stderr,
"Warning: Gradle expects SDK platform version to be an integer, "
2888 "but the set version is not convertible to an integer.");
2892 if (sdkPlatformVersion.isEmpty())
2896 gradleProperties[
"androidCompileSdkVersion"] = sdkPlatformVersion;
2897 gradleProperties[
"qtMinSdkVersion"] = options.
minSdkVersion;
2900 if (gradleProperties[
"androidBuildToolsVersion"].isEmpty())
2910 gradleProperties[
"qtTargetAbiList"] = abiList.
toLocal8Bit();
2911 gradleProperties[
"qtGradlePluginType"] = options.
buildAar
2912 ?
"com.android.library"
2913 :
"com.android.application";
2922 fprintf(stderr,
"Cannot set permissions %s\n",
qPrintable(gradlePath));
2934 commandLine +=
" bundle"_L1;
2937 commandLine +=
" --info"_L1;
2940 if (gradleCommand == 0) {
2941 fprintf(stderr,
"Cannot run gradle command: %s\n.",
qPrintable(commandLine));
2946 while (fgets(
buffer,
sizeof(
buffer), gradleCommand.get()) !=
nullptr) {
2947 fprintf(stdout,
"%s",
buffer);
2951 const int errorCode = pclose(gradleCommand.release());
2952 if (errorCode != 0) {
2953 fprintf(stderr,
"Building the android package failed!\n");
2955 fprintf(stderr,
" -- For more information, run this command with --verbose.\n");
2957#if defined(Q_OS_WIN32)
2964 fprintf(stderr,
"Cannot change back to old path: %s\n",
qPrintable(oldPath));
2974 fprintf(stdout,
"Uninstalling old Android package %s if present.\n",
qPrintable(options.
packageName));
2978 if (adbCommand == 0)
2983 while (fgets(
buffer,
sizeof(
buffer), adbCommand.get()) !=
nullptr)
2985 fprintf(stdout,
"%s",
buffer);
2988 const int returnCode = pclose(adbCommand.release());
2989 if (returnCode != 0) {
2990 fprintf(stderr,
"Warning: Uninstall failed!\n");
2992 fprintf(stderr,
" -- Run with --verbose for more information.\n");
3012 static const QHash<PackageType, QLatin1StringView> packageTypeToPath{
3015 static const QHash<PackageType, QLatin1StringView> packageTypeToExtension{
3022 signedSuffix =
"-signed"_L1;
3024 signedSuffix =
"-unsigned"_L1;
3027 dirPath +=
"/build/outputs/%1/"_L1.
arg(packageTypeToPath[packageType]);
3028 if (
QDir(dirPath + buildType).exists())
3029 dirPath += buildType;
3035 packageTypeToExtension[packageType]);
3048 fprintf(stdout,
"Installing Android package to device.\n");
3050 auto adbCommand =
runAdb(options,
" install -r "_L1
3053 if (adbCommand == 0)
3058 while (fgets(
buffer,
sizeof(
buffer), adbCommand.get()) !=
nullptr)
3060 fprintf(stdout,
"%s",
buffer);
3063 const int returnCode = pclose(adbCommand.release());
3064 if (returnCode != 0) {
3065 fprintf(stderr,
"Installing to device failed!\n");
3067 fprintf(stderr,
" -- Run with --verbose for more information.\n");
3087 fprintf(stdout,
"Copying STL library\n");
3093 fprintf(stderr,
"STL library does not exist at %s\n",
qPrintable(stdCppPath));
3113 fprintf(stderr,
"zipalign tool not found: %s\n",
qPrintable(zipAlignTool));
3118 return zipAlignTool;
3124 fprintf(stdout,
"Signing Android package.\n");
3135 jarSignerTool = jdkPath +
"/bin/"_L1 + jarSignerTool;
3138 fprintf(stderr,
"Cannot find jarsigner in JAVA_HOME or PATH. Please use --jdk option to pass in the correct path to JDK.\n");
3142 jarSignerTool =
"%1 -sigalg %2 -digestalg %3 -keystore %4"_L1
3167 jarSignerTool +=
" -internalsf"_L1;
3170 jarSignerTool +=
" -sectionsonly"_L1;
3173 jarSignerTool +=
" -protected"_L1;
3182 if (jarSignerCommand == 0) {
3183 fprintf(stderr,
"Couldn't run jarsigner.\n");
3189 while (fgets(
buffer,
sizeof(
buffer), jarSignerCommand.get()) !=
nullptr)
3190 fprintf(stdout,
"%s",
buffer);
3193 const int errorCode = pclose(jarSignerCommand.release());
3194 if (errorCode != 0) {
3195 fprintf(stderr,
"jarsigner command failed.\n");
3197 fprintf(stderr,
" -- Run with --verbose for more information.\n");
3220 auto zipalignRunner = [](
const QString &zipAlignCommandLine) {
3221 auto zipAlignCommand =
openProcess(zipAlignCommandLine);
3222 if (zipAlignCommand == 0) {
3223 fprintf(stderr,
"Couldn't run zipalign.\n");
3228 while (fgets(
buffer,
sizeof(
buffer), zipAlignCommand.get()) !=
nullptr)
3229 fprintf(stdout,
"%s",
buffer);
3231 return pclose(zipAlignCommand.release()) == 0;
3234 const QString verifyZipAlignCommandLine =
3240 if (zipalignRunner(verifyZipAlignCommandLine)) {
3242 fprintf(stdout,
"APK already aligned, copying it for signing.\n");
3248 fprintf(stderr,
"Could not copy unsigned APK.\n");
3253 fprintf(stdout,
"APK not aligned, aligning it for signing.\n");
3255 const QString zipAlignCommandLine =
3256 "%1%2 -f 4 %3 %4"_L1
3262 if (!zipalignRunner(zipAlignCommandLine)) {
3263 fprintf(stderr,
"zipalign command failed.\n");
3265 fprintf(stderr,
" -- Run with --verbose for more information.\n");
3270 QString apkSignCommand =
"%1 sign --ks %2"_L1
3283 apkSignCommand +=
" --verbose"_L1;
3287 auto apkSignerRunner = [](
const QString &command,
bool verbose) {
3289 if (apkSigner == 0) {
3290 fprintf(stderr,
"Couldn't run apksigner.\n");
3295 while (fgets(
buffer,
sizeof(
buffer), apkSigner.get()) !=
nullptr)
3296 fprintf(stdout,
"%s",
buffer);
3298 const int errorCode = pclose(apkSigner.release());
3299 if (errorCode != 0) {
3300 fprintf(stderr,
"apksigner command failed.\n");
3302 fprintf(stderr,
" -- Run with --verbose for more information.\n");
3309 if (!apkSignerRunner(apkSignCommand, options.
verbose))
3312 const QString apkVerifyCommand =
3313 "%1 verify --verbose %2"_L1
3348 fprintf(stdout,
"Writing dependency file.\n");
3363 depFile.write(
": ");
3366 depFile.write(
" \\\n ");
3370 depFile.write(
"\n");
3391 fprintf(stdout,
"[TIMING] %lld ns: Read input file\n", options.
timer.
nsecsElapsed());
3394 "Generating Android Package\n"
3396 " Output directory: %s\n"
3397 " Application binary: %s\n"
3398 " Android build platform: %s\n"
3399 " Install to device: %s\n",
3409 bool androidTemplatetCopied =
false;
3415 it.value().qtInstallDirectory,
3416 it.value().qtDirectories);
3422 fprintf(stdout,
"[TIMING] %lld ns: Cleaned Android file\n", options.
timer.
nsecsElapsed());
3428 fprintf(stdout,
"[TIMING] %lld ns: Copied Android template\n", options.
timer.
nsecsElapsed());
3429 androidTemplatetCopied =
true;
3436 fprintf(stdout,
"[TIMING] %lld ns: Read dependencies\n", options.
timer.
nsecsElapsed());
3442 fprintf(stdout,
"[TIMING] %lld ns: Copied Qt files\n", options.
timer.
nsecsElapsed());
3448 fprintf(stdout,
"[TIMING] %lld ms: Copied extra libs\n", options.
timer.
nsecsElapsed());
3454 fprintf(stdout,
"[TIMING] %lld ns: Copied extra resources\n", options.
timer.
nsecsElapsed());
3460 fprintf(stdout,
"[TIMING] %lld ns: Copied GNU STL\n", options.
timer.
nsecsElapsed());
3464 QString appLibPath =
"%1/libs/%2/lib%3_%2.so"_L1.
3474 fprintf(stdout,
"[TIMING] %lld ns: Checked for application binary\n", options.
timer.
nsecsElapsed());
3477 fprintf(stdout,
"[TIMING] %lld ns: Bundled Qt libs\n", options.
timer.
nsecsElapsed());
3495 if (options.
build) {
3500 fprintf(stdout,
"[TIMING] %lld ns: Copied android sources\n", options.
timer.
nsecsElapsed());
3506 fprintf(stdout,
"[TIMING] %lld ns: Updated files\n", options.
timer.
nsecsElapsed());
3509 fprintf(stdout,
"[TIMING] %lld ns: Created project\n", options.
timer.
nsecsElapsed());
3515 fprintf(stdout,
"[TIMING] %lld ns: Built project\n", options.
timer.
nsecsElapsed());
3524 fprintf(stdout,
"[TIMING] %lld ns: Signed package\n", options.
timer.
nsecsElapsed());
3531 fprintf(stdout,
"[TIMING] %lld ns: Installed APK\n", options.
timer.
nsecsElapsed());
3536 fprintf(stdout,
"Android package built successfully in %.3f ms.\n", options.
timer.
elapsed() / 1000.);
3539 fprintf(stdout,
" -- It can now be run from the selected device/emulator.\n");
int toInt(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an int using base base, which is ten by default.
QList< QByteArray > split(char sep) const
Splits the byte array into subarrays wherever sep occurs, and returns the list of those arrays.
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
static QStringList arguments()
The QDirListing class provides an STL-style iterator for directory entries.
IteratorFlag
This enum class describes flags can be used to configure the behavior of QDirListing.
QStringList entryList(Filters filters=NoFilter, SortFlags sort=NoSort) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QString dirName() const
Returns the name of the directory; this is not the same as the path, e.g.
static bool isRelativePath(const QString &path)
Returns true if path is relative; returns false if it is absolute.
static QString fromNativeSeparators(const QString &pathName)
static bool setCurrent(const QString &path)
Sets the application's current working directory to path.
static QDir current()
Returns the application's current directory.
QString absolutePath() const
Returns the absolute path (a path that starts with "/" or with a drive specification),...
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QString filePath(const QString &fileName) const
Returns the path name of a file in the directory.
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
QString relativeFilePath(const QString &fileName) const
Returns the path to fileName relative to the directory.
static QString currentPath()
Returns the absolute path of the application's current directory.
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
qint64 nsecsElapsed() const noexcept
bool atEnd() const override
Returns true if the end of the file has been reached; otherwise returns false.
void close() override
Calls QFileDevice::flush() and closes the file.
QString baseName() const
Returns the base name of the file without the path.
bool isExecutable() const
QString absoluteFilePath() const
bool isFile() const
Returns true if this object points to a file or to a symbolic link to a file.
QString absolutePath() const
Returns the absolute path of the file system entry this QFileInfo refers to, excluding the entry's na...
QString canonicalFilePath() const
Returns the file system entry's canonical path, including the entry's name, that is,...
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
bool copy(const QString &newName)
Copies the file named fileName() to newName.
bool remove()
Removes the file specified by fileName().
QString fileName() const override
Returns the name set by setFileName() or to the QFile constructors.
bool rename(const QString &newName)
Renames the file currently specified by fileName() to newName.
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
qsizetype size() const noexcept
Returns the number of items in the hash.
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
QList< Key > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
iterator erase(const_iterator it)
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
qint64 readLine(char *data, qint64 maxlen)
This function reads a line of ASCII characters from the device, up to a maximum of maxSize - 1 bytes,...
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
QString errorString() const
Returns a human readable description of the last error that occurred.
\inmodule QtCore\reentrant
\inmodule QtCore\reentrant
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
\inmodule QtCore\reentrant
QJsonValue value(const QString &key) const
Returns a QJsonValue representing the value for the key key.
\inmodule QtCore\reentrant
bool isString() const
Returns true if the value contains a string.
QJsonObject toObject() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool toBool(bool defaultValue=false) const
Converts the value to a bool and returns it.
QString toString() const
Converts the value to a QString and returns it.
bool isObject() const
Returns true if the value contains an object.
bool isUndefined() const
Returns true if the value is undefined.
qsizetype size() const noexcept
bool isEmpty() const noexcept
const_reference at(qsizetype i) const noexcept
T value(qsizetype i) const
const T & constFirst() const noexcept
void append(parameter_type t)
\inmodule QtCore \reentrant
const_iterator constBegin() const noexcept
const_iterator constEnd() const noexcept
QVariant value(QAnyStringView key, const QVariant &defaultValue) const
Returns the value for setting key.
bool endsWith(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Q_CORE_EXPORT QList< QStringView > split(QStringView sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the view into substring views wherever sep occurs, and returns the list of those string views.
QString toString() const
Returns a deep copy of this string view's data as a QString.
\macro QT_RESTRICTED_CAST_FROM_ASCII
QString left(qsizetype n) const &
QByteArray toLatin1() const &
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
QString & replace(qsizetype i, qsizetype len, QChar after)
void chop(qsizetype n)
Removes n characters from the end of the string.
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
QString mid(qsizetype position, qsizetype n=-1) const &
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
void clear()
Clears the contents of the string and makes it null.
qsizetype size() const noexcept
Returns the number of characters in this string.
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
QString & insert(qsizetype i, QChar c)
QByteArray toLocal8Bit() const &
QString & append(QChar c)
QString trimmed() const &
QByteArray toUtf8() const &
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
static QByteArray escapeAndEncodeDependencyPath(const QString &path)
list append(new Employee("Blackpool", "Stephen"))
QSet< QString >::iterator it
QList< QVariant > arguments
static const struct @480 keywords[]
static QByteArray cleaned(const QByteArray &input)
size_t qstrlen(const char *str)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
static const QPainterPath::ElementType * subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end, const qreal *points, bool *closed)
static const QCssKnownValue properties[NumProperties - 1]
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char * destination
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLsizei const GLuint * paths
GLsizei const GLchar *const * path
static QString absolutePath(const QString &path)
static QString keyName(const QString &rKey)
#define qPrintable(string)
#define QStringLiteral(str)
bool uninstallApk(const Options &options)
bool containsApplicationBinary(Options *options)
void cleanTopFolders(const Options &options, const QDir &srcDir, const QString &dstDir)
QString packagePath(const Options &options, PackageType packageType)
bool writeDependencyFile(const Options &options)
void cleanAndroidFiles(const Options &options)
bool runCommand(const Options &options, const QString &command)
void readDependenciesFromFiles(Options *options, const QList< QtDependency > &files, QSet< QString > &usedDependencies, QSet< QString > &remainingDependencies)
bool alwaysOverwritableFile(const QString &fileName)
static QStringList dependenciesForDepfile
bool checkCanImportFromRootPaths(const Options *options, const QString &absolutePath, const QString &moduleUrl)
QList< QtDependency > findFilesRecursively(const Options &options, const QFileInfo &info, const QString &rootPath)
bool checkArchitecture(const Options &options, const QString &fileName)
static QString absoluteFilePath(const Options *options, const QString &relativeFileName)
bool readAndroidDependencyXml(Options *options, const QString &moduleName, QSet< QString > *usedDependencies, QSet< QString > *remainingDependencies)
GradleBuildConfigs gradleBuildConfigs(const QString &path)
bool createRcc(const Options &options)
bool updateFile(const QString &fileName, const QHash< QString, QString > &replacements)
QStringList getQtLibsFromElf(const Options &options, const QString &fileName)
bool updateLibsXml(Options *options)
QStringList getLibraryProjectsInOutputFolder(const Options &options)
static QString batSuffixAppended(QString path)
bool copyPackage(const Options &options)
bool buildAndroidProject(const Options &options)
bool signAAB(const Options &options)
QString extractPackageName(Options *options)
auto runAdb(const Options &options, const QString &arguments) -> decltype(openProcess({}))
bool quasiLexicographicalReverseLessThan(const QFileInfo &fi1, const QFileInfo &fi2)
@ SyntaxErrorOrHelpRequested
@ CannotCopyAndroidExtraLibs
@ CannotCreateAndroidProject
@ CannotCopyAndroidSources
@ CannotCopyAndroidExtraResources
@ CannotUpdateAndroidFiles
@ CannotFindApplicationBinary
@ CannotCopyAndroidTemplate
@ CannotBuildAndroidProject
QString architectureFromName(const QString &name)
bool readInputFile(Options *options)
static QString zipalignPath(const Options &options, bool *ok)
bool copyAndroidSources(const Options &options)
bool copyAndroidExtraResources(Options *options)
QString fileArchitecture(const Options &options, const QString &path)
static const bool mustReadOutputAnyway
bool isDeployment(const Options *options, Options::DeploymentMechanism deployment)
static QString execSuffixAppended(QString path)
static const QHash< QByteArray, QByteArray > elfArchitectures
QStringList allFilesInside(const QDir ¤t, const QDir &rootDir)
bool scanImports(Options *options, QSet< QString > *usedDependencies)
static bool mergeGradleProperties(const QString &path, GradleProperties properties)
QString defaultLibexecDir()
bool readDependenciesFromElf(Options *options, const QString &fileName, QSet< QString > *usedDependencies, QSet< QString > *remainingDependencies)
bool copyAndroidExtraLibs(Options *options)
bool updateAndroidFiles(Options &options)
bool readInputFileDirectory(Options *options, QJsonObject &jsonObject, const QString keyName)
QString findInPath(const QString &fileName)
QString detectLatestAndroidPlatform(const QString &sdkPath)
bool copyStdCpp(Options *options)
QString cleanPackageName(QString packageName, bool *cleaned=nullptr)
static QString llvmReadobjPath(const Options &options)
bool copyAndroidTemplate(const Options &options, const QString &androidTemplate, const QString &outDirPrefix=QString())
bool copyGradleTemplate(const Options &options)
bool updateStringsXml(const Options &options)
bool copyQtFiles(Options *options)
bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, const Options &options, bool forceOverwrite=false)
QMap< QByteArray, QByteArray > GradleProperties
auto openProcess(const QString &command)
bool updateAndroidManifest(Options &options)
bool parseCmakeBoolean(const QJsonValue &value)
void deleteMissingFiles(const Options &options, const QDir &srcDir, const QDir &dstDir)
bool readDependencies(Options *options)
bool installApk(const Options &options)
bool copyFileIfNewer(const QString &sourceFileName, const QString &destinationFileName, const Options &options, bool forceOverwrite=false)
bool goodToCopy(const Options *options, const QString &file, QStringList *unmetDependencies)
bool signPackage(const Options &options)
static GradleProperties readGradleProperties(const QString &path)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
QT_BEGIN_NAMESPACE typedef uchar * output
static QString shellQuote(const QString &arg)
QSettings settings("MySoft", "Star Runner")
[0]
settings remove("monkey")
QUrl url("example.com")
[constructor-url-reference]
char * toString(const MyType &t)
[31]
bool usesIntegerCompileSdkVersion
std::pair< QString, QString > BundledFile
bool copyDependenciesOnly
std::vector< QString > rootPaths
QString currentArchitecture
QByteArray targetSdkVersion
void setCurrentQtArchitecture(const QString &arch, const QString &directory, const QHash< QString, QString > &directories)
QString qtInstallDirectory
DeploymentMechanism deploymentMechanism
bool protectedAuthenticationPath
QStringList qmlImportPaths
bool qmlSkipImportScanning
QHash< QString, QStringList > archExtraPlugins
QString sdkBuildToolsVersion
QString qtLibExecsDirectory
QHash< QString, QStringList > archExtraLibs
QString applicationArguments
QString qmlImportScannerBinaryPath
QHash< QString, QString > qtDirectories
QHash< QString, QtInstallDirectoryWithTriple > architectures
QStringList androidDeployPlugins
QHash< QString, QList< BundledFile > > bundledFiles
QHash< QString, QList< QtDependency > > qtDependencies
QString qtPluginsDirectory
std::vector< QString > extraPrefixDirs
QHash< QString, QStringList > localLibs
QString applicationBinary
std::vector< QString > extraLibraryDirs
bool isZstdCompressionEnabled
QString androidSourceDirectory
\inmodule QtCore \reentrant
bool operator==(const QtDependency &other) const
QtDependency(const QString &rpath, const QString &apath)
QString qtInstallDirectory
QHash< QString, QString > qtDirectories
QtInstallDirectoryWithTriple(const QString &dir=QString(), const QString &t=QString(), const QHash< QString, QString > &dirs=QHash< QString, QString >())