From 5151e6766f3686a8bef475fc90aee794274ab3c9 Mon Sep 17 00:00:00 2001 From: cos Date: Sun, 22 Mar 2020 19:08:12 +0100 Subject: Initial (and likely only) commit --- Cargo.toml | 7 +++++++ FFI.java | 10 ++++++++++ MainActivity.java | 22 ++++++++++++++++++++++ README | 9 +++++++++ activity_main.xml | 13 +++++++++++++ build_apk.sh | 20 ++++++++++++++++++++ build_java.sh | 14 ++++++++++++++ build_ndk.sh | 8 ++++++++ build_rust.sh | 11 +++++++++++ cargo.config | 7 +++++++ install_rust.sh | 4 ++++ lib.rs-part0 | 13 +++++++++++++ lib.rs-part1 | 21 +++++++++++++++++++++ strings.xml | 5 +++++ 14 files changed, 164 insertions(+) create mode 100644 Cargo.toml create mode 100644 FFI.java create mode 100644 MainActivity.java create mode 100644 README create mode 100644 activity_main.xml create mode 100644 build_apk.sh create mode 100644 build_java.sh create mode 100755 build_ndk.sh create mode 100644 build_rust.sh create mode 100755 cargo.config create mode 100755 install_rust.sh create mode 100644 lib.rs-part0 create mode 100644 lib.rs-part1 create mode 100644 strings.xml diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..346a751 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[...] + +[target.'cfg(target_os="android")'.dependencies] +jni = { version = "0.5", default-features = false } + +[lib] +crate-type = ["dylib"] diff --git a/FFI.java b/FFI.java new file mode 100644 index 0000000..8104b2f --- /dev/null +++ b/FFI.java @@ -0,0 +1,10 @@ +package rs.cph.hellorust; + +public class FFI { + + private static native String func(final String pattern); + + public String runFunc(String to) { + return func(to); + } +} diff --git a/MainActivity.java b/MainActivity.java new file mode 100644 index 0000000..5ffaa29 --- /dev/null +++ b/MainActivity.java @@ -0,0 +1,22 @@ +package rs.cph.hellorust; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; + +public class MainActivity extends Activity { + + static { + System.loadLibrary("rustcode"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FFI rustcode = new FFI(); + String s = rustcode.runFunc("Sundkaj 7"); + ((TextView)findViewById(R.id.greetingField)).setText(s); + } +} diff --git a/README b/README new file mode 100644 index 0000000..fb8b67a --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +Files illustrating a minimal example of how to link Rust code from an android +java application. The pieces should be possible to puzzle together into a hello +world like example. + +These go together with the slides published at: +https://blag.netizen.se/posts/2020-03-22-rust-on-android.html + +As is told on above mentioned blog post, this is the material from a short +presentation I held a year ago on linking to Rust code from java on android. diff --git a/activity_main.xml b/activity_main.xml new file mode 100644 index 0000000..5bbe817 --- /dev/null +++ b/activity_main.xml @@ -0,0 +1,13 @@ + + + + diff --git a/build_apk.sh b/build_apk.sh new file mode 100644 index 0000000..ce9707a --- /dev/null +++ b/build_apk.sh @@ -0,0 +1,20 @@ +#!/bin/sh -e + +ANDROID_PATH="/usr/lib/android-sdk/" +BUILD_TOOLS="24.0.2" +AAPT="${ANDROID_PATH}/build-tools/${BUILD_TOOLS}/aapt" +DX="${ANDROID_PATH}/build-tools/${BUILD_TOOLS}/dx" +ZIPALIGN="${ANDROID_PATH}/build-tools/${BUILD_TOOLS}/zipalign" +APKSIGNER="/usr/bin/apksigner" +PLATFORM="${ANDROID_PATH}/platforms/android-24/android.jar" + +echo "Translating in Dalvik bytecode..." +$DX --dex --output=classes.dex obj + +echo "Making APK..." +$AAPT package -f -m -F bin/hello.unaligned.apk -M AndroidManifest.xml -S res -I $PLATFORM +$AAPT add bin/hello.unaligned.apk classes.dex lib/armeabi/librustcode.so + +echo "Aligning and signing APK..." +$APKSIGNER sign --ks ~/.android/keys/release-key.keystore bin/hello.unaligned.apk +$ZIPALIGN -f 4 bin/hello.unaligned.apk bin/hello.apk diff --git a/build_java.sh b/build_java.sh new file mode 100644 index 0000000..6fa5a4a --- /dev/null +++ b/build_java.sh @@ -0,0 +1,14 @@ +#!/bin/sh -e + +ANDROID_PATH="/usr/lib/android-sdk/" +BUILD_TOOLS="24.0.2" +AAPT="${ANDROID_PATH}/build-tools/${BUILD_TOOLS}/aapt" +PLATFORM="${ANDROID_PATH}/platforms/android-24/android.jar" +JAVAOPTS="-d obj -classpath src -bootclasspath $PLATFORM -source 1.7 -target 1.7" + +echo "Generating R.java file..." +$AAPT package -f -m -J src -M AndroidManifest.xml -S res -I $PLATFORM + +echo "Compiling..." +javac $JAVAOPTS src/rs/cph/hellorust/MainActivity.java +javac $JAVAOPTS src/rs/cph/hellorust/R.java diff --git a/build_ndk.sh b/build_ndk.sh new file mode 100755 index 0000000..694f466 --- /dev/null +++ b/build_ndk.sh @@ -0,0 +1,8 @@ +mkdir NDK +NDK_HOME="/usr/lib/android-ndk/" +API_LEVEL=24 +for ARCH in arm64 arm x86 +do + ${NDK_HOME}/build/tools/make_standalone_toolchain.py \ + --api ${API_LEVEL} --arch ${ARCH} --install-dir NDK/${ARCH} +done diff --git a/build_rust.sh b/build_rust.sh new file mode 100644 index 0000000..36cf2a8 --- /dev/null +++ b/build_rust.sh @@ -0,0 +1,11 @@ +#!/bin/sh -e + +for TARGET in aarch64-linux-android armv7-linux-androideabi +do + echo "Building rust lib for ${TARGET}" + (cd rustcode; cargo build --target ${TARGET} --release) +done + +ln -s "`pwd`"/rustcode/target/aarch64-linux-android/release/librustcode.so jniLibs/arm64/ +ln -s "`pwd`"/rustcode/target/armv7-linux-androideabi/release/librustcode.so jniLibs/armeabi/ +ln -s "`pwd`"/rustcode/target/i686-linux-android/release/librustcode.so jniLibs/x86/ diff --git a/cargo.config b/cargo.config new file mode 100755 index 0000000..5f09fe7 --- /dev/null +++ b/cargo.config @@ -0,0 +1,7 @@ +[target.armv7-linux-androideabi] +ar = "`pwd`/NDK/arm/bin/arm-linux-androideabi-ar" +linker = "`pwd`/NDK/arm/bin/arm-linux-androideabi-clang" + +[target.aarch64-linux-android ...] + +[target.i686-linux-android ...] diff --git a/install_rust.sh b/install_rust.sh new file mode 100755 index 0000000..69acd4f --- /dev/null +++ b/install_rust.sh @@ -0,0 +1,4 @@ +for TARGET in aarch64-linux-android armv7-linux-androideabi i686-linux-android +do + rustup target add ${TARGET} +done diff --git a/lib.rs-part0 b/lib.rs-part0 new file mode 100644 index 0000000..d2578cd --- /dev/null +++ b/lib.rs-part0 @@ -0,0 +1,13 @@ +use std::os::raw::{c_char}; +use std::ffi::{CString, CStr}; + +#[no_mangle] +pub extern fn rust_func(to: *const c_char) -> *mut c_char { + let c_str = unsafe { CStr::from_ptr(to) }; + let recipient = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + CString::new("Hello ".to_owned() + recipient).unwrap().into_raw() +} diff --git a/lib.rs-part1 b/lib.rs-part1 new file mode 100644 index 0000000..cec0883 --- /dev/null +++ b/lib.rs-part1 @@ -0,0 +1,21 @@ +#[cfg(target_os="android")] +#[allow(non_snake_case)] +pub mod android { + extern crate jni; + + use super::*; + use self::jni::JNIEnv; + use self::jni::objects::{JClass, JString}; + use self::jni::sys::{jstring}; + + #[no_mangle] + pub unsafe extern fn Java_rs_cph_hellorust_FFI_func(env: JNIEnv, _: JClass, java_pattern: JString) + -> jstring { + let world = rust_func(env.get_string(java_pattern).expect("invalid pattern string").as_ptr()); + // Retake pointer, to use it below and allow memory to be freed when it goes out of scope. + let world_ptr = CString::from_raw(world); + let output = env.new_string(world_ptr.to_str().unwrap()).expect("Couldn't create java string!"); + + output.into_inner() + } +} diff --git a/strings.xml b/strings.xml new file mode 100644 index 0000000..1fa2c41 --- /dev/null +++ b/strings.xml @@ -0,0 +1,5 @@ + + Hello Rust + Hello Rust! + MainActivity + -- cgit v1.2.3