diff options
author | Raphaël Cotty <raphael.cotty@gmail.com> | 2020-05-20 11:48:46 +0200 |
---|---|---|
committer | Raphaël Cotty <raphael.cotty@gmail.com> | 2020-06-04 07:55:24 +0000 |
commit | 3e2b35f2f3d8c1e9ba3d8248033dcdcc90cf80fe (patch) | |
tree | b881b8339ddc18fba81d37d793fa41810dfdb89a /share/qbs/modules/Android | |
parent | 5c0b5b590e83dd1fe0b8b381bb2c4b9e8e16d816 (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.qbs | 75 | ||||
-rw-r--r-- | share/qbs/modules/Android/sdk/utils.js | 149 |
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", |