From 30e3e301e047cb04513c28a88d6b3bd3a1afc2e8 Mon Sep 17 00:00:00 2001 From: Fredia Huya-Kouadio Date: Tue, 7 Mar 2023 17:12:00 -0800 Subject: [PATCH] Update the gradle build tasks to generate play store builds. Configure the gradle builds to sign and build the release version of the Godot Android Editor --- platform/android/SCsub | 5 +- platform/android/detect.py | 1 + platform/android/java/app/config.gradle | 2 +- platform/android/java/build.gradle | 80 ++++++++++------ platform/android/java/editor/build.gradle | 91 +++++++++++++------ .../editor/src/debug/res/values/strings.xml | 4 + platform/android/java/lib/build.gradle | 25 ++--- 7 files changed, 137 insertions(+), 71 deletions(-) create mode 100644 platform/android/java/editor/src/debug/res/values/strings.xml diff --git a/platform/android/SCsub b/platform/android/SCsub index 23d7f966734..c11a3031ede 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -54,7 +54,10 @@ if lib_arch_dir != "": if env["target"] == "release": lib_type_dir = "release" elif env["target"] == "release_debug": - lib_type_dir = "debug" + if env["tools"] and env["store_release"]: + lib_type_dir = "release" + else: + lib_type_dir = "debug" else: # debug lib_type_dir = "dev" diff --git a/platform/android/detect.py b/platform/android/detect.py index 5af6a981368..b575e9f162b 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -24,6 +24,7 @@ def get_opts(): ("ndk_platform", 'Target platform (android-, e.g. "android-19")', "android-19"), EnumVariable("android_arch", "Target architecture", "armv7", ("armv7", "arm64v8", "x86", "x86_64")), BoolVariable("android_neon", "Enable NEON support (armv7 only)", True), + BoolVariable("store_release", "Editor build for Google Play Store (for official builds only)", False), ] diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 1710887b2ff..657760a5367 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -132,7 +132,7 @@ ext.generateGodotLibraryVersion = { List requiredKeys -> String statusValue = map["status"] if (statusValue == null) { statusCode = 0 - } else if (statusValue.startsWith("alpha")) { + } else if (statusValue.startsWith("alpha") || statusValue.startsWith("dev")) { statusCode = 1 } else if (statusValue.startsWith("beta")) { statusCode = 2 diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index f05fc8212c6..3a23aa6bd90 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -9,7 +9,7 @@ buildscript { dependencies { classpath libraries.androidGradlePlugin classpath libraries.kotlinGradlePlugin - classpath 'io.github.gradle-nexus:publish-plugin:1.1.0' + classpath 'io.github.gradle-nexus:publish-plugin:1.3.0' } } @@ -36,8 +36,11 @@ allprojects { ext { supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"] - supportedTargetsMap = [release: "release", dev: "debug", debug: "release_debug"] supportedFlavors = ["editor", "template"] + supportedTargetsMapByFlavors = [ + "editor": [release: "release_debug", dev: "debug", debug: "release_debug"], + "template": [release: "release", dev: "debug", debug: "release_debug"] + ] // Used by gradle to specify which architecture to build for by default when running // `./gradlew build` (this command is usually used by Android Studio). @@ -49,6 +52,7 @@ ext { def rootDir = "../../.." def binDir = "$rootDir/bin/" +def androidEditorBuildsDir = "$binDir/android_editor_builds/" def getSconsTaskName(String flavor, String buildType, String abi) { return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize() @@ -175,13 +179,7 @@ def templateExcludedBuildTask() { if (!isAndroidStudio()) { logger.lifecycle("Excluding Android studio build tasks") for (String flavor : supportedFlavors) { - for (String buildType : supportedTargetsMap.keySet()) { - if (buildType == "release" && flavor == "editor") { - // The editor can't be used with target=release as debugging tools are then not - // included, and it would crash on errors instead of reporting them. - continue - } - + for (String buildType : supportedTargetsMapByFlavors[flavor].keySet()) { for (String abi : selectedAbis) { excludedTasks += ":lib:" + getSconsTaskName(flavor, buildType, abi) } @@ -195,7 +193,7 @@ def templateBuildTasks() { def tasks = [] // Only build the apks and aar files for which we have native shared libraries. - for (String target : supportedTargetsMap.keySet()) { + for (String target : supportedTargetsMapByFlavors["template"].keySet()) { File targetLibs = new File("lib/libs/" + target) if (targetLibs != null && targetLibs.isDirectory() @@ -221,18 +219,46 @@ def isAndroidStudio() { return sysProps != null && sysProps['idea.platform.prefix'] != null } -task copyEditorDebugBinaryToBin(type: Copy) { - dependsOn ':editor:assembleDebug' - from('editor/build/outputs/apk/debug') - into(binDir) - include('android_editor.apk') +task copyEditorReleaseApkToBin(type: Copy) { + dependsOn ':editor:assembleRelease' + from('editor/build/outputs/apk/release') + into(androidEditorBuildsDir) + include('android_editor-release*.apk') } -task copyEditorDevBinaryToBin(type: Copy) { +task copyEditorReleaseAabToBin(type: Copy) { + dependsOn ':editor:bundleRelease' + from('editor/build/outputs/bundle/release') + into(androidEditorBuildsDir) + include('android_editor-release*.aab') +} + +task copyEditorDebugApkToBin(type: Copy) { + dependsOn ':editor:assembleDebug' + from('editor/build/outputs/apk/debug') + into(androidEditorBuildsDir) + include('android_editor-debug.apk') +} + +task copyEditorDebugAabToBin(type: Copy) { + dependsOn ':editor:bundleDebug' + from('editor/build/outputs/bundle/debug') + into(androidEditorBuildsDir) + include('android_editor-debug.aab') +} + +task copyEditorDevApkToBin(type: Copy) { dependsOn ':editor:assembleDev' from('editor/build/outputs/apk/dev') - into(binDir) - include('android_editor_dev.apk') + into(androidEditorBuildsDir) + include('android_editor-dev.apk') +} + +task copyEditorDevAabToBin(type: Copy) { + dependsOn ':editor:bundleDev' + from('editor/build/outputs/bundle/dev') + into(androidEditorBuildsDir) + include('android_editor-dev.aab') } /** @@ -247,18 +273,14 @@ task generateGodotEditor { def tasks = [] - for (String target : supportedTargetsMap.keySet()) { - if (target == "release") { - // The editor can't be used with target=release as debugging tools are then not - // included, and it would crash on errors instead of reporting them. - continue - } + for (String target : supportedTargetsMapByFlavors["editor"].keySet()) { File targetLibs = new File("lib/libs/tools/" + target) if (targetLibs != null && targetLibs.isDirectory() && targetLibs.listFiles() != null && targetLibs.listFiles().length > 0) { - tasks += "copyEditor${target.capitalize()}BinaryToBin" + tasks += "copyEditor${target.capitalize()}ApkToBin" + tasks += "copyEditor${target.capitalize()}AabToBin" } } @@ -306,9 +328,11 @@ task cleanGodotEditor(type: Delete) { // Delete the generated binary apks delete("editor/build/outputs/apk") - // Delete the Godot editor apks in the Godot bin directory - delete("$binDir/android_editor.apk") - delete("$binDir/android_editor_dev.apk") + // Delete the generated aab binaries + delete("editor/build/outputs/bundle") + + // Delete the Godot editor apks & aabs in the Godot bin directory + delete(androidEditorBuildsDir) } /** diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle index dff0a7bd78f..f45875c6b6f 100644 --- a/platform/android/java/editor/build.gradle +++ b/platform/android/java/editor/build.gradle @@ -13,22 +13,67 @@ dependencies { } ext { - // Build number added as a suffix to the version code, and incremented for each build/upload to - // the Google Play store. - // This should be reset on each stable release of Godot. - editorBuildNumber = 0 + // Retrieve the build number from the environment variable; default to 0 if none is specified. + // The build number is added as a suffix to the version code for upload to the Google Play store. + getEditorBuildNumber = { -> + int buildNumber = 0 + String versionStatus = System.getenv("GODOT_VERSION_STATUS") + if (versionStatus != null && !versionStatus.isEmpty()) { + try { + buildNumber = Integer.parseInt(versionStatus.replaceAll("[^0-9]", "")); + } catch (NumberFormatException ignored) { + buildNumber = 0 + } + } + + return buildNumber + } // Value by which the Godot version code should be offset by to make room for the build number editorBuildNumberOffset = 100 + + // Return the keystore file used for signing the release build. + getGodotKeystoreFile = { -> + def keyStore = System.getenv("GODOT_ANDROID_SIGN_KEYSTORE") + if (keyStore == null) { + return null + } + return file(keyStore) + } + + // Return the key alias used for signing the release build. + getGodotKeyAlias = { -> + def kAlias = System.getenv("GODOT_ANDROID_KEYSTORE_ALIAS") + return kAlias + } + + // Return the password for the key used for signing the release build. + getGodotSigningPassword = { -> + def signingPassword = System.getenv("GODOT_ANDROID_SIGN_PASSWORD") + return signingPassword + } + + // Returns true if the environment variables contains the configuration for signing the release + // build. + hasReleaseSigningConfigs = { -> + def keystoreFile = getGodotKeystoreFile() + def keyAlias = getGodotKeyAlias() + def signingPassword = getGodotSigningPassword() + + return keystoreFile != null && keystoreFile.isFile() + && keyAlias != null && !keyAlias.isEmpty() + && signingPassword != null && !signingPassword.isEmpty() + } } def generateVersionCode() { int libraryVersionCode = getGodotLibraryVersionCode() - return (libraryVersionCode * editorBuildNumberOffset) + editorBuildNumber + return (libraryVersionCode * editorBuildNumberOffset) + getEditorBuildNumber() } def generateVersionName() { String libraryVersionName = getGodotLibraryVersionName() - return libraryVersionName + ".$editorBuildNumber" + int buildNumber = getEditorBuildNumber() + return buildNumber == 0 ? libraryVersionName : libraryVersionName + ".$buildNumber" } android { @@ -45,6 +90,7 @@ android { targetSdkVersion versions.targetSdk missingDimensionStrategy 'products', 'editor' + setProperty("archivesBaseName", "android_editor") } compileOptions { @@ -56,6 +102,15 @@ android { jvmTarget = versions.javaVersion } + signingConfigs { + release { + storeFile getGodotKeystoreFile() + storePassword getGodotSigningPassword() + keyAlias getGodotKeyAlias() + keyPassword getGodotSigningPassword() + } + } + buildTypes { dev { initWith debug @@ -65,14 +120,14 @@ android { debug { initWith release - // Need to swap with the release signing config when this is ready for public release. + applicationIdSuffix ".debug" signingConfig signingConfigs.debug } release { - // This buildtype is disabled below. - // The editor can't be used with target=release only, as debugging tools are then not - // included, and it would crash on errors instead of reporting them. + if (hasReleaseSigningConfigs()) { + signingConfig signingConfigs.release + } } } @@ -82,20 +137,4 @@ android { doNotStrip '**/*.so' } } - - // Disable 'release' buildtype. - // The editor can't be used with target=release only, as debugging tools are then not - // included, and it would crash on errors instead of reporting them. - variantFilter { variant -> - if (variant.buildType.name == "release") { - setIgnore(true) - } - } - - applicationVariants.all { variant -> - variant.outputs.all { output -> - def suffix = variant.name == "dev" ? "_dev" : "" - output.outputFileName = "android_editor${suffix}.apk" - } - } } diff --git a/platform/android/java/editor/src/debug/res/values/strings.xml b/platform/android/java/editor/src/debug/res/values/strings.xml new file mode 100644 index 00000000000..cb007da2c87 --- /dev/null +++ b/platform/android/java/editor/src/debug/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Godot Editor 3 (debug) + diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 488d9c008bc..0258f40bf00 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -80,19 +80,11 @@ android { release.jniLibs.srcDirs = ['libs/release'] // Editor jni library + editorRelease.jniLibs.srcDirs = ['libs/tools/release'] editorDebug.jniLibs.srcDirs = ['libs/tools/debug'] editorDev.jniLibs.srcDirs = ['libs/tools/dev'] } - // Disable 'editorRelease'. - // The editor can't be used with target=release as debugging tools are then not - // included, and it would crash on errors instead of reporting them. - variantFilter { variant -> - if (variant.name == "editorRelease") { - setIgnore(true) - } - } - libraryVariants.all { variant -> def flavorName = variant.getFlavorName() if (flavorName == null || flavorName == "") { @@ -102,11 +94,14 @@ android { boolean toolsFlag = flavorName == "editor" def buildType = variant.buildType.name - if (buildType == null || buildType == "" || !supportedTargetsMap.containsKey(buildType)) { + if (buildType == null || buildType == "" || !supportedTargetsMapByFlavors[flavorName].containsKey(buildType)) { throw new GradleException("Invalid build type: $buildType") } - def sconsTarget = supportedTargetsMap[buildType] + boolean productionBuild = buildType != "dev" + boolean storeRelease = buildType == "release" + + def sconsTarget = supportedTargetsMapByFlavors[flavorName][buildType] if (sconsTarget == null || sconsTarget == "") { throw new GradleException("Invalid scons target: $sconsTarget") } @@ -126,10 +121,10 @@ android { def sconsExts = (org.gradle.internal.os.OperatingSystem.current().isWindows() ? [".bat", ".cmd", ".ps1", ".exe"] : [""]) - logger.lifecycle("Looking for $sconsName executable path") + logger.debug("Looking for $sconsName executable path") for (ext in sconsExts) { String sconsNameExt = sconsName + ext - logger.lifecycle("Checking $sconsNameExt") + logger.debug("Checking $sconsNameExt") sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt) if (sconsExecutableFile != null) { @@ -149,7 +144,7 @@ android { if (sconsExecutableFile == null) { throw new GradleException("Unable to find executable path for the '$sconsName' command.") } else { - logger.lifecycle("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}") + logger.debug("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}") } for (String selectedAbi : selectedAbis) { @@ -161,7 +156,7 @@ android { def taskName = getSconsTaskName(flavorName, buildType, selectedAbi) tasks.create(name: taskName, type: Exec) { executable sconsExecutableFile.absolutePath - args "--directory=${pathToRootDir}", "platform=android", "tools=${toolsFlag}", "target=${sconsTarget}", "android_arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors() + args "--directory=${pathToRootDir}", "platform=android", "store_release=${storeRelease}", "production=${productionBuild}", "tools=${toolsFlag}", "target=${sconsTarget}", "android_arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors() } // Schedule the tasks so the generated libs are present before the aar file is packaged.