Build Android app with rust
Hasan Yousef
Posted on December 15, 2018
- Install rust
- Add $ANDROID_HOME, $NDK_HOME and $JAVA_HOME
Hasans-Air:~ h_ajsf$ sudo code $HOME/.bash_profile
// Write below
PATH=$PATH:/usr/local/Cellar/flutter/bin
PATH=$PATH:/Users/$USER/.cargo/bin
PATH=$PATH:${ANDROID_HOME}/tools/
export JAVA_HOME=$(/usr/libexec/java_home)
export ANDROID_HOME=/usr/local/share/android-sdk
export NDK_HOME=$ANDROID_HOME/ndk-bundle
// save and Exit
// Confirm changes
Hasans-Air:~ h_ajsf$ source $HOME/.bash_profile
- Add Android targets:
Hasans-Air:ndk h_ajsf$ rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android arm-linux-androideabi
For iOS, below targets are require:
Hasans-MacBook-Air:rust_lib_for_android h_ajsf$ rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios i386-apple-ios
- Create rust lib
Hasans-Air:greetings h_ajsf$ cargo new rust_lib_for_android --lib
Created binary (application) `cargo` project
Hasans-Air:greetings h_ajsf$ cd rust_lib_for_android
- Create chain tools Using rustup, like:
Hasans-MacBook-Air:~ h_ajsf$ rustup target add x86_64-linux-android
Hasans-MacBook-Air:~ h_ajsf$ rustup toolchain install stable-x86_64-linux-android
Hasans-MacBook-Air:rust_lib_for_android h_ajsf$ rustup override set stable-x86_64-linux-android
see (here)[https://forge.rust-lang.org/platform-support.html] for supported platforms.
Or, using Android NDK, as not all Tier 2 platforms are working fine with rustup tolchain
, NDK can be used as:
usage: make_standalone_toolchain.py [-h] --arch {arm,arm64,x86,x86_64}
[--api API] [--stl STL] [--force] [-v]
[--package-dir PACKAGE_DIR | --install-dir INSTALL_DIR]
Details commands are below:
Hasans-MacBook-Air:rust_lib_for_android h_ajsf$ ${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 28 --arch x86_64 --install-dir NDK/x86_64
Hasans-Air:rust_lib_for_android h_ajsf$ ${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 28 --arch arm64 --install-dir NDK/arm64
Hasans-Air:rust_lib_for_android h_ajsf$ ${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 28 --arch arm --install-dir NDK/arm
Hasans-Air:rust_lib_for_android h_ajsf$ ${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 28 --arch x86 --install-dir NDK/x86
For iOS,cargo-lipo
creates a universal library, and can be installed as below:
Hasans-MacBook-Air:rust_lib_for_android h_ajsf$ cargo install cargo-lipo
- Create /.cargo/config file
Hasans-Air:rust_lib_for_android h_ajsf$ mkdir .cargo
Hasans-Air:rust_lib_for_android h_ajsf$ cd .cargo
Hasans-Air:.cargo h_ajsf$ touch config
- Open the greetings folder in your favorite IFE, I'm using VS Code
Hasans-Air:.cargo h_ajsf$ cd ..
Hasans-Air:rust_lib_for_android h_ajsf$ cd ..
Hasans-Air:documents h_ajsf$ ls
rust_lib_for_android
Hasans-Air:documents h_ajsf$ code rust_app
- Add the below to the /.cargo/config file
[target.x86_64-linux-android]
ar = "NDK/x86/bin/x86_64-linux-android-ar"
linker = "NDK/x86_64/bin/x86_64-linux-android-clang"
[target.aarch64-linux-android]
ar = "NDK/arm64/bin/aarch64-linux-android-ar"
linker = "NDK/arm64/bin/aarch64-linux-android-clang"
[target.armv7-linux-androideabi]
ar = "NDK/arm/bin/arm-linux-androideabi-ar"
linker = "NDK/arm/bin/arm-linux-androideabi-clang"
[target.i686-linux-android]
ar = "NDK/x86/bin/i686-linux-android-ar"
linker = "NDK/x86/bin/i686-linux-android-clang"
- Replace the content of the src/lib.rs by the below:
use std::os::raw::{c_char};
use std::ffi::{CString, CStr};
#[no_mangle]
pub extern fn rust_greeting(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()
}
/// Expose the JNI interface for android below
#[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_com_mozilla_greetings_RustGreetings_greeting(env: JNIEnv, _: JClass, java_pattern: JString) -> jstring {
// Our Java companion code might pass-in "world" as a string, hence the name.
let world = rust_greeting(env.get_string(java_pattern).expect("invalid pattern string").as_ptr());
// Retake pointer so that we can 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()
}
}
- Add the below to the Cargo.toml
[target.'cfg(target_os="android")'.dependencies]
jni = { version = "0.5", default-features = false }
[lib]
name = "greetings"
crate-type = ["dylib"]
- Build the static library for each target
Hasans-MacBook-Air:rust_lib_for_android h_ajsf$ cargo build --target x86_64-linux-android --release
Hasans-MacBook-Air:rust_lib_for_android h_ajsf$ cargo build --target aarch64-linux-android --release
Hasans-MacBook-Air:rust_lib_for_android h_ajsf$ cargo build --target armv7-linux-androideabi --release
Hasans-MacBook-Air:rust_lib_for_android h_ajsf$ cargo build --target i686-linux-android --release
Note aarch64
can be used for both arm64
and arm64-v8a
For iOS, run cargo lipo --release
Hasans-MacBook-Air:rust_lib_for_android h_ajsf$ cargo lipo --release
Due to a known rustc issue, cargo-lipo can only be run on macOS. See https://github.com/rust-lang/rust/issues/36156#issuecomment-373201676 for more info.
Compiling cargo v0.1.0 (/Users/h_ajsf/Documents/greetings/rust_lib_for_android)
warning: dropping unsupported crate type `dylib` for target `aarch64-apple-ios`
warning: dropping unsupported crate type `cdylib` for target `aarch64-apple-ios`
Finished release [optimized] target(s) in 5.90s
Compiling cargo v0.1.0 (/Users/h_ajsf/Documents/greetings/rust_lib_for_android)
warning: dropping unsupported crate type `dylib` for target `armv7-apple-ios`
warning: dropping unsupported crate type `cdylib` for target `armv7-apple-ios`
Finished release [optimized] target(s) in 0.98s
Compiling cargo v0.1.0 (/Users/h_ajsf/Documents/greetings/rust_lib_for_android)
warning: dropping unsupported crate type `dylib` for target `i386-apple-ios`
warning: dropping unsupported crate type `cdylib` for target `i386-apple-ios`
Finished release [optimized] target(s) in 1.56s
Compiling cargo v0.1.0 (/Users/h_ajsf/Documents/greetings/rust_lib_for_android)
warning: dropping unsupported crate type `dylib` for target `x86_64-apple-ios`
warning: dropping unsupported crate type `cdylib` for target `x86_64-apple-ios`
Finished release [optimized] target(s) in 0.88s
And the universal iOS library can be found in cargo/target/universal/release/libgreetings.a
- For iOS:
- Add the
greetings.h
file, by:File\Add files to "Greetings"...
- Add native file
libgreetings.a
and the native interactive frameworklibresolv.tbd
byGeneral -> Linked Frameworks and Libraries
- Create bridging header
Greetings-Bridging-Header.h
byFile\New\File.... Header File
and import thegreetings.h
file into it so the file became as below:
#ifndef Greetings_Bridging_Header_h
#define Greetings_Bridging_Header_h
#import "greetings.h"
#endif
- Update the
Build Settings -> Objective-C Bridging Header
by adding the path ofGreetings-Bridging-Header.h
file - Update the
Build Settings -> Library Search Paths
by adding the path of importedlibgreetings.a
file - Create
RustGreetings
swift file byFile\New\File...
andiOS\Source\Swift File
, and add to it the:
class RustGreetings {
func sayHello(to: String) -> String {
let result = rust_greeting(to)
let swift_result = String(cString: result!)
rust_greeting_free(UnsafeMutablePointer(mutating: result))
return swift_result
}
}
- Update the
viewDidLoad
in theViewController.swift
file, by adding:
let rustGreetings = RustGreetings()
print("\(rustGreetings.sayHello(to: "world"))")
💖 💪 🙅 🚩
Hasan Yousef
Posted on December 15, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.