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
This commit is contained in:
Fredia Huya-Kouadio 2023-03-07 17:12:00 -08:00
parent 3943de2e6c
commit 30e3e301e0
7 changed files with 137 additions and 71 deletions

View file

@ -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"

View file

@ -24,6 +24,7 @@ def get_opts():
("ndk_platform", 'Target platform (android-<api>, 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),
]

View file

@ -132,7 +132,7 @@ ext.generateGodotLibraryVersion = { List<String> 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

View file

@ -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)
}
/**

View file

@ -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"
}
}
}

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_editor_name_string">Godot Editor 3 (debug)</string>
</resources>

View file

@ -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.