Bläddra i källkod

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

(cherry picked from commit 0d569bea5dfca80fa97879a93af62090d98073a2)
Fredia Huya-Kouadio 2 år sedan
förälder
incheckning
f7ee97d6d7

+ 4 - 1
platform/android/SCsub

@@ -56,7 +56,10 @@ if lib_arch_dir != "":
     if env.dev_build:
         lib_type_dir = "dev"
     elif env.debug_features:
-        lib_type_dir = "debug"
+        if env.editor_build and env["store_release"]:
+            lib_type_dir = "release"
+        else:
+            lib_type_dir = "debug"
     else:  # Release
         lib_type_dir = "release"
 

+ 3 - 0
platform/android/detect.py

@@ -22,6 +22,8 @@ def can_build():
 
 
 def get_opts():
+    from SCons.Variables import BoolVariable
+
     return [
         ("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()),
         (
@@ -29,6 +31,7 @@ def get_opts():
             'Target platform (android-<api>, e.g. "android-' + str(get_min_target_api()) + '")',
             "android-" + str(get_min_target_api()),
         ),
+        BoolVariable("store_release", "Editor build for Google Play Store (for official builds only)", False),
     ]
 
 

+ 1 - 1
platform/android/java/app/config.gradle

@@ -135,7 +135,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

+ 44 - 14
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'
     }
 }
 
@@ -38,9 +38,7 @@ ext {
     supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"]
     supportedFlavors = ["editor", "template"]
     supportedFlavorsBuildTypes = [
-        // 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.
-        "editor": ["dev", "debug"],
+        "editor": ["dev", "debug", "release"],
         "template": ["dev", "debug", "release"]
     ]
 
@@ -54,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()
@@ -221,18 +220,46 @@ def isAndroidStudio() {
     return sysProps != null && sysProps['idea.platform.prefix'] != null
 }
 
-task copyEditorDebugBinaryToBin(type: Copy) {
+task copyEditorReleaseApkToBin(type: Copy) {
+    dependsOn ':editor:assembleRelease'
+    from('editor/build/outputs/apk/release')
+    into(androidEditorBuildsDir)
+    include('android_editor-release*.apk')
+}
+
+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(binDir)
-    include('android_editor.apk')
+    into(androidEditorBuildsDir)
+    include('android_editor-debug.apk')
 }
 
-task copyEditorDevBinaryToBin(type: Copy) {
+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')
 }
 
 /**
@@ -253,7 +280,8 @@ task generateGodotEditor {
             && targetLibs.isDirectory()
             && targetLibs.listFiles() != null
             && targetLibs.listFiles().length > 0) {
-            tasks += "copyEditor${target.capitalize()}BinaryToBin"
+            tasks += "copyEditor${target.capitalize()}ApkToBin"
+            tasks += "copyEditor${target.capitalize()}AabToBin"
         }
     }
 
@@ -301,9 +329,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)
 }
 
 /**

+ 65 - 27
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
@@ -64,15 +119,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 +136,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"
-        }
-    }
 }

+ 4 - 0
platform/android/java/editor/src/debug/res/values/strings.xml

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

+ 10 - 13
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 == "") {
@@ -105,9 +97,14 @@ android {
         }
 
         boolean devBuild = buildType == "dev"
+        boolean runTests = devBuild
+        boolean productionBuild = !devBuild
+        boolean storeRelease = buildType == "release"
 
         def sconsTarget = flavorName
         if (sconsTarget == "template") {
+            // Tests are not supported on template builds
+            runTests = false
             switch (buildType) {
                 case "release":
                     sconsTarget += "_release"
@@ -135,10 +132,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) {
                 // We're done!
@@ -155,7 +152,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) {
@@ -167,7 +164,7 @@ android {
             def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
             tasks.create(name: taskName, type: Exec) {
                 executable sconsExecutableFile.absolutePath
-                args "--directory=${pathToRootDir}", "platform=android", "dev_mode=${devBuild}", "dev_build=${devBuild}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
+                args "--directory=${pathToRootDir}", "platform=android", "store_release=${storeRelease}", "production=${productionBuild}", "dev_mode=${devBuild}", "dev_build=${devBuild}", "tests=${runTests}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
             }
 
             // Schedule the tasks so the generated libs are present before the aar file is packaged.