godot-rust/gdext

Android support

Bromeon opened this issue · 11 comments

Knowledge base for the Android port: what's missing, insights, obstacles, etc.
There's no active work going on at the moment, but we can already collect some infos.

Related Discord thread.

@Bromeon link is dead. Can you post the discussion here? Discord is bad for open discussions (and open source projects) being so locked down.

The link works for me, but one needs to be in the godot-rust server.

Discord is bad for open discussions (and open source projects) being so locked down.

The entire reason why I created this issue is precisely that the relevant points are moved to GitHub and not buried in a Discord thread. The idea is that everyone posts here with a summary of their findings.

That said, the reality is that many people use Discord, there is clear demand for free-flowing chat, and we would be considerably worse off if we had only GitHub. But this issue is not the place to discuss it, so I'll mark our comments off-topic 🙂

First update rust binaries:

rustup target add aarch64-linux-android
rustup target add arm-linux-androideabi
rustup target add i686-linux-android
rustup target add x86_64-linux-android

Windows users need this for the cargo build. Install https://developer.android.com/ndk/downloads and Set environment variables:

C_INCLUDE_PATH=$HOME/Android/Sdk/ndk/26.0.10792818/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/:$HOME/Android/Sdk/ndk/26.0.10792818/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/arm-linux-androideabi cargo build --target armv7-linux-androideabi
C_INCLUDE_PATH=$HOME/Android/Sdk/ndk/26.0.10792818/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/:$HOME/Android/Sdk/ndk/26.0.10792818/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/aarch64-linux-android cargo build --target aarch64-linux-android

What is actually missing here still?
Is it just some documentation, like the discord thread implies, or is there more to it than that?

It's mostly missing one or a few motivated users that drive this forward, in terms of:

  1. reproducible and robust toolchain to compile to Android (including Godot configuration)
  2. CI integration, maybe some tests to prevent regressions
  3. documentation

That is not to say a single person has to do all of them. I gladly provide support, especially when it comes to gdext-specific things such as CI setup, Godot integration tests etc. But I don't see myself realistically having the capacity to implement this myself in the next months, there's also the WASM port which is actively being worked on, and lots of core features to be ironed out.

Ok, I can at least give a rough description on how I made it work locally (Linux/Arch):

  1. Install system dependencies
  • aur/android-sdk
  • aur/android-ndk
  1. Install rust targets
rustup target add aarch64-linux-android

2.5) Install cargo ndk

cargo install cargo ndk
  1. Build shared libraries for Android arm64
    (you need to repeat this step every time you change your Rust code)
   env ANDROID_NDK_HOME=/opt/android-ndk cargo ndk -t arm64-v8a build
   env ANDROID_NDK_HOME=/opt/android-ndk cargo ndk -t arm64-v8a build --release

NOTE: If you are on a different distro, or you installed the ndk through Android Studio, you'll need to change the path to point to your correct ndk!

  1. Generate a developer key for signing the APK
keytool -keyalg RSA -genkeypair -alias androiddebugkey -keypass android -keystore debug.keystore -storepass android -dname "CN=Android Debug,O=Android,C=US" -validity 9999 -deststoretype pkcs12

Note: I'm not quite sure which package keytool is from, it may not necessarily be installed by only installing the dependencies mentioned above.

  1. Add Android SDK path as well as username and password in Godot Settings
  • Top Bar
  • Editor
  • Editor Settings
  • Search for "Android" in the search bar
  • Click on Export->Android in the left pane
  • Set SDK path to /home/USER/Android/Sdk
    • You need Android Studio for this! - Don't forget to install the correct SDK
  • Set the debug keystore to the file you created in the previous step with keytool (debug.keystore)
  • Set user to "androiddebugkey"
  • Set password to "android"
  • Close the Settings
  1. Add Android Build-Template
  • Top Bar
  • Project
  • Create Android-Build-Template
  • follow the Wizard
  1. Add the Android targets to your .gdextensions file

In my case it looks like this:

[configuration]
entry_symbol = "gdext_rust_init"
compatibility_minimum = 4.1

[libraries]
linux.debug.x86_64     = "res://../rust/target/debug/librust_project.so"
linux.release.x86_64   = "res://../rust/target/release/librust_project.so"
                            
windows.debug.x86_64   = "res://../rust/target/debug/rust_project.dll"
windows.release.x86_64 = "res://../rust/target/release/rust_project.dll"
                            
macos.debug            = "res://../rust/target/debug/librust_project.dylib"
macos.release          = "res://../rust/target/release/librust_project.dylib"

macos.debug.arm64      = "res://../rust/target/debug/librust_project.dylib"
macos.release.arm64    = "res://../rust/target/release/librust_project.dylib"

