From 3e2b35f2f3d8c1e9ba3d8248033dcdcc90cf80fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cotty?= Date: Wed, 20 May 2020 11:48:46 +0200 Subject: Android: Add option to use aapt2 instead of aapt in Android.sdk module aapt has been deprecated (https://developer.android.com/studio/command-line/aapt2) and replaced by aapt2. PropertyOption aaptFileName allows those value: "aapt" and "aapt2". Default value is set to "aapt" for the moment. Fixes: QBS-1562 Change-Id: I1970cafaa54a0e035c08ffe5f9967b712f5c5253 Reviewed-by: Denis Shienkov Reviewed-by: Christian Kandeler --- doc/reference/modules/android-sdk-module.qdoc | 8 ++ share/qbs/modules/Android/sdk/sdk.qbs | 75 ++++++++++- share/qbs/modules/Android/sdk/utils.js | 149 ++++++++++++++++++++++ tests/auto/blackbox/tst_blackboxandroid.cpp | 175 +++++++++++++++++++++----- 4 files changed, 374 insertions(+), 33 deletions(-) diff --git a/doc/reference/modules/android-sdk-module.qdoc b/doc/reference/modules/android-sdk-module.qdoc index 2c2e43eab..74599d0ba 100644 --- a/doc/reference/modules/android-sdk-module.qdoc +++ b/doc/reference/modules/android-sdk-module.qdoc @@ -233,3 +233,11 @@ \qmlproperty stringList Android.sdk::aidlSearchPaths Search paths for import statements to pass to the \c aidl tool via the \c{-I} option. */ + +/*! + \qmlproperty string Android.sdk::aaptName + + Name of the aapt binary. Allowed options: "aapt" and "aapt2". + + \defaultvalue \c "aapt" +*/ diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs index 84492a869..681b3da94 100644 --- a/share/qbs/modules/Android/sdk/sdk.qbs +++ b/share/qbs/modules/Android/sdk/sdk.qbs @@ -139,7 +139,14 @@ Module { } property path buildToolsDir: FileInfo.joinPaths(sdkDir, "build-tools", buildToolsVersion) - property path aaptFilePath: FileInfo.joinPaths(buildToolsDir, "aapt") + property string aaptName: "aapt" + PropertyOptions { + name: "aaptName" + allowedValues: ["aapt", "aapt2"] + } + property path aaptFilePath: FileInfo.joinPaths(buildToolsDir, aaptName) + readonly property bool _enableAapt2: aaptName === "aapt2" + property path apksignerFilePath: FileInfo.joinPaths(buildToolsDir, "apksigner") property path aidlFilePath: FileInfo.joinPaths(buildToolsDir, "aidl") property path dxFilePath: FileInfo.joinPaths(buildToolsDir, "dx") @@ -151,6 +158,8 @@ Module { property path generatedJavaFilesBaseDir: FileInfo.joinPaths(product.buildDirectory, "gen") property path generatedJavaFilesDir: FileInfo.joinPaths(generatedJavaFilesBaseDir, (packageName || "").split('.').join('/')) + property path compiledResourcesDir: FileInfo.joinPaths(product.buildDirectory, + "compiled_resources") property string apkContentsDir: FileInfo.joinPaths(product.buildDirectory, "bin") property string debugKeyStorePath: FileInfo.joinPaths( Environment.getEnv(qbs.hostOS.contains("windows") @@ -311,7 +320,7 @@ Module { } Rule { - condition: _enableRules + condition: _enableRules && !_enableAapt2 multiplex: true inputs: ["android.resources", "android.assets", "android.manifest_final"] @@ -333,6 +342,51 @@ Module { prepare: SdkUtils.prepareAaptGenerate.apply(SdkUtils, arguments) } + Rule { + condition: _enableRules && _enableAapt2 + inputs: ["android.resources"] + outputArtifacts: { + var outputs = []; + var resources = inputs["android.resources"]; + for (var i = 0; i < resources.length; ++i) { + var filePath = resources[i].filePath; + var resourceFileName = SdkUtils.generateAapt2ResourceFileName(filePath); + var compiledName = FileInfo.joinPaths(product.Android.sdk.compiledResourcesDir, + resourceFileName); + outputs.push({filePath: compiledName, fileTags: "android.resources_compiled"}); + } + return outputs; + } + outputFileTags: ["android.resources_compiled"] + + prepare: SdkUtils.prepareAapt2CompileResource.apply(SdkUtils, arguments) + } + + Rule { + condition: _enableRules && _enableAapt2 + multiplex: true + inputs: ["android.resources_compiled", "android.assets", "android.manifest_final"] + outputFileTags: ["java.java", "android.apk_base"] + outputArtifacts: { + var artifacts = []; + artifacts.push({ + filePath: product.Android.sdk.apkBaseName + ".apk_base", + fileTags: ["android.apk_base"] + }); + var resources = inputs["android.resources_compiled"]; + if (resources && resources.length) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.Android.sdk.generatedJavaFilesDir, + "R.java"), + fileTags: ["java.java"] + }); + } + + return artifacts; + } + prepare: SdkUtils.prepareAapt2Link.apply(SdkUtils, arguments) + } + Rule { condition: _enableRules multiplex: true @@ -421,7 +475,7 @@ Module { } Rule { - condition: _enableRules + condition: _enableRules && !_enableAapt2 multiplex: true inputs: [ "android.resources", "android.assets", "android.manifest_final", @@ -434,4 +488,19 @@ Module { } prepare: SdkUtils.prepareAaptPackage.apply(SdkUtils, arguments) } + + Rule { + condition: _enableRules && _enableAapt2 + multiplex: true + inputs: [ + "android.apk_base", "android.manifest_final", + "android.dex", "android.stl_deployed", + "android.nativelibrary_deployed", "android.keystore" + ] + Artifact { + filePath: product.Android.sdk.apkBaseName + ".apk" + fileTags: "android.apk" + } + prepare: SdkUtils.prepareApkPackage.apply(SdkUtils, arguments) + } } diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js index 4ee9bd3b5..b1c929e68 100644 --- a/share/qbs/modules/Android/sdk/utils.js +++ b/share/qbs/modules/Android/sdk/utils.js @@ -136,6 +136,96 @@ function commonAaptPackageArgs(project, product, inputs, outputs, input, output, return args; } +// Rules: from https://developer.android.com/studio/command-line/aapt2 +// Input Output +// XML resource files, such as Resource table with *.arsc.flat as its extension. +// String and Style, which are +// located in the res/values/ +// directory. + +// All other resource files. All files other than the files under res/values/ directory are +// converted to binary XML files with *.flat extensions. +// Additionally all PNG files are crunched by default and adopt +// *.png.flat extensions. +function generateAapt2ResourceFileName(filePath) { + var suffix = FileInfo.suffix(filePath); + if (suffix === "xml") { + var data = new Xml.DomDocument(); + data.load(filePath); + var rootElem = data.documentElement(); + if (rootElem && rootElem.isElement() && rootElem.tagName() === "resources") + // This is a valid XML resource file + suffix = "arsc"; + // If the xml file is not a "resources" one then it's treated like any other resource file. + } + var dir = FileInfo.path(filePath); + var baseName = FileInfo.completeBaseName(filePath) + return FileInfo.fileName(dir) + "_" + baseName + "." + suffix + ".flat"; +} + +function prepareAapt2CompileResource(project, product, inputs, outputs, input, output, + explicitlyDependsOn) { + var cmds = []; + var resources = inputs["android.resources"]; + var compilesResourcesDir = product.Android.sdk.compiledResourcesDir; + if (!File.makePath(compilesResourcesDir)) { + throw "Cannot create directory '" + FileInfo.toNativeSeparators(compilesResourcesDir) + + "'."; + } + var args = ["compile", input.filePath, "-o", compilesResourcesDir]; + var cmd = new Command(product.Android.sdk.aaptFilePath, args); + var outputFileName = generateAapt2ResourceFileName(input.filePath); + cmd.description = "compiling resource " + input.fileName + " into " + outputFileName; + cmds.push(cmd); + + return cmds; +} + +function prepareAapt2Link(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; + 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 i = 0; + if (compiledResources) { + for (i = 0; i < compiledResources.length; ++i) + args.push(compiledResources[i].filePath); + } + args.push("--no-auto-version"); + args.push("--auto-add-overlay"); + args.push("--manifest", manifestFilePath); + args.push("--java", product.Android.sdk.generatedJavaFilesBaseDir); + + var assets = inputs["android.assets"]; + var assetDirs = []; + if (assets) { + for (i = 0; i < assets.length; ++i) { + var assetDir = findParentDir(assets[i].filePath, "assets"); + if (!assetDir) { + throw "File '" + assets[i].filePath + "' is tagged as an Android asset, " + + "but is not located under a directory called 'assets'."; + } + if (!assetDirs.contains(assetDir)) + assetDirs.push(assetDir); + } + } + for (i = 0; i < assetDirs.length; ++i) + args.push("-A", assetDirs[i]); + if (product.qbs.buildVariant === "debug") + args.push("-v"); + + var cmd = new Command(product.Android.sdk.aaptFilePath, args); + cmd.description = "Linking resources"; + cmd.workingDirectory = product.buildDirectory; + cmds.push(cmd); + + return cmds; +} + function prepareAaptGenerate(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var args = commonAaptPackageArgs.apply(this, arguments); @@ -194,6 +284,65 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex return cmds; } +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 dexFilePath = inputs["android.dex"][0].filePath; + + var copyCmd = new JavaScriptCommand(); + copyCmd.description = "copying apk"; + copyCmd.source = apkInputFilePath; + copyCmd.target = apkOutputFilePathUnaligned; + copyCmd.sourceCode = function() { + File.copy(source, target); + } + cmds.push(copyCmd); + + var jarArgs = ["-uvf", apkOutputFilePathUnaligned, "."]; + var libPath = FileInfo.joinPaths(product.Android.sdk.apkContentsDir); + var jarCmd = new Command(product.java.jarFilePath, jarArgs); + jarCmd.description = "packaging files"; + jarCmd.workingDirectory = libPath; + cmds.push(jarCmd); + + if (!product.Android.sdk.useApksigner) { + args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1", + "-keystore", inputs["android.keystore"][0].filePath, + "-storepass", "android", + apkOutputFilePathUnaligned, + "androiddebugkey"]; + cmd = new Command(product.java.jarsignerFilePath, args); + cmd.description = "Signing " + apkOutput.fileName; + cmds.push(cmd); + } + + cmd = new Command(product.Android.sdk.zipalignFilePath, + ["-f", "4", apkOutputFilePathUnaligned, apkOutput.filePath]); + cmd.silent = true; + cmds.push(cmd); + + cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.unalignedApk = apkOutputFilePathUnaligned; + cmd.sourceCode = function() { File.remove(unalignedApk); }; + cmds.push(cmd); + + if (product.Android.sdk.useApksigner) { + // TODO: Implement full signing support, not just using the debug keystore + args = ["sign", + "--ks", inputs["android.keystore"][0].filePath, + "--ks-pass", "pass:android", + apkOutput.filePath]; + cmd = new Command(product.Android.sdk.apksignerFilePath, args); + cmd.description = "Signing " + apkOutput.fileName; + cmds.push(cmd); + } + + return cmds; +} + function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath) { var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", "androiddebugkey", "-storepass", "android", "-keypass", "android", "-keyalg", "RSA", diff --git a/tests/auto/blackbox/tst_blackboxandroid.cpp b/tests/auto/blackbox/tst_blackboxandroid.cpp index 040a1a6e2..e8d44188c 100644 --- a/tests/auto/blackbox/tst_blackboxandroid.cpp +++ b/tests/auto/blackbox/tst_blackboxandroid.cpp @@ -80,6 +80,7 @@ void TestBlackboxAndroid::android() QFETCH(QStringList, productNames); QFETCH(QList, expectedFilesLists); QFETCH(QStringList, qmlAppCustomProperties); + QFETCH(bool, enableAapt2); const SettingsPtr s = settings(); Profile p(theProfileName(projectDir == "qml-app"), s.get()); @@ -103,6 +104,7 @@ void TestBlackboxAndroid::android() && ndkSamplesDirs.contains(projectDir)) QSKIP("NDK samples directory not present"); + const QString aaptVersion = enableAapt2 ? "aapt2" : "aapt"; QDir::setCurrent(testDataDir + "/" + projectDir); static const QStringList configNames { "debug", "release" }; @@ -110,11 +112,13 @@ void TestBlackboxAndroid::android() auto currentExpectedFilesLists = expectedFilesLists; const QString configArgument = "config:" + configName; QbsRunParameters resolveParams("resolve"); + resolveParams.buildDirectory = aaptVersion; 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.profile = p.name(); QCOMPARE(runQbs(buildParams), 0); for (const QString &productName : qAsConst(productNames)) { @@ -123,7 +127,8 @@ void TestBlackboxAndroid::android() QCOMPARE(m_qbsStdout.count("Generating BuildConfig.java"), isIncrementalBuild ? 0 : productNames.size()); QVERIFY(m_qbsStdout.contains(productName.toLocal8Bit() + ".apk")); - const QString apkFilePath = relativeProductBuildDir(productName, configName) + const QString apkFilePath = aaptVersion + "/" + relativeProductBuildDir(productName, + configName) + '/' + productName + ".apk"; QVERIFY2(regularFileExists(apkFilePath), qPrintable(apkFilePath)); const QString jarFilePath = androidPaths["jar"]; @@ -206,7 +211,6 @@ void TestBlackboxAndroid::android_data() .toString() == "clang"; return QByteArray("lib/${ARCH}/") + (usesClang ? "libc++_shared.so" : oldcxxLib); }; - bool usingOldQt = true; QStringList qmakeFilePaths = pQt.value(QStringLiteral("moduleProviders.Qt.qmakeFilePaths")). toStringList(); @@ -265,24 +269,56 @@ void TestBlackboxAndroid::android_data() QTest::addColumn("productNames"); QTest::addColumn>("expectedFilesLists"); QTest::addColumn("qmlAppCustomProperties"); + QTest::addColumn("enableAapt2"); + + 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"}); QTest::newRow("teapot") << "teapot" << QStringList("TeapotNativeActivity") - << (QList() << 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"})) - << QStringList(); + << (QList() << teaPotAppExpectedFiles) + << QStringList{aaptVersion(enableAapt2)} << enableAapt2; + enableAapt2 = true; + QTest::newRow("teapot") + << "teapot" << QStringList("TeapotNativeActivity") + << (QList() << teaPotAppExpectedFiles) + << QStringList{aaptVersion(enableAapt2)} << enableAapt2; + enableAapt2 = false; + QTest::newRow("minimal-native") + << "minimal-native" << QStringList("minimalnative") + << (QList() << commonFiles + expandArchs({archs.first()}, { + "lib/${ARCH}/libminimalnative.so", + cxxLibPath("libstlport_shared.so", false), + "lib/${ARCH}/libdependency.so"})) + << QStringList{"products.minimalnative.multiplexByQbsProperties:[]", + "modules.qbs.architecture:" + archsStringList.first(), + aaptVersion(enableAapt2)} + << enableAapt2; + enableAapt2 = true; QTest::newRow("minimal-native") << "minimal-native" << QStringList("minimalnative") << (QList() << commonFiles + expandArchs({archs.first()}, { + "resources.arsc", "lib/${ARCH}/libminimalnative.so", cxxLibPath("libstlport_shared.so", false), "lib/${ARCH}/libdependency.so"})) << QStringList{"products.minimalnative.multiplexByQbsProperties:[]", - "modules.qbs.architecture:" + archsStringList.first()}; + "modules.qbs.architecture:" + archsStringList.first(), + aaptVersion(enableAapt2)} + << enableAapt2; QByteArrayList qmlAppExpectedFiles; QByteArrayList qmlAppMinistroExpectedFiles; QByteArrayList qmlAppCustomMetaDataExpectedFiles; @@ -325,14 +361,12 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libQt5Qml.so", "lib/${ARCH}/libQt5QuickParticles.so", "lib/${ARCH}/libQt5Quick.so", - "lib/${ARCH}/libqmlapp.so", - "res/layout/splash.xml"}); + "lib/${ARCH}/libqmlapp.so"}); qmlAppMinistroExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { "resources.arsc", "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list", cxxLibPath("libgnustl_shared.so", true), - "lib/${ARCH}/libqmlapp.so", - "res/layout/splash.xml"}); + "lib/${ARCH}/libqmlapp.so"}); qmlAppCustomMetaDataExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { "resources.arsc", "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes", @@ -371,8 +405,7 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libQt5Qml.so", "lib/${ARCH}/libQt5QuickParticles.so", "lib/${ARCH}/libQt5Quick.so", - "lib/${ARCH}/libqmlapp.so", - "res/layout/splash.xml"}); + "lib/${ARCH}/libqmlapp.so"}); qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false", "modules.qbs.architecture:" + archsForQt.first()}; } else { @@ -411,14 +444,12 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libQt5Quick_${ARCH}.so", "lib/${ARCH}/libQt5QmlModels_${ARCH}.so", "lib/${ARCH}/libQt5QmlWorkerScript_${ARCH}.so", - "lib/${ARCH}/libqmlapp_${ARCH}.so", - "res/layout/splash.xml"}); + "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", - "res/layout/splash.xml"}); + "lib/${ARCH}/libqmlapp_${ARCH}.so"}); qmlAppCustomMetaDataExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { "resources.arsc", "assets/android_rcc_bundle.rcc", @@ -455,23 +486,57 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libQt5Quick_${ARCH}.so", "lib/${ARCH}/libQt5QmlModels_${ARCH}.so", "lib/${ARCH}/libQt5QmlWorkerScript_${ARCH}.so", - "lib/${ARCH}/libqmlapp_${ARCH}.so", - "res/layout/splash.xml"}); + "lib/${ARCH}/libqmlapp_${ARCH}.so"}); 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 + // declared. + enableAapt2 = false; + QTest::newRow("qml app") + << "qml-app" << QStringList("qmlapp") + << (QList() << (QByteArrayList() << qmlAppExpectedFiles + << "res/layout/splash.xml")) + << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2)) << enableAapt2; + enableAapt2 = true; QTest::newRow("qml app") << "qml-app" << QStringList("qmlapp") << (QList() << qmlAppExpectedFiles) - << qmlAppCustomProperties; + << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2)) << enableAapt2; + enableAapt2 = false; + QTest::newRow("qml app using Ministro") + << "qml-app" << QStringList("qmlapp") + << (QList() << (QByteArrayList() << qmlAppMinistroExpectedFiles + << "res/layout/splash.xml")) + << QStringList{"modules.Qt.android_support.useMinistro:true", + "modules.Android.sdk.automaticSources:false", + aaptVersion(enableAapt2)} << enableAapt2; + enableAapt2 = true; QTest::newRow("qml app using Ministro") << "qml-app" << QStringList("qmlapp") << (QList() << qmlAppMinistroExpectedFiles) << QStringList{"modules.Qt.android_support.useMinistro:true", - "modules.Android.sdk.automaticSources:false"}; + "modules.Android.sdk.automaticSources:false", + aaptVersion(enableAapt2)} << enableAapt2; + enableAapt2 = false; + QTest::newRow("qml app with custom metadata") + << "qml-app" << QStringList("qmlapp") + << (QList() << (QByteArrayList() << qmlAppCustomMetaDataExpectedFiles + << "res/layout/splash.xml")) + << QStringList{"modules.Android.sdk.automaticSources:true", + aaptVersion(enableAapt2)} << enableAapt2; + enableAapt2 = true; QTest::newRow("qml app with custom metadata") << "qml-app" << QStringList("qmlapp") << (QList() << qmlAppCustomMetaDataExpectedFiles) - << QStringList("modules.Android.sdk.automaticSources:true"); + << QStringList{"modules.Android.sdk.automaticSources:true", + aaptVersion(enableAapt2)} << enableAapt2; + enableAapt2 = false; QTest::newRow("no native") << "no-native" << QStringList("com.example.android.basicmediadecoder") @@ -493,9 +558,41 @@ void TestBlackboxAndroid::android_data() "res/menu/action_menu.xml", "res/menu-v11/action_menu.xml", "res/raw/vid_bigbuckbunny.mp4"})) - << QStringList(); + << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + enableAapt2 = true; + QTest::newRow("no native") + << "no-native" + << QStringList("com.example.android.basicmediadecoder") + << (QList() << 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; + enableAapt2 = false; QTest::newRow("aidl") << "aidl" << QStringList("io.qbs.aidltest") - << QList{commonFiles} << QStringList(); + << (QList() << (QByteArrayList() << commonFiles)) + << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + enableAapt2 = true; + QTest::newRow("aidl") << "aidl" << QStringList("io.qbs.aidltest") + << (QList() << (QByteArrayList() << commonFiles + << "resources.arsc")) + << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + enableAapt2 = false; QTest::newRow("multiple libs") << "multiple-libs-per-apk" << QStringList("twolibs") @@ -504,7 +601,17 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/liblib1.so", "lib/${ARCH}/liblib2.so", cxxLibPath("libstlport_shared.so", false)})) - << QStringList(); + << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + enableAapt2 = true; + QTest::newRow("multiple libs") + << "multiple-libs-per-apk" + << QStringList("twolibs") + << (QList() << commonFiles + 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", @@ -518,11 +625,19 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libp2lib1.so", "lib/${ARCH}/libp2lib2.so", cxxLibPath("libstlport_shared.so", false)}); + enableAapt2 = false; + QTest::newRow("multiple apks") + << "multiple-apks-per-project" + << (QStringList() << "twolibs1" << "twolibs2") + << QList{expectedFiles1, expectedFiles2} + << QStringList(aaptVersion(enableAapt2)) << enableAapt2; + enableAapt2 = true; + expectedFiles2 << "resources.arsc"; QTest::newRow("multiple apks") << "multiple-apks-per-project" << (QStringList() << "twolibs1" << "twolibs2") << QList{expectedFiles1, expectedFiles2} - << QStringList(); + << QStringList(aaptVersion(enableAapt2)) << enableAapt2; } QTEST_MAIN(TestBlackboxAndroid) -- cgit v1.2.3