Skip to content

Commit

Permalink
Feature/android support (#101)
Browse files Browse the repository at this point in the history
* Add jvm cpp distinction by platforms

* Differentiate jvm init arguments for android and desktop, android one is dummy doing nothing

* Remove exceptions in favor of error crash and logs. Make android implem of InitArgs and get ClassLoader from android platform

* avoid copy jre for android export

* Exclude java 9+ module-info for android support

* Make difference between dex jars and standard jars

* create class loader that load bootstrap jar for android

* Set bootstrap classloader as parent classloader for main jar loader on android, platform ready

* Automate dex conversion and update docs

* Update supported targets

* Add android build to CI

* Replace `ANDROID_HOME` with `ANDROID_SDK_ROOT` and update docs

* Make jvm exception crash engine only when in debug and not TOOL

* Add aar to jre export in export plugin

Co-authored-by: Cedric Hippmann <[email protected]>
  • Loading branch information
piiertho and chippmann authored Mar 13, 2021
1 parent 48699c8 commit 7bbf2b0
Show file tree
Hide file tree
Showing 34 changed files with 550 additions and 271 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/check-pr-engine-editor-debug-and-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ jobs:
build-editor-debug:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
name: [ Linux, OSX, Windows ]
include:
- os: ubuntu-latest
- name: Linux
os: ubuntu-latest
platform: x11
- os: macos-latest
- name: OSX
os: macos-latest
platform: osx
- os: windows-latest
- name: Windows
os: windows-latest
platform: windows
runs-on: ${{ matrix.os }}
steps:
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/check-pr-engine-editor-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ jobs:
build-editor-release:
strategy:
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
name: [ Linux, OSX, Windows ]
include:
- os: ubuntu-latest
- name: Linux
os: ubuntu-latest
platform: x11
- os: macos-latest
- name: OSX
os: macos-latest
platform: osx
- os: windows-latest
- name: Windows
os: windows-latest
platform: windows
runs-on: ${{ matrix.os }}
steps:
Expand Down
35 changes: 31 additions & 4 deletions .github/workflows/check-pr-engine-export-template-debug.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ jobs:
build-export-debug:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
name: [ Linux, OSX, Windows, Android ]
include:
- os: ubuntu-latest
- name: Linux
os: ubuntu-latest
platform: x11
- os: macos-latest
- name: OSX
os: macos-latest
platform: osx
- os: windows-latest
- name: Windows
os: windows-latest
platform: windows
- name: Android
os: ubuntu-latest
platform: android
runs-on: ${{ matrix.os }}
steps:
- name: Configure dependencies
Expand All @@ -39,6 +45,27 @@ jobs:
run: |
python -c "import sys; print(sys.version)"
python -m pip install scons
- name: Set up JDK 1.8
if: matrix.platform == 'android'
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Setup Android SDK
if: matrix.platform == 'android'
uses: android-actions/setup-android@v2
- name: Build debug export template
if: matrix.platform != 'android'
run: |
scons platform=${{ matrix.platform }} tools=no target=release_debug bits=64
- name: Build android debug binary armv7
if: matrix.platform == 'android'
run: |
scons platform=${{ matrix.platform }} target=release_debug android_arch=armv7
- name: Build android debug binary arm64v8
if: matrix.platform == 'android'
run: |
scons platform=${{ matrix.platform }} target=release_debug android_arch=arm64v8
- name: Build android debug export template
if: matrix.platform == 'android'
run: |
cd platform/android/java/ && ./gradlew generateGodotTemplates
35 changes: 31 additions & 4 deletions .github/workflows/check-pr-engine-export-template-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ jobs:
build-export-release:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
name: [ Linux, OSX, Windows, Android ]
include:
- os: ubuntu-latest
- name: Linux
os: ubuntu-latest
platform: x11
- os: macos-latest
- name: OSX
os: macos-latest
platform: osx
- os: windows-latest
- name: Windows
os: windows-latest
platform: windows
- name: Android
os: ubuntu-latest
platform: android
runs-on: ${{ matrix.os }}
steps:
- name: Configure dependencies
Expand All @@ -39,6 +45,27 @@ jobs:
run: |
python -c "import sys; print(sys.version)"
python -m pip install scons
- name: Set up JDK 1.8
if: matrix.platform == 'android'
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Setup Android SDK
if: matrix.platform == 'android'
uses: android-actions/setup-android@v2
- name: Build release export template
if: matrix.platform != 'android'
run: |
scons platform=${{ matrix.platform }} tools=no target=release bits=64
- name: Build android release binary armv7
if: matrix.platform == 'android'
run: |
scons platform=${{ matrix.platform }} target=release android_arch=armv7
- name: Build android release binary arm64v8
if: matrix.platform == 'android'
run: |
scons platform=${{ matrix.platform }} target=release android_arch=arm64v8
- name: Build android release export template
if: matrix.platform == 'android'
run: |
cd platform/android/java/ && ./gradlew generateGodotTemplates
12 changes: 9 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ set(GODOT_ROOT_DIR ../../)
# Get sources
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS src/*.cpp)

add_library(${PROJECT_NAME} SHARED register_types.cpp ${SOURCES} src/gd_kotlin.cpp src/gd_kotlin.h src/godotkotlin_defs.h src/jni/wrapper.h src/kt_function.cpp src/kt_function.h src/kt_property.cpp src/kt_property.h src/jni/local_frame.cpp src/jni/local_frame.h src/kt_signal_info.cpp src/kt_signal_info.h src/bridges/memory_bridge.cpp src/bridges/memory_bridge.h src/jni/java_method_signature.cpp src/jni/java_method_signature.h src/java_instance_wrapper.h src/shared_buffer.h src/shared_buffer.cpp src/type_manager.cpp src/type_manager.h src/bridges_manager.cpp src/bridges_manager.h src/bridges/variant_array_bridge.cpp src/bridges/variant_array_bridge.h src/bridges/constants.h src/bridges/dictionary_bridge.cpp src/bridges/dictionary_bridge.h src/bridges/bridges_utils.h src/bridges/rid_bridge.cpp src/bridges/rid_bridge.h src/bridges/node_path_bridge.cpp src/bridges/node_path_bridge.h src/bridges/pool_byte_array_bridge.h src/bridges/pool_byte_array_bridge.cpp src/bridges/pool_color_array_bridge.h src/bridges/pool_color_array_bridge.cpp src/bridges/pool_int_array_bridge.h src/bridges/pool_int_array_bridge.cpp src/bridges/pool_real_array_bridge.h src/bridges/pool_real_array_bridge.cpp src/bridges/pool_string_array_bridge.h src/bridges/pool_string_array_bridge.cpp src/bridges/pool_vector2_array_bridge.h src/bridges/pool_vector2_array_bridge.cpp src/bridges/pool_vector3_array_bridge.h src/bridges/pool_vector3_array_bridge.cpp src/logging.h src/kt_constructor.cpp src/kt_constructor.h src/bridges/gd_print_bridge.cpp src/bridges/gd_print_bridge.h src/kotlin_editor_export_plugin.cpp src/kotlin_editor_export_plugin.h)
add_library(${PROJECT_NAME} SHARED register_types.cpp ${SOURCES} src/gd_kotlin.cpp src/gd_kotlin.h src/godotkotlin_defs.h src/jni/wrapper.h src/kt_function.cpp src/kt_function.h src/kt_property.cpp src/kt_property.h src/jni/local_frame.cpp src/jni/local_frame.h src/kt_signal_info.cpp src/kt_signal_info.h src/bridges/memory_bridge.cpp src/bridges/memory_bridge.h src/jni/java_method_signature.cpp src/jni/java_method_signature.h src/java_instance_wrapper.h src/shared_buffer.h src/shared_buffer.cpp src/type_manager.cpp src/type_manager.h src/bridges_manager.cpp src/bridges_manager.h src/bridges/variant_array_bridge.cpp src/bridges/variant_array_bridge.h src/bridges/constants.h src/bridges/dictionary_bridge.cpp src/bridges/dictionary_bridge.h src/bridges/bridges_utils.h src/bridges/rid_bridge.cpp src/bridges/rid_bridge.h src/bridges/node_path_bridge.cpp src/bridges/node_path_bridge.h src/bridges/pool_byte_array_bridge.h src/bridges/pool_byte_array_bridge.cpp src/bridges/pool_color_array_bridge.h src/bridges/pool_color_array_bridge.cpp src/bridges/pool_int_array_bridge.h src/bridges/pool_int_array_bridge.cpp src/bridges/pool_real_array_bridge.h src/bridges/pool_real_array_bridge.cpp src/bridges/pool_string_array_bridge.h src/bridges/pool_string_array_bridge.cpp src/bridges/pool_vector2_array_bridge.h src/bridges/pool_vector2_array_bridge.cpp src/bridges/pool_vector3_array_bridge.h src/bridges/pool_vector3_array_bridge.cpp src/logging.h src/kt_constructor.cpp src/kt_constructor.h src/bridges/gd_print_bridge.cpp src/bridges/gd_print_bridge.h src/kotlin_editor_export_plugin.cpp src/kotlin_editor_export_plugin.h src/jni/platforms/jvm_desktop.cpp src/jni/platforms/jvm_android.cpp src/jni/platforms/init_args_desktop.cpp src/jni/platforms/init_args_android.cpp)

# JNI
find_package(JNI REQUIRED)
Expand All @@ -21,5 +21,11 @@ target_include_directories(${PROJECT_NAME}
)

# Uncomment to pass auto completion in TOOLS mode
add_compile_definitions(TOOLS_ENABLED)
add_compile_definitions(DEBUG_ENABLED)
#add_compile_definitions(TOOLS_ENABLED)
add_compile_definitions(DEBUG_ENABLED)

# platforms
#add_compile_definitions(WINDOWS_ENABLED)
#add_compile_definitions(X11_ENABLED)
#add_compile_definitions(OSX_ENABLED)
add_compile_definitions(__ANDROID__)
19 changes: 12 additions & 7 deletions SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@ Import("env")

java_home = os.environ["JAVA_HOME"]

java_include_dirs = [
java_home + "/include",
java_home + "/include/linux",
java_home + "/include/win32",
java_home + "/include/darwin"
]
if env["platform"] != "android":
java_include_dirs = [
java_home + "/include",
java_home + "/include/linux",
java_home + "/include/win32",
java_home + "/include/darwin"
]

module_env = env.Clone()
module_env.Append(CPPPATH=[java_include_dirs])

if env["platform"] != "android":
module_env.Append(CPPPATH=[java_include_dirs])

module_env.add_source_files(module_env.modules_sources, "register_types.cpp")
module_env.add_source_files(module_env.modules_sources, "src/jni/*.cpp")
module_env.add_source_files(module_env.modules_sources, "src/jni/platforms/*.cpp")
module_env.add_source_files(module_env.modules_sources, "src/bridges/*.cpp")
module_env.add_source_files(module_env.modules_sources, "src/*.cpp")
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ nav:
- Properties: user-guide/properties.md
- Signals: user-guide/signals.md
- Functions: user-guide/functions.md
- Exporting: user-guide/exporting.md
- Advanced:
- Custom Src dirs: advanced/custom-src-dirs.md
- Commandline args: advanced/commandline-args.md
Expand Down
3 changes: 2 additions & 1 deletion docs/src/doc/supported-platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ While Kotlin and Godot supports a wide range of platforms, this module for the m
- Windows X64
- Linux X64
- MacOS X64
- Android (arm64v8 and armv7)

Mobile platforms such as Android and iOS will be supported at a later date.
iOS will be supported at a later date.
37 changes: 31 additions & 6 deletions docs/src/doc/user-guide/exporting.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Exporting games

To export game, you should copy exports in right template folder, which should be:
To export the game, you should copy exports into the correct template folder, which should be:
- Windows: `%APPDATA%\Godot\templates\<godot-version>`
- Linux: `~/.local/share/godot/templates/<godot-version>`

Expand All @@ -12,15 +12,40 @@ And `release_debug` to:
- Windows: `windows_64_debug.exe`
- Linux: `linux_x11_64_debug`

On osx use the script mentionned in [this issue](https://github.com/godotengine/godot-docs/issues/3194#issuecomment-588862977),
this will do the job for you.
On osx use the script mentioned in [this issue](https://github.com/godotengine/godot-docs/issues/3194#issuecomment-588862977). It will do the job for you.

Then you can export your game as usual, your game `jar` will be included in `pck`.
On desktop platforms, this also copies the jre folder of your project in the exported game folder.

## Particularities

`godot-bootstrap.jar` and `main.jar` are set into `pck` during the export process. Also, as a real file path is needed to
handle them, they are copied on the first game version start from `res://` to `user://` (We check if exists and also md5)
`godot-bootstrap.jar` and `main.jar` are set into `pck` during the export process. As a real file path is needed to
handle them, they are copied on the first game version start from `res://` to `user://` (We check if it exists and also check the md5 hash)
to only update when needed.
Don't forget to remove them when uninstalling the game.
Don't forget to remove them when writing an uninstaller for the game.

## Android
In order to build on android, set the `isAndroidExportEnabled` flag to true in the `build.gradle.kts`:
```kotlin
godot {
isAndroidExportEnabled.set(true)
}
```
On android we do not embedd a JVM. We use the existing DalvikVM of android.
In order for your game to load the necessary jar files, they need to be converted into dex format.
Our gradle plugin will handle this for you, but you need to fulfill the following requirements:

- Android SDK installed
- `dx` tool resolvable by doing one of the following:
- set the `PATH` environment variable to include `<android-sdk-dir>/build-tools/<version>/`
- set the path to the `dx` tool through gradle with the property `dxToolPath`:
```kotlin
godot {
isAndroidExportEnabled.set(true)
dxToolPath.set("${System.getenv("ANDROID_SDK_ROOT")}/build-tools/30.0.3/dx")
}
```

!!! warning "Important:"
Same as on the desktop targets, the game copies the needed jar files to the `user://` dir upon first execution or if the files have changed. On android this is the applications `files` folder.
If you do IO operations on android, never empty the whole `files` folder! Only delete what you have added or exclude the following two files when clearing the `files` folder: `godot-bootstrap-dex.jar` and `main-dex.jar`.
6 changes: 6 additions & 0 deletions harness/tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ repositories {
jcenter()
}

godot {
//uncomment to test android
// isAndroidExportEnabled.set(true)
// dxToolPath.set("${System.getenv("ANDROID_SDK_ROOT")}/build-tools/30.0.3/dx")
}

dependencies {
implementation("joda-time:joda-time:2.10.6") //external dependency to test dependency inclusion in dummyCompilation
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,22 @@ class MultiArgsConstructorTest : Node {
@RegisterConstructor
constructor(i: Int, s: String, obj: Object?, variantArray: VariantArray<Any?>?, navMesh: NavigationMesh) : this()

constructor(iShouldNOTFailAsImNotRegistered: String, s: Int, obj: Object?, variantArray: VariantArray<Any?>?, navMesh: NavigationMesh) : this()
constructor(i: Int, s: String, obj: Object?, variantArray: VariantArray<Any?>?, navMesh: NavigationMesh, tooManyArgsShouldWorkIfNotRegistered: String) : this()
constructor(
iShouldNOTFailAsImNotRegistered: String,
s: Int,
obj: Object?,
variantArray: VariantArray<Any?>?,
navMesh: NavigationMesh
) : this()

constructor(
i: Int,
s: String,
obj: Object?,
variantArray: VariantArray<Any?>?,
navMesh: NavigationMesh,
tooManyArgsShouldWorkIfNotRegistered: String
) : this()

// constructors which should fail:
// @RegisterConstructor
Expand Down
1 change: 1 addition & 0 deletions kt/godot-library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ tasks {
archiveBaseName.set("godot-bootstrap")
archiveVersion.set("")
archiveClassifier.set("")
exclude("**/module-info.class") //for android support: excludes java 9+ module info which cannot be parsed by the dx tool
finalizedBy(copyBootstrapJar)
}