android.release.arm64  = "res://../rust/target/aarch64-linux-android/debug/librust_project.so"
android.debug.arm64    = "res://../rust/target/aarch64-linux-android/release/librust_project.so"
  1. Reload the Project in the Editor
    You are either prompted to reload the project,
    or just use TopBar->Project->Reload, or just close Godot and re-open it.

  2. Export the APK

-> Top Bar
-> Project
-> Export

  • Select Android
  • Rename your project acc. to android rules (otherwise you'll get an error)
  • Click "Export Project"

You should now be left with an apk file in the godot/build folder.
You can copy this apk to an end device and it should work.

Note that Android Emulators which use x86/64-Android will not work for executing this. - We didn't compile in the necessary targets. - Though I'm sure you could add them somehow.

Step-by-step for building Android library on Mac Apple/Arm devices

I'm experimenting with how to get this working on Mac (ARM) computers, using Godot 4.2.1, I dont have it working yet, but here is the work in progress.

  1. Setup needed before building anything.
    rustup target add aarch64-linux-android
    rustup target add armv7-linux-androideabi
  1. Install the ndk, then poke around your /User/$USER/Library/ folder to find the correct CC and AR paths inside the ndk and set them
export CC=/Users/USER/Library/Android/sdk/ndk/23.2.8568313/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android31-clang
export AR=/Users/USER/Library/Android/sdk/ndk/23.2.8568313/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ar
export RANLIB=/Users/USER/Library/Android/sdk/ndk/23.2.8568313/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ranlib
export PATH="/Users/USER/Library/Android/sdk/ndk/23.2.8568313/toolchains/llvm/prebuilt/darwin-x86_64/bin/:$PATH"
  1. Add the lazy-function-tables feature flag.

This feature flag makes your application non-thread safe. But bypasses a bug that is currently under investigation.

[dependencies]
godot = { git = "https://github.com/godot-rust/gdext", branch = "master", features = ["lazy-function-tables"]}
  1. Add this config ~/.cargo/config:

It is not clear to me why this is needed. The next step fails on Mac if you do not have these settings:

[target.armv7-linux-androideabi]
linker = "armv7a-linux-androideabi31-clang"
rustflags = [
  "-C", "link-arg=-undefined",
  "-C", "link-arg=dynamic_lookup",
]

[target.aarch64-linux-android]
linker = "aarch64-linux-android31-clang"
rustflags = [
  "-C", "link-arg=-undefined",
  "-C", "link-arg=dynamic_lookup",
]
  1. Build your rust code into a file to be shared into Godot:
cargo build --target armv7-linux-androideabi && \
    cargo build --target aarch64-linux-android && \
    cargo build --release --target armv7-linux-androideabi && \
    cargo build --release --target aarch64-linux-android
  1. Copy into project:
cp target/armv7-linux-androideabi/debug/libpolla.so ../Polla/lib_polla_armv7-linux-androideabi_debug.so
cp target/armv7-linux-androideabi/release/libpolla.so ../Polla/lib_polla_armv7-linux-androideabi_release.so
cp target/aarch64-linux-android/debug/libpolla.so ../Polla/lib_polla_aarch64-linux-android_debug.so
cp target/aarch64-linux-android/release/libpolla.so ../Polla/lib_polla_aarch64-linux-android_release.so

I can confirm I got it working on Android too, as @moberer described, using Ubuntu 22.04.

I am not sure what cargo ndk is though, on my end this produced error: no such command: 'ndk'. Instead I used:

ANDROID_NDK_HOME=<path/to/the/ndk> cargo build --target aarch64-linux-android
ANDROID_NDK_HOME=<path/to/the/ndk> cargo build --target aarch64-linux-android --release

Also I got error: linking with 'cc' failed: exit status: 1 doing so, so I used the trick described here; I added this to ~/.cargo/config (actually I created this file):

[target.aarch64-linux-android]
linker = "<path/to/the/ndk>/26.1.10909125/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android34-clang"

The versions could be different.

It worked fine after that 👌

I am not sure what cargo ndk is though, on my end this produced error: no such command: 'ndk'.

Cargo-NDK is a helper tool supposed to make this easier.

But you need to install it manually, before being able to use it.
I forgot to add that to my writeup, sorry for any wasted time because of that...

You can install it like that:

cargo install cargo-ndk

Afterward, the command should work.

But as you discovered, it is not really necessary and only a "helper" to make the commands a bit less annoying.

  • Rename your project acc. to android rules (otherwise you'll get an error)

I do not know what this field is or how to fix it. but it might just be the issue I am encountering. my app runs fine on my laptop, but when I run in android it is a black screen. could this be the cause?

  • Rename your project acc. to android rules (otherwise you'll get an error)

I do not know what this field is or how to fix it. but it might just be the issue I am encountering. my app runs fine on my laptop, but when I run in android it is a black screen. could this be the cause?

Unlikely, when I had a bad name, I think it manifested as an export error.