This is the fifth day of my participation in the August Wen Challenge.More challenges in August

0x01 Introduction

Rust is suddenly hot, running as fast as C++ and is rumored to be a possible replacement for C/C++. But that’s not what we’re talking about today. Let’s take a look at Rust’s powerful cross-compilation, building the SO library with Rust. Most of the operations on the network are under the Linux system, including official documents are also illustrated with the Linux system as an example. How to use Rust to build so libraries on Windows?

0x02 Tools

Main tools:

  1. Android Studio — A tool for writing Android Demos
  2. IDEA (VS Code also works) – a tool for writing the Rust language
  3. Python — Executes the Python script that generates the toolchain

0x03 Environment

  1. Install the Rust environment
  2. Installing Python
  3. Install the Android SDK and Android NDK environment

0x04 Code

New project

IDEA Install the Rust plug-in and create a project. If multiple words exist in the project name of the new project, you are advised to separate them with underscores (_), such as rust_jni_android, to prevent warning from being compiled later.

Add the dependent

Add JNI dependencies to Cargo. Toml and declare the crate_type of lib.rs to be cdylib. Tell the compiler to compile into a library. This will build dynamic libraries (.so,.dylib, or.dll files, depending on your operating system type).

[dependencies] # Jni = {version = "0.17.0", default-features = false} [lib] crate_type = ["cdylib"]Copy the code
Write the code

coding…

#! [cfg(target_os = "android")] #! [allow(non_snake_case)] use std::ffi::{CString, CStr}; use jni::JNIEnv; use jni::objects::{JObject, JString}; use jni::sys::{jstring}; #[no_mangle] pub unsafe extern fn Java_com_example_logproject_NativeMethodTest_hello(env: JNIEnv, _: JObject, j_recipient: JString) -> jstring { let recipient = CString::from( CStr::from_ptr( env.get_string(j_recipient).unwrap().as_ptr() ) ); let output = env.new_string("Hello ".to_owned() + recipient.to_str().unwrap()).unwrap(); output.into_inner() } #[no_mangle] pub unsafe extern fn Java_com_example_logproject_NativeMethodTest_init(env: JNIEnv, jclass: JObject) - > jstring {/ / will be a setEnable method is set to true the let clazz = env. Find_class (" com/example/logproject/SDK "). Unwrap (); env.call_static_method(clazz, "setEnable", "(Z)V", &[JValue::from(true)]); let str = "i'm a so by rust!" ; let str = env.new_string(str.to_owned()).unwrap(); str.into_inner() }Copy the code

0x04 Building

Prepare tool chain

Build the toolchain in Python. If the Android NDK is installed in the default path, create a new folder anywhere (no Chinese path is recommended) and use CMD or PowerShell to execute the following code. If the NDK installation location is not the default installation location, change the NDK_HOME address.

Note: API 29 in API 29 is the version of the API downloaded from the Android SDK. The androidx used in my project here has also downloaded the SDK of API 28, which can still run normally.

$NDK_HOME="$env:LOCALAPPDATA\Android\Sdk\ndk-bundle"
mkdir NDK
python "$NDK_HOME\build\tools\make_standalone_toolchain.py" --api 28 --arch arm64 --install-dir NDK/arm64
python "$NDK_HOME\build\tools\make_standalone_toolchain.py" --api 28 --arch arm --install-dir NDK/arm
python "$NDK_HOME\build\tools\make_standalone_toolchain.py" --api 28 --arch x86 --install-dir NDK/x86
Copy the code

The generation process takes about 2-3 minutes. Here are the results.

Create the Cargo configuration file

Go to the Cargo installation directory and check if there is a config file in the directory. There should be no config file by default. Note: Config files do not require extensions.Add the following to the config file,Change the paths in AR and Linker to those used to create the tool chain

[target.aarch64-linux-android]
ar = "E:\rust_jni\NDK\arm64\bin\aarch64-linux-android-ar.exe"
linker = "E:\rust_jni\NDK\arm64\bin\aarch64-linux-android-clang.cmd"
​
[target.armv7-linux-androideabi]
ar = "E:\rust_jni\NDK\arm\bin\arm-linux-androideabi-ar.exe"
linker = "E:\rust_jni\NDK\arm\bin\arm-linux-androideabi-clang.cmd"
​
[target.i686-linux-android]
ar = "E:\rust_jni\NDK\x86\bin\i686-linux-android-ar.exe"
linker = "E:\rust_jni\NDK\x86\bin\i686-linux-android-clang.cmd"
Copy the code

Install cross-compiled components

Executing the following code in CMD or PowerShell to support compilation for each platform will still take a few minutes. The mapping between platforms and Android NDK Filter is as follows: Aarch64-linux-android corresponds to ARM64-V8A armv7-linux-androideabi corresponds to Armeabi-v7A i686-linux-Android corresponds to x86

rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android
Copy the code
Generated so library

The three platforms require the following three commands to generate them separately. Here I have only tested so libraries that generate ARM64-V8A and Armeabi-V7a.

cargo build --target aarch64-linux-android --release
cargo build --target armv7-linux-androideabi --release
cargo build --target i686-linux-android --release
Copy the code

In order toarmeabi-v7aFor example, open the code just written in IDEA, enter the compile command in the console, and press Enter.

Where is the so library? antargetDirectory, I compiled twice here, so there are two compiled files

And then I’m expandingarmv7-linux-androideabi-release-deps-librust_jni_android.

Then copy to the Android project directory.

0x05 Finally

All the work is done, and the generated SO is ready to use. But I found that debugging so file is complicated, I have not found a good debugging method, if there is a good method, we can share.

Rust-jni :docs. Rs/JNI /0.17.0/… Building and Deploying aRust library on Android: mozilla. Making. IO/firefox – bro…