diff options
-rw-r--r-- | .travis.yml | 11 | ||||
-rw-r--r-- | doc/reference/items/convenience/application.qdoc | 5 | ||||
-rw-r--r-- | doc/reference/modules/android-sdk-module.qdoc | 9 | ||||
-rw-r--r-- | docker-compose.yml | 4 | ||||
-rw-r--r-- | docker/bionic/test-android.Dockerfile | 4 | ||||
-rw-r--r-- | share/qbs/imports/qbs/base/AndroidApk.qbs | 2 | ||||
-rw-r--r-- | share/qbs/imports/qbs/base/Application.qbs | 2 | ||||
-rw-r--r-- | share/qbs/module-providers/Qt/templates/android_support.qbs | 6 | ||||
-rw-r--r-- | share/qbs/modules/Android/sdk/sdk.qbs | 65 | ||||
-rw-r--r-- | share/qbs/modules/Android/sdk/utils.js | 90 | ||||
-rw-r--r-- | src/lib/corelib/api/project.cpp | 5 | ||||
-rw-r--r-- | src/lib/corelib/api/projectdata.cpp | 6 | ||||
-rw-r--r-- | src/lib/corelib/api/projectdata_p.h | 4 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml | 1 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml | 1 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackboxandroid.cpp | 609 |
16 files changed, 562 insertions, 262 deletions
diff --git a/.travis.yml b/.travis.yml index ef86db701..e5a73e9f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,14 +64,21 @@ jobs: - docker-compose run --rm bionic scripts/run-analyzer.sh - <<: *build-on-bionic - name: With Qbs and with Qt for Android + name: With Qbs and with Qt 5.13 for Android before_install: - docker-compose pull bionic - docker-compose pull bionic-android-513 - - docker-compose pull bionic-android-514 script: - docker-compose run bionic qbs build modules.cpp.compilerWrapper:ccache modules.qbsbuildconfig.enableBundledQt:true config:release - docker-compose run bionic-android-513 scripts/test-qt-for-android.sh release/install-root/usr/local/bin + + - <<: *build-on-bionic + name: With Qbs and with Qt 5.14 for Android + before_install: + - docker-compose pull bionic + - docker-compose pull bionic-android-514 + script: + - docker-compose run bionic qbs build modules.cpp.compilerWrapper:ccache modules.qbsbuildconfig.enableBundledQt:true config:release - docker-compose run bionic-android-514 scripts/test-qt-for-android.sh release/install-root/usr/local/bin - &build-on-macos diff --git a/doc/reference/items/convenience/application.qdoc b/doc/reference/items/convenience/application.qdoc index e92247f5f..ca77e153c 100644 --- a/doc/reference/items/convenience/application.qdoc +++ b/doc/reference/items/convenience/application.qdoc @@ -41,8 +41,9 @@ The target artifact of this type of product is usually an executable binary tagged \c "application". However, on Android, unless you set \l{Product::}{consoleApplication} to \c true, - the application target will be an APK package tagged \c "android.apk", and a - dependency to the \l{Android.sdk} module is automatically added to the product. + the application target will be an APK or an AAB package tagged \c "android.package" + according to the \l{Android.sdk}{packageType} property. A dependency to the \l{Android.sdk} + module is automatically added to the product. */ /*! diff --git a/doc/reference/modules/android-sdk-module.qdoc b/doc/reference/modules/android-sdk-module.qdoc index 74599d0ba..676eaa825 100644 --- a/doc/reference/modules/android-sdk-module.qdoc +++ b/doc/reference/modules/android-sdk-module.qdoc @@ -241,3 +241,12 @@ \defaultvalue \c "aapt" */ + +/*! + \qmlproperty string Android.sdk::packageType + + Type of the package. Allowed options: "apk" and "aab". + Type "apk" generates a runnable package whereas "aab" generates a package for Google Play. + + \defaultvalue \c "apk" +*/ diff --git a/docker-compose.yml b/docker-compose.yml index ac9ba433f..b8b4528c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: bionic-android-513: << : *linux hostname: bionic-android - image: ${DOCKER_USER:-qbsbuild}/qbsdev:bionic-android-5.13.2-2 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:bionic-android-5.13.2-3 build: dockerfile: docker/bionic/test-android.Dockerfile context: . @@ -36,7 +36,7 @@ services: bionic-android-514: << : *linux hostname: bionic-android - image: ${DOCKER_USER:-qbsbuild}/qbsdev:bionic-android-5.14.0-2 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:bionic-android-5.14.0-3 build: dockerfile: docker/bionic/test-android.Dockerfile context: . diff --git a/docker/bionic/test-android.Dockerfile b/docker/bionic/test-android.Dockerfile index 56bb0b9e6..3ec8b4808 100644 --- a/docker/bionic/test-android.Dockerfile +++ b/docker/bionic/test-android.Dockerfile @@ -89,6 +89,10 @@ RUN mkdir ${ANDROID_SDK_ROOT}/samples && \ rm -v master.zip && \ mv android-BasicMediaDecoder-master android-BasicMediaDecoder +# Download buildtool to generate aab packages in ${ANDROID_SDK_ROOT} +RUN cd ${ANDROID_SDK_ROOT} && \ + curl -sLO https://github.com/google/bundletool/releases/download/0.15.0/bundletool-all-0.15.0.jar + USER root # diff --git a/share/qbs/imports/qbs/base/AndroidApk.qbs b/share/qbs/imports/qbs/base/AndroidApk.qbs index 5f86d8457..70cf6aa93 100644 --- a/share/qbs/imports/qbs/base/AndroidApk.qbs +++ b/share/qbs/imports/qbs/base/AndroidApk.qbs @@ -32,7 +32,7 @@ import qbs.File import qbs.FileInfo Product { - type: ["android.apk"] + type: ["android.package"] qbs.targetPlatform: "android" Depends { name: "Android.sdk" } } diff --git a/share/qbs/imports/qbs/base/Application.qbs b/share/qbs/imports/qbs/base/Application.qbs index 63ffc6283..1e4f805a8 100644 --- a/share/qbs/imports/qbs/base/Application.qbs +++ b/share/qbs/imports/qbs/base/Application.qbs @@ -29,7 +29,7 @@ ****************************************************************************/ NativeBinary { - type: isForAndroid && !consoleApplication ? ["android.apk"] : ["application"] + type: isForAndroid && !consoleApplication ? ["android.package"] : ["application"] property bool usesNativeCode diff --git a/share/qbs/module-providers/Qt/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs index 342018321..2835a9336 100644 --- a/share/qbs/module-providers/Qt/templates/android_support.qbs +++ b/share/qbs/module-providers/Qt/templates/android_support.qbs @@ -18,7 +18,7 @@ Module { property string _androidDeployQtFilePath: FileInfo.joinPaths(_qtInstallDir, "bin", "androiddeployqt") property string _qtInstallDir - property bool _enableSdkSupport: product.type && product.type.contains("android.apk") + property bool _enableSdkSupport: product.type && product.type.contains("android.package") && !consoleApplication property bool _enableNdkSupport: !product.aggregate || product.multiplexConfigurationId property string _templatesBaseDir: FileInfo.joinPaths(_qtInstallDir, "src", "android") @@ -312,7 +312,7 @@ Module { File.move(product.Qt.android_support._deployQtOutDir + "/AndroidManifest.xml", outputs["android.manifest_final"][0].filePath); var libsDir = product.Qt.android_support._deployQtOutDir + "/libs"; - var libDir = product.Android.sdk.apkContentsDir + "/lib"; + var libDir = product.Android.sdk.packageContentsDir + "/lib"; var listFilePath = outputs["android.deployqt_list"][0].filePath; var oldLibs = []; try { @@ -364,7 +364,7 @@ Module { for (var i in inputs["android.nativelibrary"]) architectures.push(inputs["android.nativelibrary"][i].Android.ndk.abi); for (var i in architectures) { - var abiDirPath = FileInfo.joinPaths(product.Android.sdk.apkContentsDir, + var abiDirPath = FileInfo.joinPaths(product.Android.sdk.packageContentsDir, "lib", architectures[i]); var files = File.directoryEntries(abiDirPath, File.Files); for (var i = 0; i < files.length; ++i) { diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs index 681b3da94..31f3ed465 100644 --- a/share/qbs/modules/Android/sdk/sdk.qbs +++ b/share/qbs/modules/Android/sdk/sdk.qbs @@ -50,6 +50,14 @@ Module { environmentPaths: (ndkDir ? [ndkDir] : []).concat(base) } + Probes.PathProbe { + id: bundletoolProbe + platformSearchPaths: [Android.sdk.sdkDir] + names: ["bundletool-all"] + nameSuffixes: ["-0.11.0.jar", "-0.12.0.jar", "-0.13.0.jar", "-0.13.3.jar", "-0.13.4.jar", + "-0.14.0.jar", "-0.15.0.jar"] + } + property path sdkDir: sdkProbe.path property path ndkDir: ndkProbe.path property path ndkSamplesDir: ndkProbe.samplesDir @@ -60,6 +68,8 @@ Module { property int buildToolsVersionPatch: buildToolsVersionParts[2] property string platform: sdkProbe.platform + property path bundletoolFilePath: bundletoolProbe.filePath + // Product-specific properties and files property string packageName: { var idx = product.name.indexOf(".") @@ -146,6 +156,12 @@ Module { } property path aaptFilePath: FileInfo.joinPaths(buildToolsDir, aaptName) readonly property bool _enableAapt2: aaptName === "aapt2" + property string packageType: "apk" + PropertyOptions { + name: "packageType" + allowedValues: ["apk", "aab"] + } + readonly property bool _generateAab: packageType == "aab" property path apksignerFilePath: FileInfo.joinPaths(buildToolsDir, "apksigner") property path aidlFilePath: FileInfo.joinPaths(buildToolsDir, "aidl") @@ -160,7 +176,7 @@ Module { (packageName || "").split('.').join('/')) property path compiledResourcesDir: FileInfo.joinPaths(product.buildDirectory, "compiled_resources") - property string apkContentsDir: FileInfo.joinPaths(product.buildDirectory, "bin") + property string packageContentsDir: FileInfo.joinPaths(product.buildDirectory, packageType) property string debugKeyStorePath: FileInfo.joinPaths( Environment.getEnv(qbs.hostOS.contains("windows") ? "USERPROFILE" : "HOME"), @@ -186,6 +202,14 @@ Module { + "ANDROID_HOME environment variable to a valid " + "Android SDK location."); } + if (!bundletoolFilePath && _generateAab) { + throw ModUtils.ModuleError("Could not find Android bundletool at the following " + + "location:\n\t" + Android.sdk.sdkDir + + "\nInstall the Android bundletool to the above location, " + + "or set the Android.sdk.bundletoolFilePath property.\n" + + "Android bundletool can be downloaded from " + + "https://github.com/google/bundletool"); + } } FileTagger { @@ -366,12 +390,13 @@ Module { condition: _enableRules && _enableAapt2 multiplex: true inputs: ["android.resources_compiled", "android.assets", "android.manifest_final"] - outputFileTags: ["java.java", "android.apk_base"] + outputFileTags: ["java.java", "android.apk_resources"] outputArtifacts: { var artifacts = []; artifacts.push({ - filePath: product.Android.sdk.apkBaseName + ".apk_base", - fileTags: ["android.apk_base"] + filePath: product.Android.sdk.apkBaseName + (product.Android.sdk._generateAab ? + ".apk_aab" : ".apk_apk"), + fileTags: ["android.apk_resources"] }); var resources = inputs["android.resources_compiled"]; if (resources && resources.length) { @@ -419,7 +444,10 @@ Module { inputs: ["java.class"] inputsFromDependencies: ["java.jar"] Artifact { - filePath: FileInfo.joinPaths(product.Android.sdk.apkContentsDir, "classes.dex") + filePath: product.Android.sdk._generateAab ? + FileInfo.joinPaths(product.Android.sdk.packageContentsDir, "dex", + "classes.dex") : + FileInfo.joinPaths(product.Android.sdk.packageContentsDir, "classes.dex") fileTags: ["android.dex"] } prepare: SdkUtils.prepareDex.apply(SdkUtils, arguments) @@ -431,7 +459,7 @@ Module { inputsFromDependencies: inputTags inputs: product.aggregate ? [] : inputTags Artifact { - filePath: FileInfo.joinPaths(product.Android.sdk.apkContentsDir, "lib", + filePath: FileInfo.joinPaths(product.Android.sdk.packageContentsDir, "lib", input.Android.ndk.abi, input.fileName) fileTags: "android.nativelibrary_deployed" } @@ -475,7 +503,7 @@ Module { } Rule { - condition: _enableRules && !_enableAapt2 + condition: _enableRules && !_enableAapt2 && !_generateAab multiplex: true inputs: [ "android.resources", "android.assets", "android.manifest_final", @@ -484,23 +512,38 @@ Module { ] Artifact { filePath: product.Android.sdk.apkBaseName + ".apk" - fileTags: "android.apk" + fileTags: "android.package" } prepare: SdkUtils.prepareAaptPackage.apply(SdkUtils, arguments) } Rule { - condition: _enableRules && _enableAapt2 + condition: _enableRules && _enableAapt2 && !_generateAab multiplex: true inputs: [ - "android.apk_base", "android.manifest_final", + "android.apk_resources", "android.manifest_final", "android.dex", "android.stl_deployed", "android.nativelibrary_deployed", "android.keystore" ] Artifact { filePath: product.Android.sdk.apkBaseName + ".apk" - fileTags: "android.apk" + fileTags: "android.package" } prepare: SdkUtils.prepareApkPackage.apply(SdkUtils, arguments) } + + Rule { + condition: _enableRules && _enableAapt2 && _generateAab + multiplex: true + inputs: [ + "android.apk_resources", "android.manifest_final", + "android.dex", "android.stl_deployed", + "android.nativelibrary_deployed" + ] + Artifact { + filePath: product.Android.sdk.apkBaseName + ".aab" + fileTags: "android.package" + } + prepare: SdkUtils.prepareBundletoolPackage.apply(SdkUtils, arguments) + } } diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js index b1c929e68..63bf1f5c4 100644 --- a/share/qbs/modules/Android/sdk/utils.js +++ b/share/qbs/modules/Android/sdk/utils.js @@ -79,7 +79,7 @@ function prepareDex(project, product, inputs, outputs, input, output, explicitly args = args.concat(jarFiles); var cmd = new Command(dxFilePath, args); - cmd.description = "Creating " + output.fileName; + cmd.description = "creating " + output.fileName; return [cmd]; } @@ -183,13 +183,12 @@ function prepareAapt2CompileResource(project, product, inputs, outputs, input, o function prepareAapt2Link(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var cmds = []; + var baseOutputFilePath = outputs["android.apk_resources"][0].filePath; var manifestFilePath = inputs["android.manifest_final"][0].filePath; var compilesResourcesDir = product.Android.sdk.compiledResourcesDir; - var apkOutputFilePath = outputs["android.apk_base"][0].filePath; var compiledResources = inputs["android.resources_compiled"]; - var args = ["link", "-o", apkOutputFilePath, "-I", product.Android.sdk.androidJarFilePath]; - //For aab: args.push("link", "--proto-format"); + var args = ["link", "-o", baseOutputFilePath, "-I", product.Android.sdk.androidJarFilePath]; var i = 0; if (compiledResources) { for (i = 0; i < compiledResources.length; ++i) @@ -217,9 +216,10 @@ function prepareAapt2Link(project, product, inputs, outputs, input, output, expl args.push("-A", assetDirs[i]); if (product.qbs.buildVariant === "debug") args.push("-v"); - + if (product.Android.sdk._generateAab) + args.push("--proto-format"); var cmd = new Command(product.Android.sdk.aaptFilePath, args); - cmd.description = "Linking resources"; + cmd.description = "linking resources"; cmd.workingDirectory = product.buildDirectory; cmds.push(cmd); @@ -234,18 +234,18 @@ function prepareAaptGenerate(project, product, inputs, outputs, input, output, if (resources && resources.length) args.push("-J", ModUtils.moduleProperty(product, "generatedJavaFilesBaseDir")); var cmd = new Command(product.Android.sdk.aaptFilePath, args); - cmd.description = "Processing resources"; + cmd.description = "processing resources"; return [cmd]; } function prepareAaptPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var cmds = []; - var apkOutput = outputs["android.apk"][0]; + var apkOutput = outputs["android.package"][0]; var args = commonAaptPackageArgs.apply(this, arguments); args.push("-F", apkOutput.filePath + ".unaligned"); - args.push(product.Android.sdk.apkContentsDir); + args.push(product.Android.sdk.packageContentsDir); var cmd = new Command(product.Android.sdk.aaptFilePath, args); - cmd.description = "Generating " + apkOutput.filePath; + cmd.description = "generating " + apkOutput.fileName; cmds.push(cmd); if (!product.Android.sdk.useApksigner) { @@ -255,7 +255,7 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex apkOutput.filePath + ".unaligned", "androiddebugkey"]; cmd = new Command(product.java.jarsignerFilePath, args); - cmd.description = "Signing " + apkOutput.fileName; + cmd.description = "signing " + apkOutput.fileName; cmds.push(cmd); } @@ -277,7 +277,7 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex "--ks-pass", "pass:android", apkOutput.filePath]; cmd = new Command(product.Android.sdk.apksignerFilePath, args); - cmd.description = "Signing " + apkOutput.fileName; + cmd.description = "signing " + apkOutput.fileName; cmds.push(cmd); } @@ -286,9 +286,9 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex function prepareApkPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var cmds = []; - var apkInputFilePath = inputs["android.apk_base"][0].filePath; - var apkOutput = outputs["android.apk"][0]; - var apkOutputFilePathUnaligned = outputs["android.apk"][0].filePath + ".unaligned"; + var apkInputFilePath = inputs["android.apk_resources"][0].filePath; + var apkOutput = outputs["android.package"][0]; + var apkOutputFilePathUnaligned = outputs["android.package"][0].filePath + ".unaligned"; var dexFilePath = inputs["android.dex"][0].filePath; var copyCmd = new JavaScriptCommand(); @@ -301,7 +301,7 @@ function prepareApkPackage(project, product, inputs, outputs, input, output, exp cmds.push(copyCmd); var jarArgs = ["-uvf", apkOutputFilePathUnaligned, "."]; - var libPath = FileInfo.joinPaths(product.Android.sdk.apkContentsDir); + var libPath = FileInfo.joinPaths(product.Android.sdk.packageContentsDir); var jarCmd = new Command(product.java.jarFilePath, jarArgs); jarCmd.description = "packaging files"; jarCmd.workingDirectory = libPath; @@ -314,7 +314,7 @@ function prepareApkPackage(project, product, inputs, outputs, input, output, exp apkOutputFilePathUnaligned, "androiddebugkey"]; cmd = new Command(product.java.jarsignerFilePath, args); - cmd.description = "Signing " + apkOutput.fileName; + cmd.description = "signing " + apkOutput.fileName; cmds.push(cmd); } @@ -336,13 +336,65 @@ function prepareApkPackage(project, product, inputs, outputs, input, output, exp "--ks-pass", "pass:android", apkOutput.filePath]; cmd = new Command(product.Android.sdk.apksignerFilePath, args); - cmd.description = "Signing " + apkOutput.fileName; + cmd.description = "signing " + apkOutput.fileName; cmds.push(cmd); } return cmds; } +function prepareBundletoolPackage(project, product, inputs, outputs, input, output, + explicitlyDependsOn) { + var cmds = []; + var baseModuleDir = product.Android.sdk.packageContentsDir; + var manifestDirName = FileInfo.joinPaths(baseModuleDir, "manifest"); + var pkgBaseFileName = inputs["android.apk_resources"][0].filePath; + + var jarResourcesArgs = ["xf", pkgBaseFileName]; + var jarResourcesCmd = new Command(product.java.jarFilePath, jarResourcesArgs); + jarResourcesCmd.description = "extracting resources apk"; + jarResourcesCmd.workingDirectory = baseModuleDir; + cmds.push(jarResourcesCmd); + + var moveManifestCmd = new JavaScriptCommand(); + moveManifestCmd.description = "moving manifest in manifest directory"; + moveManifestCmd.path = manifestDirName; + moveManifestCmd.manifestFilePath = baseModuleDir + "/AndroidManifest.xml"; + moveManifestCmd.sourceCode = function() { + if (!File.exists(path)) + File.makePath(path); + if (File.exists(manifestFilePath)) + File.move(manifestFilePath, path + "/AndroidManifest.xml"); + } + cmds.push(moveManifestCmd); + + var baseFilePath = FileInfo.joinPaths(product.buildDirectory, "base.zip"); + var jarBaseArgs = ["cfM", baseFilePath, "."]; + var jarBaseCmd = new Command(product.java.jarFilePath, jarBaseArgs); + jarBaseCmd.description = "compressing base module"; + jarBaseCmd.workingDirectory = baseModuleDir; + cmds.push(jarBaseCmd); + + var aabFilePath = outputs["android.package"][0].filePath; + var removeCmd = new JavaScriptCommand(); + removeCmd.description = "removing previous aab"; + removeCmd.filePath = aabFilePath; + removeCmd.sourceCode = function() { + if (File.exists(filePath)) + File.remove(filePath); + } + cmds.push(removeCmd); + + var args = ["-jar", product.Android.sdk.bundletoolFilePath, "build-bundle"]; + args.push("--modules=" + baseFilePath); + args.push("--output=" + aabFilePath); + var cmd = new Command(product.java.interpreterFilePath, args); + cmd.description = "generating " + outputs["android.package"][0].fileName; + cmds.push(cmd); + + return cmds; +} + function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath) { var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", "androiddebugkey", "-storepass", "android", "-keypass", "android", "-keyalg", "RSA", @@ -365,7 +417,7 @@ function stlDeploymentData(product, inputs, type) uniqueFilePaths.push(currentInput.filePath); data.uniqueInputs.push(currentInput); var outputFileName = currentInput.fileName; - data.outputFilePaths.push(FileInfo.joinPaths(product.Android.sdk.apkContentsDir, "lib", + data.outputFilePaths.push(FileInfo.joinPaths(product.Android.sdk.packageContentsDir, "lib", currentInput.Android.ndk.abi, outputFileName)); } diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp index 378b13dfe..f7a9eca09 100644 --- a/src/lib/corelib/api/project.cpp +++ b/src/lib/corelib/api/project.cpp @@ -635,7 +635,10 @@ static bool productIsRunnable(const ResolvedProductConstPtr &product) { const bool isBundle = product->moduleProperties->moduleProperty( QStringLiteral("bundle"), QStringLiteral("isBundle")).toBool(); - return isRunnableArtifact(product->fileTags, isBundle); + const QString androidSdkPackageType = product->moduleProperties->moduleProperty( + QStringLiteral("Android.sdk"), QStringLiteral("packageType")).toString(); + const bool isAndroidApk = androidSdkPackageType == QStringLiteral("apk"); + return isRunnableArtifact(product->fileTags, isBundle, isAndroidApk); } static bool productIsMultiplexed(const ResolvedProductConstPtr &product) diff --git a/src/lib/corelib/api/projectdata.cpp b/src/lib/corelib/api/projectdata.cpp index cb7d12b98..c378fbea4 100644 --- a/src/lib/corelib/api/projectdata.cpp +++ b/src/lib/corelib/api/projectdata.cpp @@ -323,8 +323,10 @@ bool ArtifactData::isExecutable() const { const bool isBundle = d->properties.getModuleProperty( QStringLiteral("bundle"), QStringLiteral("isBundle")).toBool(); - return isRunnableArtifact( - FileTags::fromStringList(d->fileTags), isBundle); + const QString androidSdkPackageType = d->properties.getModuleProperty( + QStringLiteral("Android.sdk"), QStringLiteral("packageType")).toString(); + const bool isAndroidApk = androidSdkPackageType == QStringLiteral("apk"); + return isRunnableArtifact(FileTags::fromStringList(d->fileTags), isBundle, isAndroidApk); } /*! diff --git a/src/lib/corelib/api/projectdata_p.h b/src/lib/corelib/api/projectdata_p.h index 834aeec23..e241ea92c 100644 --- a/src/lib/corelib/api/projectdata_p.h +++ b/src/lib/corelib/api/projectdata_p.h @@ -123,11 +123,11 @@ public: QString buildDir; }; -inline bool isRunnableArtifact(const FileTags &fileTags, bool isBundle) +inline bool isRunnableArtifact(const FileTags &fileTags, bool isBundle, bool isAndroidApk) { return (fileTags.contains("application") && (!isBundle || fileTags.contains("bundle.content"))) || fileTags.contains("bundle.application-executable") - || fileTags.contains("android.apk") + || (fileTags.contains("android.package") && isAndroidApk) || fileTags.contains("msi"); } diff --git a/tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml index e8a950847..1d27681ac 100644 --- a/tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml +++ b/tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" + android:versionName="1.0" android:versionCode="1" package="io.qbs.aidltest"> <application android:label="AidlTest"> <activity android:name="MainActivity"> diff --git a/tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml index 575e95e8d..f61dc9850 100644 --- a/tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml +++ b/tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" + android:versionName="1.0" android:versionCode="1" package="somedefault"> <application android:label="MinimalNative"> <activity android:name="MainActivity"> diff --git a/tests/auto/blackbox/tst_blackboxandroid.cpp b/tests/auto/blackbox/tst_blackboxandroid.cpp index e8d44188c..5e850874e 100644 --- a/tests/auto/blackbox/tst_blackboxandroid.cpp +++ b/tests/auto/blackbox/tst_blackboxandroid.cpp @@ -81,6 +81,8 @@ void TestBlackboxAndroid::android() QFETCH(QList<QByteArrayList>, expectedFilesLists); QFETCH(QStringList, qmlAppCustomProperties); QFETCH(bool, enableAapt2); + QFETCH(bool, generateAab); + QFETCH(bool, isIncrementalBuild); const SettingsPtr s = settings(); Profile p(theProfileName(projectDir == "qml-app"), s.get()); @@ -104,7 +106,7 @@ void TestBlackboxAndroid::android() && ndkSamplesDirs.contains(projectDir)) QSKIP("NDK samples directory not present"); - const QString aaptVersion = enableAapt2 ? "aapt2" : "aapt"; + const QString buildSubDir = enableAapt2 ? (generateAab ? "aab" : "aapt2") : "aapt"; QDir::setCurrent(testDataDir + "/" + projectDir); static const QStringList configNames { "debug", "release" }; @@ -112,29 +114,29 @@ void TestBlackboxAndroid::android() auto currentExpectedFilesLists = expectedFilesLists; const QString configArgument = "config:" + configName; QbsRunParameters resolveParams("resolve"); - resolveParams.buildDirectory = aaptVersion; + resolveParams.buildDirectory = buildSubDir; resolveParams.arguments << configArgument << qmlAppCustomProperties; resolveParams.profile = p.name(); QCOMPARE(runQbs(resolveParams), 0); QbsRunParameters buildParams(QStringList{"--command-echo-mode", "command-line", configArgument}); - buildParams.buildDirectory = aaptVersion; + buildParams.buildDirectory = buildSubDir; buildParams.profile = p.name(); QCOMPARE(runQbs(buildParams), 0); for (const QString &productName : qAsConst(productNames)) { const QByteArray tag(QTest::currentDataTag()); - const bool isIncrementalBuild = tag.startsWith("qml app") && tag != "qml app"; QCOMPARE(m_qbsStdout.count("Generating BuildConfig.java"), isIncrementalBuild ? 0 : productNames.size()); - QVERIFY(m_qbsStdout.contains(productName.toLocal8Bit() + ".apk")); - const QString apkFilePath = aaptVersion + "/" + relativeProductBuildDir(productName, - configName) - + '/' + productName + ".apk"; - QVERIFY2(regularFileExists(apkFilePath), qPrintable(apkFilePath)); + const QString packageName = productName + (generateAab ? ".aab" : ".apk"); + QVERIFY(m_qbsStdout.contains(packageName.toLocal8Bit())); + const QString packageFilePath = buildSubDir + "/" + relativeProductBuildDir(productName, + configName) + + '/' + packageName; + QVERIFY2(regularFileExists(packageFilePath), qPrintable(packageFilePath)); const QString jarFilePath = androidPaths["jar"]; QVERIFY(!jarFilePath.isEmpty()); QProcess jar; - jar.start(jarFilePath, QStringList() << "-tf" << apkFilePath); + jar.start(jarFilePath, QStringList() << "-tf" << packageFilePath); QVERIFY2(jar.waitForStarted(), qPrintable(jar.errorString())); QVERIFY2(jar.waitForFinished(), qPrintable(jar.errorString())); QVERIFY2(jar.exitCode() == 0, qPrintable(jar.readAllStandardError().constData())); @@ -209,7 +211,8 @@ void TestBlackboxAndroid::android_data() const auto cxxLibPath = [&p, &pQt](const QByteArray &oldcxxLib, bool forQt) { const bool usesClang = (forQt ? pQt : p).value(QStringLiteral("qbs.toolchainType")) .toString() == "clang"; - return QByteArray("lib/${ARCH}/") + (usesClang ? "libc++_shared.so" : oldcxxLib); + const QByteArray path = "lib/${ARCH}/"; + return path + (usesClang ? "libc++_shared.so" : oldcxxLib); }; bool usingOldQt = true; QStringList qmakeFilePaths = pQt.value(QStringLiteral("moduleProviders.Qt.qmakeFilePaths")). @@ -246,129 +249,229 @@ void TestBlackboxAndroid::android_data() .replace("arm64", "arm64-v8a"); }); - auto expandArchs = [] (const QByteArrayList &archs, const QByteArrayList &lst) { + auto expandArchs = [] (const QByteArrayList &archs, const QByteArrayList &lst, bool aabPackage) { const QByteArray &archPlaceHolder = "${ARCH}"; QByteArrayList result; + QByteArray base( aabPackage ? "base/" : QByteArray()); for (const QByteArray &entry : lst) { if (entry.contains(archPlaceHolder)) { for (const QByteArray &arch : qAsConst(archs)) - result << QByteArray(entry).replace(archPlaceHolder, arch); + result << (base + QByteArray(entry).replace(archPlaceHolder, arch)); } else { - result << entry; + result << (base + entry); } } return result; }; - const QByteArrayList commonFiles = expandArchs(archs, { - "AndroidManifest.xml", "META-INF/ANDROIDD.RSA", "META-INF/ANDROIDD.SF", - "META-INF/MANIFEST.MF", "classes.dex" - }); + auto commonFiles = [](bool generateAab) { + if (generateAab) + return (QByteArrayList() + << "base/manifest/AndroidManifest.xml" << "base/dex/classes.dex" + << "BundleConfig.pb"); + return (QByteArrayList() + << "AndroidManifest.xml" << "META-INF/ANDROIDD.RSA" << "META-INF/ANDROIDD.SF" + << "META-INF/MANIFEST.MF" << "classes.dex"); + }; QTest::addColumn<QString>("projectDir"); QTest::addColumn<QStringList>("productNames"); QTest::addColumn<QList<QByteArrayList>>("expectedFilesLists"); QTest::addColumn<QStringList>("qmlAppCustomProperties"); QTest::addColumn<bool>("enableAapt2"); + QTest::addColumn<bool>("generateAab"); + QTest::addColumn<bool>("isIncrementalBuild"); const auto aaptVersion = [](bool enableAapt2) { return QString("modules.Android.sdk.aaptName:") + (enableAapt2 ? "aapt2" : "aapt"); }; bool enableAapt2 = false; - QByteArrayList teaPotAppExpectedFiles; - teaPotAppExpectedFiles << commonFiles + expandArchs(archs, { - "resources.arsc", - "assets/Shaders/ShaderPlain.fsh", - "assets/Shaders/VS_ShaderPlain.vsh", - cxxLibPath("libgnustl_shared.so", false), - "lib/${ARCH}/libTeapotNativeActivity.so", - "res/layout/widgets.xml", - "res/mipmap-hdpi-v4/ic_launcher.png", - "res/mipmap-mdpi-v4/ic_launcher.png", - "res/mipmap-xhdpi-v4/ic_launcher.png", - "res/mipmap-xxhdpi-v4/ic_launcher.png"}); + const auto packageType = [](bool generateAab) { + return QString("modules.Android.sdk.packageType:") + (generateAab ? "aab" : "apk"); + }; + bool generateAab = false; + bool isIncrementalBuild = false; + + auto teaPotAppExpectedFiles = [&](const QByteArrayList &archs, bool generateAab) { + QByteArrayList expectedFile; + expectedFile << commonFiles(generateAab) + expandArchs(archs, { + "assets/Shaders/ShaderPlain.fsh", + "assets/Shaders/VS_ShaderPlain.vsh", + cxxLibPath("libgnustl_shared.so", false), + "lib/${ARCH}/libTeapotNativeActivity.so", + "res/layout/widgets.xml", + "res/mipmap-hdpi-v4/ic_launcher.png", + "res/mipmap-mdpi-v4/ic_launcher.png", + "res/mipmap-xhdpi-v4/ic_launcher.png", + "res/mipmap-xxhdpi-v4/ic_launcher.png"}, generateAab); + if (generateAab) + expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb"; + else + expectedFile << "resources.arsc"; + return expectedFile; + }; + QTest::newRow("teapot") << "teapot" << QStringList("TeapotNativeActivity") - << (QList<QByteArrayList>() << teaPotAppExpectedFiles) - << QStringList{aaptVersion(enableAapt2)} << enableAapt2; + << (QList<QByteArrayList>() << teaPotAppExpectedFiles(archs, generateAab)) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = true; - QTest::newRow("teapot") + QTest::newRow("teapot aapt2") + << "teapot" << QStringList("TeapotNativeActivity") + << (QList<QByteArrayList>() << teaPotAppExpectedFiles(archs, generateAab)) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; + generateAab = true; + QTest::newRow("teapot aapt2 aab") << "teapot" << QStringList("TeapotNativeActivity") - << (QList<QByteArrayList>() << teaPotAppExpectedFiles) - << QStringList{aaptVersion(enableAapt2)} << enableAapt2; + << (QList<QByteArrayList>() << teaPotAppExpectedFiles(archs, generateAab)) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = false; + generateAab = false; QTest::newRow("minimal-native") << "minimal-native" << QStringList("minimalnative") - << (QList<QByteArrayList>() << commonFiles + expandArchs({archs.first()}, { + << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs({archs.first()}, { "lib/${ARCH}/libminimalnative.so", cxxLibPath("libstlport_shared.so", false), - "lib/${ARCH}/libdependency.so"})) + "lib/${ARCH}/libdependency.so"}, generateAab)) << QStringList{"products.minimalnative.multiplexByQbsProperties:[]", "modules.qbs.architecture:" + archsStringList.first(), aaptVersion(enableAapt2)} - << enableAapt2; + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = true; - QTest::newRow("minimal-native") + QTest::newRow("minimal-native aapt2") << "minimal-native" << QStringList("minimalnative") - << (QList<QByteArrayList>() << commonFiles + expandArchs({archs.first()}, { - "resources.arsc", + << (QList<QByteArrayList>() << commonFiles(generateAab) + + (QByteArrayList() << "resources.arsc") + expandArchs({archs.first()}, { "lib/${ARCH}/libminimalnative.so", cxxLibPath("libstlport_shared.so", false), - "lib/${ARCH}/libdependency.so"})) + "lib/${ARCH}/libdependency.so"}, generateAab)) << QStringList{"products.minimalnative.multiplexByQbsProperties:[]", "modules.qbs.architecture:" + archsStringList.first(), - aaptVersion(enableAapt2)} - << enableAapt2; - QByteArrayList qmlAppExpectedFiles; - QByteArrayList qmlAppMinistroExpectedFiles; - QByteArrayList qmlAppCustomMetaDataExpectedFiles; - QStringList qmlAppCustomProperties; - if (usingOldQt) { - qmlAppExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { - "resources.arsc", - "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes", - "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir", - "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes", - "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir", - "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list", - cxxLibPath("libgnustl_shared.so", true), - "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so", - "lib/${ARCH}/libplugins_imageformats_libqgif.so", - "lib/${ARCH}/libplugins_imageformats_libqicns.so", - "lib/${ARCH}/libplugins_imageformats_libqico.so", - "lib/${ARCH}/libplugins_imageformats_libqjpeg.so", - "lib/${ARCH}/libplugins_imageformats_libqtga.so", - "lib/${ARCH}/libplugins_imageformats_libqtiff.so", - "lib/${ARCH}/libplugins_imageformats_libqwbmp.so", - "lib/${ARCH}/libplugins_imageformats_libqwebp.so", - "lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so", - "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so", - "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so", - "lib/${ARCH}/libQt5Core.so", - "lib/${ARCH}/libQt5Gui.so", - "lib/${ARCH}/libQt5Network.so", - "lib/${ARCH}/libQt5Qml.so", - "lib/${ARCH}/libQt5QuickParticles.so", - "lib/${ARCH}/libQt5Quick.so", - "lib/${ARCH}/libqmlapp.so"}); - qmlAppMinistroExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { - "resources.arsc", + aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; + generateAab = true; + QTest::newRow("minimal-native aapt2 aab") + << "minimal-native" << QStringList("minimalnative") + << (QList<QByteArrayList>() << commonFiles(generateAab) + + (QByteArrayList() << "base/resources.pb" << "base/native.pb") + + expandArchs({archs.first()}, { + "lib/${ARCH}/libminimalnative.so", + cxxLibPath("libstlport_shared.so", false), + "lib/${ARCH}/libdependency.so"}, generateAab)) + << QStringList{"products.minimalnative.multiplexByQbsProperties:[]", + "modules.qbs.architecture:" + archsStringList.first(), + aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; + auto qmlAppExpectedFiles = [&](bool generateAab) { + QByteArrayList expectedFile; + if (usingOldQt) { + expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, { + "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes", + "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir", + "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes", + "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir", + "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list", + cxxLibPath("libgnustl_shared.so", true), + "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so", + "lib/${ARCH}/libplugins_imageformats_libqgif.so", + "lib/${ARCH}/libplugins_imageformats_libqicns.so", + "lib/${ARCH}/libplugins_imageformats_libqico.so", + "lib/${ARCH}/libplugins_imageformats_libqjpeg.so", + "lib/${ARCH}/libplugins_imageformats_libqtga.so", + "lib/${ARCH}/libplugins_imageformats_libqtiff.so", + "lib/${ARCH}/libplugins_imageformats_libqwbmp.so", + "lib/${ARCH}/libplugins_imageformats_libqwebp.so", + "lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so", + "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so", + "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so", + "lib/${ARCH}/libQt5Core.so", + "lib/${ARCH}/libQt5Gui.so", + "lib/${ARCH}/libQt5Network.so", + "lib/${ARCH}/libQt5Qml.so", + "lib/${ARCH}/libQt5QuickParticles.so", + "lib/${ARCH}/libQt5Quick.so", + "lib/${ARCH}/libqmlapp.so"}, generateAab); + } else { + expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, { + "assets/android_rcc_bundle.rcc", + cxxLibPath("libgnustl_shared.so", true), + "lib/${ARCH}/libplugins_bearer_qandroidbearer_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qicns_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qjpeg_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qtga_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so", + "lib/${ARCH}/libplugins_platforms_qtforandroid_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_debugger_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_inspector_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_local_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_messages_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_native_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_nativedebugger_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_profiler_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_preview_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_quickprofiler_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_server_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_tcp_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick.2_qtquick2plugin_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_Window.2_windowplugin_${ARCH}.so", + "lib/${ARCH}/libQt5Core_${ARCH}.so", + "lib/${ARCH}/libQt5Gui_${ARCH}.so", + "lib/${ARCH}/libQt5Network_${ARCH}.so", + "lib/${ARCH}/libQt5Qml_${ARCH}.so", + "lib/${ARCH}/libQt5QuickParticles_${ARCH}.so", + "lib/${ARCH}/libQt5Quick_${ARCH}.so", + "lib/${ARCH}/libQt5QmlModels_${ARCH}.so", + "lib/${ARCH}/libQt5QmlWorkerScript_${ARCH}.so", + "lib/${ARCH}/libqmlapp_${ARCH}.so"}, generateAab); + } + if (generateAab) + expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb"; + else + expectedFile << "resources.arsc"; + return expectedFile; + }; + + auto qmlAppMinistroExpectedFiles = [&](bool generateAab) { + QByteArrayList expectedFile; + if (usingOldQt) { + expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, { "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list", cxxLibPath("libgnustl_shared.so", true), - "lib/${ARCH}/libqmlapp.so"}); - qmlAppCustomMetaDataExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { - "resources.arsc", + "lib/${ARCH}/libqmlapp.so"}, generateAab); + } else { + expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, { + "assets/android_rcc_bundle.rcc", + cxxLibPath("libgnustl_shared.so", true), + "lib/${ARCH}/libqmlapp_${ARCH}.so"}, generateAab); + } + if (generateAab) + expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb"; + else + expectedFile << "resources.arsc"; + return expectedFile; + }; + auto qmlAppCustomMetaDataExpectedFiles = [&](bool generateAab) { + QByteArrayList expectedFile; + if (usingOldQt) { + expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, { "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes", "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir", "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes", @@ -405,53 +508,9 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libQt5Qml.so", "lib/${ARCH}/libQt5QuickParticles.so", "lib/${ARCH}/libQt5Quick.so", - "lib/${ARCH}/libqmlapp.so"}); - qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false", - "modules.qbs.architecture:" + archsForQt.first()}; - } else { - qmlAppExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { - "resources.arsc", - "assets/android_rcc_bundle.rcc", - cxxLibPath("libgnustl_shared.so", true), - "lib/${ARCH}/libplugins_bearer_qandroidbearer_${ARCH}.so", - "lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so", - "lib/${ARCH}/libplugins_imageformats_qicns_${ARCH}.so", - "lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so", - "lib/${ARCH}/libplugins_imageformats_qjpeg_${ARCH}.so", - "lib/${ARCH}/libplugins_imageformats_qtga_${ARCH}.so", - "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so", - "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so", - "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so", - "lib/${ARCH}/libplugins_platforms_qtforandroid_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_debugger_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_inspector_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_local_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_messages_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_native_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_nativedebugger_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_profiler_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_preview_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_quickprofiler_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_server_${ARCH}.so", - "lib/${ARCH}/libplugins_qmltooling_qmldbg_tcp_${ARCH}.so", - "lib/${ARCH}/libqml_QtQuick.2_qtquick2plugin_${ARCH}.so", - "lib/${ARCH}/libqml_QtQuick_Window.2_windowplugin_${ARCH}.so", - "lib/${ARCH}/libQt5Core_${ARCH}.so", - "lib/${ARCH}/libQt5Gui_${ARCH}.so", - "lib/${ARCH}/libQt5Network_${ARCH}.so", - "lib/${ARCH}/libQt5Qml_${ARCH}.so", - "lib/${ARCH}/libQt5QuickParticles_${ARCH}.so", - "lib/${ARCH}/libQt5Quick_${ARCH}.so", - "lib/${ARCH}/libQt5QmlModels_${ARCH}.so", - "lib/${ARCH}/libQt5QmlWorkerScript_${ARCH}.so", - "lib/${ARCH}/libqmlapp_${ARCH}.so"}); - qmlAppMinistroExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { - "resources.arsc", - "assets/android_rcc_bundle.rcc", - cxxLibPath("libgnustl_shared.so", true), - "lib/${ARCH}/libqmlapp_${ARCH}.so"}); - qmlAppCustomMetaDataExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { - "resources.arsc", + "lib/${ARCH}/libqmlapp.so"}, generateAab); + } else { + expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, { "assets/android_rcc_bundle.rcc", "assets/dummyasset.txt", cxxLibPath("libgnustl_shared.so", true), @@ -486,61 +545,115 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libQt5Quick_${ARCH}.so", "lib/${ARCH}/libQt5QmlModels_${ARCH}.so", "lib/${ARCH}/libQt5QmlWorkerScript_${ARCH}.so", - "lib/${ARCH}/libqmlapp_${ARCH}.so"}); + "lib/${ARCH}/libqmlapp_${ARCH}.so"}, generateAab); + } + if (generateAab) + expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb"; + else + expectedFile << "resources.arsc"; + return expectedFile; + }; + QStringList qmlAppCustomProperties; + if (usingOldQt) { + qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false", + "modules.qbs.architecture:" + archsForQt.first()}; + } else { qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false"}; } + // aapt tool for the resources works with a directory option pointing to the parent directory // of the resources (res). // The Qt.android_support module adds res/values/libs.xml (from Qt install dir). So the res from // Qt install res directory is added to aapt. This results in adding res/layout/splash.xml to // the package eventhough the file is not needed. // On the other hand aapt2 requires giving all the resources files. - // Also when enabling aapt2 the resources.arsc is always created, eventhough no resources is + // Also when enabling aapt2 the resources.arsc is always created, eventhough no resources are // declared. enableAapt2 = false; + generateAab = false; QTest::newRow("qml app") << "qml-app" << QStringList("qmlapp") - << (QList<QByteArrayList>() << (QByteArrayList() << qmlAppExpectedFiles + << (QList<QByteArrayList>() << (QByteArrayList() << qmlAppExpectedFiles(generateAab) << "res/layout/splash.xml")) - << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2)) << enableAapt2; + << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2) + << packageType(generateAab)) + << enableAapt2 << generateAab << isIncrementalBuild; + enableAapt2 = true; - QTest::newRow("qml app") + QTest::newRow("qml app aapt2") + << "qml-app" << QStringList("qmlapp") + << (QList<QByteArrayList>() << qmlAppExpectedFiles(generateAab)) + << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2) + << packageType(generateAab)) + << enableAapt2 << generateAab << isIncrementalBuild; + generateAab = true; + QTest::newRow("qml app aab") << "qml-app" << QStringList("qmlapp") - << (QList<QByteArrayList>() << qmlAppExpectedFiles) - << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2)) << enableAapt2; + << (QList<QByteArrayList>() << qmlAppExpectedFiles(generateAab)) + << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2) + << packageType(generateAab)) + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = false; + generateAab = false; + isIncrementalBuild = true; QTest::newRow("qml app using Ministro") << "qml-app" << QStringList("qmlapp") - << (QList<QByteArrayList>() << (QByteArrayList() << qmlAppMinistroExpectedFiles + << (QList<QByteArrayList>() << (QByteArrayList() + << qmlAppMinistroExpectedFiles(generateAab) << "res/layout/splash.xml")) - << QStringList{"modules.Qt.android_support.useMinistro:true", - "modules.Android.sdk.automaticSources:false", - aaptVersion(enableAapt2)} << enableAapt2; + << (QStringList() << "modules.Qt.android_support.useMinistro:true" + << "modules.Android.sdk.automaticSources:false" << aaptVersion(enableAapt2) + << packageType(generateAab)) + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = true; - QTest::newRow("qml app using Ministro") + QTest::newRow("qml app using Ministro aapt2") + << "qml-app" << QStringList("qmlapp") + << (QList<QByteArrayList>() << qmlAppMinistroExpectedFiles(generateAab)) + << (QStringList() << "modules.Qt.android_support.useMinistro:true" + << "modules.Android.sdk.automaticSources:false" << aaptVersion(enableAapt2) + << packageType(generateAab)) + << enableAapt2 << generateAab << isIncrementalBuild; + generateAab = true; + QTest::newRow("qml app using Ministro aab") << "qml-app" << QStringList("qmlapp") - << (QList<QByteArrayList>() << qmlAppMinistroExpectedFiles) - << QStringList{"modules.Qt.android_support.useMinistro:true", - "modules.Android.sdk.automaticSources:false", - aaptVersion(enableAapt2)} << enableAapt2; + << (QList<QByteArrayList>() << qmlAppMinistroExpectedFiles(generateAab)) + << (QStringList() << "modules.Qt.android_support.useMinistro:true" + << "modules.Android.sdk.automaticSources:false" << aaptVersion(enableAapt2) + << packageType(generateAab)) + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = false; + generateAab = false; QTest::newRow("qml app with custom metadata") << "qml-app" << QStringList("qmlapp") - << (QList<QByteArrayList>() << (QByteArrayList() << qmlAppCustomMetaDataExpectedFiles + << (QList<QByteArrayList>() << (QByteArrayList() + << qmlAppCustomMetaDataExpectedFiles(generateAab) << "res/layout/splash.xml")) << QStringList{"modules.Android.sdk.automaticSources:true", - aaptVersion(enableAapt2)} << enableAapt2; + aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = true; - QTest::newRow("qml app with custom metadata") + QTest::newRow("qml app with custom metadata aapt2") << "qml-app" << QStringList("qmlapp") - << (QList<QByteArrayList>() << qmlAppCustomMetaDataExpectedFiles) - << QStringList{"modules.Android.sdk.automaticSources:true", - aaptVersion(enableAapt2)} << enableAapt2; + << (QList<QByteArrayList>() << (QByteArrayList() + << qmlAppCustomMetaDataExpectedFiles(generateAab))) + << QStringList{"modules.Android.sdk.automaticSources:true", aaptVersion(enableAapt2), + packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; + generateAab = true; + QTest::newRow("qml app with custom metadata aab") + << "qml-app" << QStringList("qmlapp") + << (QList<QByteArrayList>() << (QByteArrayList() + << qmlAppCustomMetaDataExpectedFiles(generateAab))) + << QStringList{"modules.Android.sdk.automaticSources:true", aaptVersion(enableAapt2), + packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; + isIncrementalBuild = false; enableAapt2 = false; + generateAab = false; QTest::newRow("no native") << "no-native" << QStringList("com.example.android.basicmediadecoder") - << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, { + << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs(archs, { "resources.arsc", "res/drawable-hdpi-v4/ic_action_play_disabled.png", "res/drawable-hdpi-v4/ic_action_play.png", @@ -557,87 +670,151 @@ void TestBlackboxAndroid::android_data() "res/layout/sample_main.xml", "res/menu/action_menu.xml", "res/menu-v11/action_menu.xml", - "res/raw/vid_bigbuckbunny.mp4"})) - << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + "res/raw/vid_bigbuckbunny.mp4"}, generateAab)) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = true; - QTest::newRow("no native") + auto noNativeExpectedFiles = [&](bool generateAab) { + QByteArrayList expectedFile; + expectedFile << commonFiles(generateAab) + expandArchs(archs, { + "res/drawable-hdpi-v4/ic_action_play_disabled.png", + "res/drawable-hdpi-v4/ic_action_play.png", + "res/drawable-hdpi-v4/ic_launcher.png", + "res/drawable-hdpi-v4/tile.9.png", + "res/drawable-mdpi-v4/ic_action_play_disabled.png", + "res/drawable-mdpi-v4/ic_action_play.png", + "res/drawable-mdpi-v4/ic_launcher.png", + "res/drawable/selector_play.xml", + "res/drawable-xhdpi-v4/ic_action_play_disabled.png", + "res/drawable-xhdpi-v4/ic_action_play.png", + "res/drawable-xhdpi-v4/ic_launcher.png", + "res/drawable-xxhdpi-v4/ic_launcher.png", + "res/layout/sample_main.xml", + "res/menu/action_menu.xml", + // I have no idea why this file is generated with aapt and not with aapt2 + //"res/menu-v11/action_menu.xml", + "res/raw/vid_bigbuckbunny.mp4"}, generateAab); + if (generateAab) + expectedFile << "base/resources.pb"; + else + expectedFile << "resources.arsc"; + return expectedFile; + }; + QTest::newRow("no native aapt2") << "no-native" << QStringList("com.example.android.basicmediadecoder") - << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, { - "resources.arsc", - "res/drawable-hdpi-v4/ic_action_play_disabled.png", - "res/drawable-hdpi-v4/ic_action_play.png", - "res/drawable-hdpi-v4/ic_launcher.png", - "res/drawable-hdpi-v4/tile.9.png", - "res/drawable-mdpi-v4/ic_action_play_disabled.png", - "res/drawable-mdpi-v4/ic_action_play.png", - "res/drawable-mdpi-v4/ic_launcher.png", - "res/drawable/selector_play.xml", - "res/drawable-xhdpi-v4/ic_action_play_disabled.png", - "res/drawable-xhdpi-v4/ic_action_play.png", - "res/drawable-xhdpi-v4/ic_launcher.png", - "res/drawable-xxhdpi-v4/ic_launcher.png", - "res/layout/sample_main.xml", - "res/menu/action_menu.xml", - // I have no idea why this file is generated with aapt and not with aapt2 - //"res/menu-v11/action_menu.xml", - "res/raw/vid_bigbuckbunny.mp4"})) - << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + << (QList<QByteArrayList>() << noNativeExpectedFiles(generateAab)) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; + generateAab = true; + QTest::newRow("no native aab") + << "no-native" + << QStringList("com.example.android.basicmediadecoder") + << (QList<QByteArrayList>() << noNativeExpectedFiles(generateAab)) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = false; + generateAab = false; QTest::newRow("aidl") << "aidl" << QStringList("io.qbs.aidltest") - << (QList<QByteArrayList>() << (QByteArrayList() << commonFiles)) - << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + << (QList<QByteArrayList>() << (QByteArrayList() + << commonFiles(generateAab))) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = true; QTest::newRow("aidl") << "aidl" << QStringList("io.qbs.aidltest") - << (QList<QByteArrayList>() << (QByteArrayList() << commonFiles - << "resources.arsc")) - << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + << (QList<QByteArrayList>() << (QByteArrayList() + << commonFiles(generateAab) + << "resources.arsc")) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; + generateAab = true; + QTest::newRow("aidl") << "aidl" << QStringList("io.qbs.aidltest") + << (QList<QByteArrayList>() << (QByteArrayList() + << commonFiles(generateAab) + << "base/resources.pb")) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = false; + generateAab = false; QTest::newRow("multiple libs") << "multiple-libs-per-apk" << QStringList("twolibs") - << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, { + << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs(archs, { "resources.arsc", "lib/${ARCH}/liblib1.so", "lib/${ARCH}/liblib2.so", - cxxLibPath("libstlport_shared.so", false)})) - << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + cxxLibPath("libstlport_shared.so", false)}, generateAab)) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = true; - QTest::newRow("multiple libs") + QTest::newRow("multiple libs aapt2") << "multiple-libs-per-apk" << QStringList("twolibs") - << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, { + << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs(archs, { "resources.arsc", "lib/${ARCH}/liblib1.so", "lib/${ARCH}/liblib2.so", - cxxLibPath("libstlport_shared.so", false)})) - << QStringList(aaptVersion(enableAapt2)) << enableAapt2; - QByteArrayList expectedFiles1 = qbs::toList(qbs::toSet(commonFiles - + expandArchs(QByteArrayList{"armeabi-v7a", "x86"}, { - "resources.arsc", - "lib/${ARCH}/libp1lib1.so", - cxxLibPath("libstlport_shared.so", false)}) - + expandArchs(QByteArrayList{archs}, { - "resources.arsc", - "lib/${ARCH}/libp1lib2.so", - cxxLibPath("libstlport_shared.so", false)}))); - QByteArrayList expectedFiles2 = commonFiles + expandArchs(archs, { - "lib/${ARCH}/libp2lib1.so", - "lib/${ARCH}/libp2lib2.so", - cxxLibPath("libstlport_shared.so", false)}); + cxxLibPath("libstlport_shared.so", false)}, generateAab)) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; + generateAab = true; + QTest::newRow("multiple libs aab") + << "multiple-libs-per-apk" + << QStringList("twolibs") + << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs(archs, { + "resources.pb", "native.pb", + "lib/${ARCH}/liblib1.so", + "lib/${ARCH}/liblib2.so", + cxxLibPath("libstlport_shared.so", false)}, generateAab)) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = false; + generateAab = false; + auto expectedFiles1 = [&](bool generateAab) { + QByteArrayList expectedFile = qbs::toList(qbs::toSet(commonFiles(generateAab) + + expandArchs(QByteArrayList{"armeabi-v7a", "x86"}, { + "lib/${ARCH}/libp1lib1.so", + cxxLibPath("libstlport_shared.so", false)}, generateAab) + + expandArchs(QByteArrayList{archs}, { + "lib/${ARCH}/libp1lib2.so", + cxxLibPath("libstlport_shared.so", false)}, generateAab))); + if (generateAab) + expectedFile << "base/resources.pb" << "base/native.pb"; + else + expectedFile << "resources.arsc"; + return expectedFile; + }; + auto expectedFiles2 = [&](bool generateAab) { + QByteArrayList expectedFile = commonFiles(generateAab) + expandArchs(archs, { + "lib/${ARCH}/libp2lib1.so", + "lib/${ARCH}/libp2lib2.so", + cxxLibPath("libstlport_shared.so", false)}, generateAab); + return expectedFile; + }; + QTest::newRow("multiple apks") << "multiple-apks-per-project" << (QStringList() << "twolibs1" << "twolibs2") - << QList<QByteArrayList>{expectedFiles1, expectedFiles2} - << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + << QList<QByteArrayList>{expectedFiles1(generateAab), expectedFiles2(generateAab)} + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; enableAapt2 = true; - expectedFiles2 << "resources.arsc"; - QTest::newRow("multiple apks") + QTest::newRow("multiple apks aapt2") + << "multiple-apks-per-project" + << (QStringList() << "twolibs1" << "twolibs2") + << (QList<QByteArrayList>() << expectedFiles1(generateAab) + << (QByteArrayList() << expectedFiles2(generateAab) << "resources.arsc")) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; + generateAab = true; + QTest::newRow("multiple apks aab") << "multiple-apks-per-project" << (QStringList() << "twolibs1" << "twolibs2") - << QList<QByteArrayList>{expectedFiles1, expectedFiles2} - << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + << (QList<QByteArrayList>() << expectedFiles1(generateAab) + << (QByteArrayList() << expectedFiles2(generateAab) << "base/resources.pb" + << "base/native.pb")) + << QStringList{aaptVersion(enableAapt2), packageType(generateAab)} + << enableAapt2 << generateAab << isIncrementalBuild; } QTEST_MAIN(TestBlackboxAndroid) |