aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/config/macros.qdocconf1
-rw-r--r--doc/reference/modules/codesign-module.qdoc65
-rwxr-xr-xscripts/test-qt-for-android.sh1
-rw-r--r--share/qbs/modules/Android/sdk/sdk.qbs61
-rw-r--r--share/qbs/modules/Android/sdk/utils.js64
-rw-r--r--share/qbs/modules/codesign/android.qbs116
-rw-r--r--share/qbs/modules/codesign/codesign.js72
-rw-r--r--tests/auto/blackbox/testdata-android/qt-app/test.keystorebin0 -> 1284 bytes
-rw-r--r--tests/auto/blackbox/tst_blackboxandroid.cpp108
9 files changed, 370 insertions, 118 deletions
diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf
index a8abe25e5..6811215c0 100644
--- a/doc/config/macros.qdocconf
+++ b/doc/config/macros.qdocconf
@@ -4,6 +4,7 @@ macro.qbsversion = $QBS_VERSION
macro.defaultvalue = "Default:"
macro.nodefaultvalue = "Default: Undefined"
macro.appleproperty = "This property is specific to Apple platforms."
+macro.androidproperty = "This property is specific to Android platforms."
macro.unixproperty = "This property is specific to Unix platforms."
macro.windowsproperty = "This property is specific to Windows."
macro.baremetalproperty = "This property is specific to bare-metal platforms."
diff --git a/doc/reference/modules/codesign-module.qdoc b/doc/reference/modules/codesign-module.qdoc
index ab95798a7..70a37477b 100644
--- a/doc/reference/modules/codesign-module.qdoc
+++ b/doc/reference/modules/codesign-module.qdoc
@@ -33,7 +33,8 @@
\brief Provides code signing support.
- The \c codesign module contains properties and rules for code signing on Apple platforms.
+ The \c codesign module contains properties and rules for code signing on Apple and Android
+ platforms.
\section2 Relevant File Tags
@@ -244,3 +245,65 @@
\appleproperty
*/
+
+/*!
+ \qmlproperty bool codesign::useApksigner
+
+ If \c true, the package is signed using apksignerFilePath binary.
+ Set this property to \c false to use the jarsignerFilePath one.
+ Set by the Android.sdk module.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c true
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keystorePath
+
+ The absolute path to the keystore file.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "${HOME}/.android/debug.keystore"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keystorePassword
+
+ The keystore password.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "android"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keyPassword
+
+ The key password.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "android"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keyAlias
+
+ The key alias.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "androiddebugkey"
+
+ \androidproperty
+*/
diff --git a/scripts/test-qt-for-android.sh b/scripts/test-qt-for-android.sh
index 4d476624b..55ef991d4 100755
--- a/scripts/test-qt-for-android.sh
+++ b/scripts/test-qt-for-android.sh
@@ -62,6 +62,7 @@ qbs setup-android --ndk-dir ${ANDROID_HOME}/ndk-bundle --sdk-dir ${ANDROID_HOME}
export QBS_AUTOTEST_PROFILE=qbs_autotests-android
export QBS_AUTOTEST_ALWAYS_LOG_STDERR=true
+export QTEST_FUNCTION_TIMEOUT=9000000
if [ ! "${QT_VERSION}" \< "5.14.0" ] && [ "${QT_VERSION}" \< "6.0.0" ]; then
echo "Using multi-arch data sets for qml tests (only for qt version >= 5.14 and < 6.0.0) with all architectures"
diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs
index 0e40d059b..cae5fc2e5 100644
--- a/share/qbs/modules/Android/sdk/sdk.qbs
+++ b/share/qbs/modules/Android/sdk/sdk.qbs
@@ -178,20 +178,18 @@ Module {
property path compiledResourcesDir: FileInfo.joinPaths(product.buildDirectory,
"compiled_resources")
property string packageContentsDir: FileInfo.joinPaths(product.buildDirectory, packageType)
- property string debugKeyStorePath: FileInfo.joinPaths(
- Environment.getEnv(qbs.hostOS.contains("windows")
- ? "USERPROFILE" : "HOME"),
- ".android", "debug.keystore")
- property bool useApksigner: buildToolsVersion && Utilities.versionCompare(
- buildToolsVersion, "24.0.3") >= 0
property stringList aidlSearchPaths
Depends { name: "java"; condition: _enableRules }
+ Depends { name: "codesign"; condition: _enableRules }
Properties {
condition: _enableRules
java.languageVersion: platformJavaVersion
java.runtimeVersion: platformJavaVersion
java.bootClassPaths: androidJarFilePath
+ codesign.apksignerFilePath: Android.sdk.apksignerFilePath
+ codesign._packageName: Android.sdk.apkBaseName + (_generateAab ? ".aab" : ".apk")
+ codesign.useApksigner: !_generateAab
}
validate: {
@@ -211,6 +209,11 @@ Module {
+ "Android bundletool can be downloaded from "
+ "https://github.com/google/bundletool");
}
+ if (Utilities.versionCompare(buildToolsVersion, "24.0.3") < 0) {
+ throw ModUtils.ModuleError("Version of Android SDK build tools too old. This version "
+ + "is " + buildToolsVersion + " and minimum version is "
+ + "24.0.3. Please update the Android SDK.")
+ }
}
FileTagger {
@@ -223,36 +226,6 @@ Module {
fileTags: ["android.aidl"]
}
- FileTagger {
- patterns: ["*.keystore"]
- fileTags: ["android.keystore"]
- }
-
- // Typically there is a debug keystore in ~/.android/debug.keystore which gets created
- // by the native build tools the first time a build is done. However, we don't want to create it
- // ourselves, because writing to a location outside the qbs build directory is both polluting
- // and has the potential for race conditions. So we'll instruct the user what to do.
- Group {
- name: "Android debug keystore"
- files: {
- if (!File.exists(Android.sdk.debugKeyStorePath)) {
- throw ModUtils.ModuleError("Could not find an Android debug keystore at " +
- Android.sdk.debugKeyStorePath + ". " +
- "If you are developing for Android on this machine for the first time and " +
- "have never built an application using the native Gradle / Android Studio " +
- "tooling, this is normal. You must create the debug keystore now using the " +
- "following command, in order to continue:\n\n" +
- SdkUtils.createDebugKeyStoreCommandString(java.keytoolFilePath,
- Android.sdk.debugKeyStorePath) +
- "\n\n" +
- "See the following URL for more information: " +
- "https://developer.android.com/studio/publish/app-signing.html#debug-mode");
- }
- return [Android.sdk.debugKeyStorePath];
- }
- fileTags: ["android.keystore"]
- }
-
Parameter {
property bool embedJar: true
}
@@ -525,11 +498,11 @@ Module {
inputs: [
"android.resources", "android.assets", "android.manifest_final",
"android.dex", "android.stl_deployed",
- "android.nativelibrary_deployed", "android.keystore"
+ "android.nativelibrary_deployed"
]
Artifact {
- filePath: product.Android.sdk.apkBaseName + ".apk"
- fileTags: "android.package"
+ filePath: product.Android.sdk.apkBaseName + ".apk_unsigned"
+ fileTags: "android.package_unsigned"
}
prepare: SdkUtils.prepareAaptPackage.apply(SdkUtils, arguments)
}
@@ -540,11 +513,11 @@ Module {
inputs: [
"android.apk_resources", "android.manifest_final",
"android.dex", "android.stl_deployed",
- "android.nativelibrary_deployed", "android.keystore"
+ "android.nativelibrary_deployed"
]
Artifact {
- filePath: product.Android.sdk.apkBaseName + ".apk"
- fileTags: "android.package"
+ filePath: product.Android.sdk.apkBaseName + ".apk_unsigned"
+ fileTags: "android.package_unsigned"
}
prepare: SdkUtils.prepareApkPackage.apply(SdkUtils, arguments)
}
@@ -558,8 +531,8 @@ Module {
"android.nativelibrary_deployed"
]
Artifact {
- filePath: product.Android.sdk.apkBaseName + ".aab"
- fileTags: "android.package"
+ filePath: product.Android.sdk.apkBaseName + ".aab_unsigned"
+ fileTags: "android.package_unsigned"
}
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 6fa200822..9511ae9de 100644
--- a/share/qbs/modules/Android/sdk/utils.js
+++ b/share/qbs/modules/Android/sdk/utils.js
@@ -242,7 +242,7 @@ function prepareAaptGenerate(project, product, inputs, outputs, input, output,
function prepareAaptPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
var cmds = [];
- var apkOutput = outputs["android.package"][0];
+ var apkOutput = outputs["android.package_unsigned"][0];
var args = commonAaptPackageArgs.apply(this, arguments);
args.push("-F", apkOutput.filePath + ".unaligned");
args.push(product.Android.sdk.packageContentsDir);
@@ -250,17 +250,6 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex
cmd.description = "generating " + apkOutput.fileName;
cmds.push(cmd);
- if (!product.Android.sdk.useApksigner) {
- args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1",
- "-keystore", inputs["android.keystore"][0].filePath,
- "-storepass", "android",
- apkOutput.filePath + ".unaligned",
- "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", apkOutput.filePath + ".unaligned", apkOutput.filePath]);
cmd.silent = true;
@@ -271,26 +260,14 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex
cmd.unalignedApk = apkOutput.filePath + ".unaligned";
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 prepareApkPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
var cmds = [];
var apkInputFilePath = inputs["android.apk_resources"][0].filePath;
- var apkOutput = outputs["android.package"][0];
- var apkOutputFilePathUnaligned = outputs["android.package"][0].filePath + ".unaligned";
+ var apkOutput = outputs["android.package_unsigned"][0];
+ var apkOutputFilePathUnaligned = apkOutput.filePath + ".unaligned";
var dexFilePath = inputs["android.dex"][0].filePath;
var copyCmd = new JavaScriptCommand();
@@ -309,17 +286,6 @@ function prepareApkPackage(project, product, inputs, outputs, input, output, exp
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;
@@ -330,18 +296,6 @@ function prepareApkPackage(project, product, inputs, outputs, input, output, exp
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;
}
@@ -377,7 +331,7 @@ function prepareBundletoolPackage(project, product, inputs, outputs, input, outp
jarBaseCmd.workingDirectory = baseModuleDir;
cmds.push(jarBaseCmd);
- var aabFilePath = outputs["android.package"][0].filePath;
+ var aabFilePath = outputs["android.package_unsigned"][0].filePath;
var removeCmd = new JavaScriptCommand();
removeCmd.description = "removing previous aab";
removeCmd.filePath = aabFilePath;
@@ -391,20 +345,12 @@ function prepareBundletoolPackage(project, product, inputs, outputs, input, outp
args.push("--modules=" + baseFilePath);
args.push("--output=" + aabFilePath);
var cmd = new Command(product.java.interpreterFilePath, args);
- cmd.description = "generating " + outputs["android.package"][0].fileName;
+ cmd.description = "generating " + aabFilePath.fileName;
cmds.push(cmd);
return cmds;
}
-function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath) {
- var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", "androiddebugkey",
- "-storepass", "android", "-keypass", "android", "-keyalg", "RSA",
- "-keysize", "2048", "-validity", "10000", "-dname",
- "CN=Android Debug,O=Android,C=US"];
- return Process.shellQuote(keytoolFilePath, args);
-}
-
function stlDeploymentData(product, inputs, type)
{
var data = { uniqueInputs: [], outputFilePaths: []};
diff --git a/share/qbs/modules/codesign/android.qbs b/share/qbs/modules/codesign/android.qbs
new file mode 100644
index 000000000..be96d42de
--- /dev/null
+++ b/share/qbs/modules/codesign/android.qbs
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Raphaƫl Cotty <raphael.cotty@gmail.com>
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of the Qbs.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import qbs
+import qbs.Environment
+import qbs.File
+import qbs.FileInfo
+import qbs.ModUtils
+import qbs.Probes
+import "codesign.js" as CodeSign
+
+CodeSignModule {
+ condition: qbs.targetOS.contains("android")
+ priority: 1
+ enableCodeSigning: true
+
+ property bool useApksigner: true
+ property path apksignerFilePath
+
+ Probes.JdkProbe {
+ id: jdk
+ environmentPaths: (jdkPath ? [jdkPath] : []).concat(base)
+ }
+ property string jdkPath: jdk.path
+ property string jarsignerFilePath: FileInfo.joinPaths(jdkPath, "bin", jarsignerName)
+ property string jarsignerName: "jarsigner"
+ property string keytoolFilePath: FileInfo.joinPaths(jdkPath, "bin", keytoolName)
+ property string keytoolName: "keytool"
+
+ property string debugKeystorePath: FileInfo.joinPaths(
+ Environment.getEnv(qbs.hostOS.contains("windows")
+ ? "USERPROFILE" : "HOME"),
+ ".android", "debug.keystore")
+ readonly property string debugKeystorePassword: "android"
+ readonly property string debugPassword: "android"
+ readonly property string debugKeyAlias: "androiddebugkey"
+
+ property string keystorePath: debugKeystorePath
+ property string keystorePassword: debugKeystorePassword
+ property string keyPassword: debugPassword
+ property string keyAlias: debugKeyAlias
+
+ // Private property set by the Android.sdk module
+ property string _packageName
+
+ Rule {
+ condition: useApksigner
+ inputs: ["android.package_unsigned"]
+ Artifact {
+ filePath: product.codesign._packageName
+ fileTags: "android.package"
+ }
+ prepare: CodeSign.signApkPackage.apply(this, arguments)
+ }
+
+ Rule {
+ condition: !useApksigner
+ inputs: ["android.package_unsigned"]
+ Artifact {
+ filePath: product.codesign._packageName
+ fileTags: "android.package"
+ }
+ prepare: CodeSign.signAabPackage.apply(this, arguments)
+ }
+
+ validate: {
+ // Typically there is a debug keystore in ~/.android/debug.keystore which gets created
+ // by the native build tools the first time a build is done. However, we don't want to
+ // create it ourselves, because writing to a location outside the qbs build directory is
+ // both polluting and has the potential for race conditions. So we'll instruct the user what
+ // to do.
+ if (keystorePath === debugKeystorePath && !File.exists(debugKeystorePath)) {
+ throw ModUtils.ModuleError("Could not find an Android debug keystore at " +
+ codesign.debugKeystorePath + ". " +
+ "If you are developing for Android on this machine for the first time and " +
+ "have never built an application using the native Gradle / Android Studio " +
+ "tooling, this is normal. You must create the debug keystore now using the " +
+ "following command, in order to continue:\n\n" +
+ CodeSign.createDebugKeyStoreCommandString(codesign.keytoolFilePath,
+ codesign.debugKeystorePath,
+ codesign.debugKeystorePassword,
+ codesign.debugPassword,
+ codesign.debugKeyAlias) +
+ "\n\n" +
+ "See the following URL for more information: " +
+ "https://developer.android.com/studio/publish/app-signing.html#debug-mode");
+ }
+ }
+}
diff --git a/share/qbs/modules/codesign/codesign.js b/share/qbs/modules/codesign/codesign.js
index 1d039e7ca..bf7e95224 100644
--- a/share/qbs/modules/codesign/codesign.js
+++ b/share/qbs/modules/codesign/codesign.js
@@ -32,6 +32,7 @@
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
var PathTools = require("qbs.PathTools");
+var Process = require("qbs.Process");
var PropertyList = require("qbs.PropertyList");
var Utilities = require("qbs.Utilities");
@@ -277,3 +278,74 @@ function prepareSign(project, product, inputs, outputs, input, output) {
return cmds;
}
+
+function signApkPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var apkInput = inputs["android.package_unsigned"][0];
+ var apkOutput = outputs["android.package"][0];
+ var cmd;
+ if (product.codesign.enableCodeSigning) {
+ var args = ["sign",
+ "--ks", product.codesign.keystorePath,
+ "--ks-pass", "pass:" + product.codesign.keystorePassword,
+ "--ks-key-alias", product.codesign.keyAlias,
+ "--key-pass", "pass:" + product.codesign.keyPassword,
+ "--out", apkOutput.filePath,
+ apkInput.filePath];
+ cmd = new Command(product.codesign.apksignerFilePath, args);
+ cmd.description = "signing " + apkOutput.fileName;
+ } else {
+ cmd = new JavaScriptCommand();
+ cmd.description = "copying without signing " + apkOutput.fileName;
+ cmd.source = apkInput.filePath;
+ cmd.target = apkOutput.filePath;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ // If enableCodeSigning is changed to false without any change to unsigned package then
+ // the copy won't happen because of timestamps. So the target file needs file needs to
+ // be removed to avoid it.
+ File.remove(target);
+ File.copy(source, target);
+ }
+ }
+ return cmd;
+}
+
+function signAabPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var aabInput = inputs["android.package_unsigned"][0];
+ var aabOutput = outputs["android.package"][0];
+ var cmd;
+ if (product.codesign.enableCodeSigning) {
+ args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1",
+ "-keystore", product.codesign.keystorePath,
+ "-storepass", product.codesign.keystorePassword,
+ "-keypass", product.codesign.keyPassword,
+ "-signedjar", aabOutput.filePath,
+ aabInput.filePath,
+ product.codesign.keyAlias];
+ cmd = new Command(product.codesign.jarsignerFilePath, args);
+ cmd.description = "signing " + aabOutput.fileName;
+ } else {
+ cmd = new JavaScriptCommand();
+ cmd.description = "copying without signing " + aabOutput.fileName;
+ cmd.source = aabInput.filePath;
+ cmd.target = aabOutput.filePath;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ // If enableCodeSigning is changed to false without any change to unsigned package then
+ // the copy won't happen because of timestamps. So the target file needs file needs to
+ // be removed to avoid it.
+ File.remove(target);
+ File.copy(source, target);
+ }
+ }
+ return cmd;
+}
+
+function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath, keystorePassword,
+ keyPassword, keyAlias) {
+ var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", keyAlias,
+ "-storepass", keystorePassword, "-keypass", keyPassword, "-keyalg", "RSA",
+ "-keysize", "2048", "-validity", "10000", "-dname",
+ "CN=Android Debug,O=Android,C=US"];
+ return Process.shellQuote(keytoolFilePath, args);
+}
diff --git a/tests/auto/blackbox/testdata-android/qt-app/test.keystore b/tests/auto/blackbox/testdata-android/qt-app/test.keystore
new file mode 100644
index 000000000..5713d10d2
--- /dev/null
+++ b/tests/auto/blackbox/testdata-android/qt-app/test.keystore
Binary files differ
diff --git a/tests/auto/blackbox/tst_blackboxandroid.cpp b/tests/auto/blackbox/tst_blackboxandroid.cpp
index a03c9e318..f8ed8a0b9 100644
--- a/tests/auto/blackbox/tst_blackboxandroid.cpp
+++ b/tests/auto/blackbox/tst_blackboxandroid.cpp
@@ -268,14 +268,19 @@ void TestBlackboxAndroid::android_data()
return result;
};
- auto commonFiles = [](bool generateAab) {
+ auto commonFiles = [](bool generateAab, bool codeSign = true,
+ QString keyAlias="androiddebugkey") {
+ QByteArrayList files;
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");
+ files << "base/manifest/AndroidManifest.xml" << "base/dex/classes.dex"
+ << "BundleConfig.pb";
+ else
+ files << "AndroidManifest.xml" << "classes.dex";
+ if (codeSign)
+ files << QByteArray("META-INF/" + keyAlias.toUpper().left(8).toUtf8() + ".RSA")
+ << QByteArray("META-INF/" + keyAlias.toUpper().left(8).toUtf8() + ".SF")
+ << "META-INF/MANIFEST.MF";
+ return files;
};
QTest::addColumn<QString>("projectDir");
@@ -296,10 +301,11 @@ void TestBlackboxAndroid::android_data()
bool generateAab = false;
bool isIncrementalBuild = false;
- auto qtAppExpectedFiles = [&](bool generateAab, bool enableAapt2) {
+ auto qtAppExpectedFiles = [&](bool generateAab, bool enableAapt2, bool codeSign = true,
+ QString keyAlias="androiddebugkey") {
QByteArrayList expectedFile;
if (singleArchQt) {
- expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, {
+ expectedFile << commonFiles(generateAab, codeSign, keyAlias) + expandArchs(ndkArchsForQt, {
cxxLibPath("libgnustl_shared.so", true),
"assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
"lib/${ARCH}/libplugins_imageformats_libqgif.so",
@@ -317,7 +323,7 @@ void TestBlackboxAndroid::android_data()
"lib/${ARCH}/libQt5Widgets.so",
"lib/${ARCH}/libqt-app.so"}, generateAab);
} else {
- expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, {
+ expectedFile << commonFiles(generateAab, codeSign, keyAlias) + expandArchs(ndkArchsForQt, {
cxxLibPath("libgnustl_shared.so", true),
"lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so",
"lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so",
@@ -329,7 +335,7 @@ void TestBlackboxAndroid::android_data()
"lib/${ARCH}/libqt-app_${ARCH}.so"}, generateAab);
}
if (generateAab)
- expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb";
+ expectedFile << "base/resources.pb" << "base/native.pb";
else
expectedFile << "resources.arsc";
if (version >= qbs::Version(5, 14))
@@ -346,11 +352,82 @@ void TestBlackboxAndroid::android_data()
expectedFile << "res/layout/splash.xml";
return expectedFile;
};
+ auto codeSignProperties = [&](bool codeSign, QString keyStorePath, QString keystorePassword,
+ QString keyPassword, QString keyAlias) {
+ if (!codeSign)
+ return QStringList{"modules.codesign.enableCodeSigning:false"};
+ return QStringList{
+ "modules.codesign.enableCodeSigning:true",
+ "modules.codesign.keystorePath:" + keyStorePath,
+ "modules.codesign.keystorePassword:" + keystorePassword,
+ "modules.codesign.keyPassword:" + keyPassword,
+ "modules.codesign.keyAlias:" + keyAlias,
+ };
+ };
+ bool codeSign = true;
+ QString keyStorePath(testDataDir + "/qt-app/test.keystore");
+ QString keystorePassword("qbsKeystoreTest");
+ QString keyPassword("qbsKeyTest");
+ QString keyAlias("qbsTest");
QTest::newRow("qt app")
<< "qt-app" << QStringList("qt-app")
<< (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
- enableAapt2)))
- << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild;
+ codeSign = false;
+ QTest::newRow("qt app no signing")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild;
+ enableAapt2 = true;
+ codeSign = true;
+ QTest::newRow("qt app aapt2")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild;
+ generateAab = true;
+ QTest::newRow("qt app aab")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild;
+ codeSign = false;
+ QTest::newRow("qt app aab no signing")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
<< enableAapt2 << generateAab << isIncrementalBuild;
const QByteArrayList ndkArchsForQtSave = ndkArchsForQt;
@@ -383,6 +460,8 @@ void TestBlackboxAndroid::android_data()
return expectedFile;
};
+ generateAab = false;
+ enableAapt2 = false;
QTest::newRow("teapot")
<< "teapot" << QStringList("TeapotNativeActivity")
<< (QList<QByteArrayList>() << teaPotAppExpectedFiles(archs, generateAab))
@@ -437,6 +516,7 @@ void TestBlackboxAndroid::android_data()
"modules.qbs.architecture:" + archsStringList.first(),
aaptVersion(enableAapt2), packageType(generateAab)}
<< enableAapt2 << generateAab << isIncrementalBuild;
+
auto qmlAppExpectedFiles = [&](bool generateAab, bool enableAapt2) {
QByteArrayList expectedFile;
if (singleArchQt) {
@@ -730,7 +810,6 @@ void TestBlackboxAndroid::android_data()
} 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
@@ -851,6 +930,7 @@ void TestBlackboxAndroid::android_data()
expectedFile << "resources.arsc";
return expectedFile;
};
+
QTest::newRow("no native")
<< "no-native"
<< QStringList("com.example.android.basicmediadecoder")