aboutsummaryrefslogtreecommitdiffstats
path: root/share/qbs/modules/Android
diff options
context:
space:
mode:
authorRaphaël Cotty <raphael.cotty@gmail.com>2020-05-20 11:48:46 +0200
committerRaphaël Cotty <raphael.cotty@gmail.com>2020-06-04 07:55:24 +0000
commit3e2b35f2f3d8c1e9ba3d8248033dcdcc90cf80fe (patch)
treeb881b8339ddc18fba81d37d793fa41810dfdb89a /share/qbs/modules/Android
parent5c0b5b590e83dd1fe0b8b381bb2c4b9e8e16d816 (diff)
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 <denis.shienkov@gmail.com> Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Diffstat (limited to 'share/qbs/modules/Android')
-rw-r--r--share/qbs/modules/Android/sdk/sdk.qbs75
-rw-r--r--share/qbs/modules/Android/sdk/utils.js149
2 files changed, 221 insertions, 3 deletions
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"]
@@ -334,6 +343,51 @@ Module {
}
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",