Expand Down
21 changes: 10 additions & 11 deletions kt/godot-runtime/src/main/kotlin/godot/runtime/Bootstrap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ import java.util.concurrent.TimeUnit

class Bootstrap {
private var registry: ClassRegistry? = null
private lateinit var classloader: URLClassLoader
private lateinit var classloader: ClassLoader
private lateinit var serviceLoader: ServiceLoader<Entry>
private var executor: ScheduledExecutorService? = null
private var watchService: WatchService? = null
private var engineTypesRegistered: Boolean = false

fun init(isEditor: Boolean, jarRootDir: String) {
fun init(isEditor: Boolean, jarRootDir: String, jarFile: String, loader: ClassLoader?) {
val libsDir = Paths.get(jarRootDir)
val mainJarPath = libsDir.resolve("main.jar")
val mainJarPath = libsDir.resolve(jarFile)

if (File(mainJarPath.toString()).exists()) {
doInit(mainJarPath.toUri().toURL())
doInit(mainJarPath.toUri().toURL(), loader)
} else {
if (isEditor) {
::warning
Expand Down Expand Up @@ -65,7 +65,7 @@ class Bootstrap {
clearClassesCache()

if (File(mainJarPath.toString()).exists()) {
doInit(mainJarPath.toUri().toURL())
doInit(mainJarPath.toUri().toURL(), classloader)
} else {
warning("No main.jar detected. No classes will be loaded. Build the project to load classes")
}
Expand All @@ -87,15 +87,14 @@ class Bootstrap {
Thread.currentThread().contextClassLoader = classloader
}

private fun doInit(mainJar: URL) {
private fun doInit(mainJar: URL, classLoader: ClassLoader?) {
registry = ClassRegistry()
classloader = URLClassLoader(arrayOf(mainJar), this::class.java.classLoader)
classloader = classLoader ?: URLClassLoader(arrayOf(mainJar), this::class.java.classLoader)
Thread.currentThread().contextClassLoader = classloader
serviceLoader = ServiceLoader.load(Entry::class.java, classloader)
val entry = serviceLoader.findFirst()

if (entry.isPresent) {
with(entry.get()) {
val entryIterator = serviceLoader.iterator()
if (entryIterator.hasNext()) {
with(entryIterator.next()) {
val context = Entry.Context(registry!!)

if (!engineTypesRegistered) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package godot.gradle

import org.gradle.api.model.ObjectFactory
import org.gradle.kotlin.dsl.property

open class GodotExtension(objects: ObjectFactory) {
val isAndroidExportEnabled = objects.property<Boolean>()
val dxToolPath = objects.property<String>()
}
Loading

0 comments on commit 7bbf2b0

Please sign in to comment.