Building WASM, Android and iOS app with single/common RUST core code
Tags: rust, webassembly, android, ios
Published: 2018-12-12 19:46:22

I'll write below, the file required to write simple `hello world` code using RUST, that can be called by WASM/Android/iOS app, compiling and calling will follow in later article.. follow me to be notified..

I tested both `WASM`and `Android` and they are fine.

src folder tree:

    Hasans-Air:src h_ajsf$ tree .
    .
    ├── lib.rs
    ├── android
    │   └── mod.rs
    ├── ios
    │   └── mod.rs
    └── wasm
        └── mod.rs

    3 directories, 4 files

And the files now are:

Main:
```rust
// lib.rs
pub mod wasm;
pub mod ios;
pub mod android;

#[cfg(not(target_arch = "wasm32"))]
use std::os::raw::{c_char};

#[cfg(not(target_arch = "wasm32"))]
use std::ffi::{CStr};

pub fn rust_greetings(to: &str) -> String {
    format!("Hello {}", to)
}

#[cfg(not(target_arch = "wasm32"))]
fn char_str(pattern: *const c_char) -> &'static str {

    let c_str = unsafe { CStr::from_ptr(pattern) };
    let string_ptr = match c_str.to_str() {
        Err(_) => "there",
        Ok(string) => string,
    };
    string_ptr
}
```
WASM
```rust
// src/wasm/mod.rs
#[cfg(target_arch = "wasm32")]
pub mod wasm {
    use crate::rust_greetings;
    use wasm_bindgen::prelude::*;

    #[wasm_bindgen]
    pub fn wasm_greetings(to: &str) -> String {
        rust_greetings(to)
    }
}
```
Android
```rust
// src/android/mod.rs
#[cfg(target_os="android")]
#[allow(non_snake_case)]
pub mod android {
    use crate::rust_greetings;
    use crate::char_str;
    extern crate jni;

    use self::jni::JNIEnv;
    use self::jni::objects::{JClass, JString};
    use self::jni::sys::{jstring};

    #[no_mangle]
    pub unsafe extern fn Java_com_hasan_RustGreetings_greetings(env: JNIEnv, _: JClass, java_pattern: JString) -> jstring {

        let jvm_input = env.get_string(java_pattern)
                                        .expect("invalid pattern string").as_ptr();

        let input = rust_greetings(char_str(jvm_input));

        let output = env.new_string(input)
                                        .expect("Couldn't create java string!");
        output.into_inner()
    }
}
```
iOS
```rust
// src/ios/mod.rs
#[cfg(target_os="ios")]
#[no_mangle]
pub mod ios {
    use crate::char_str;

    use std::ffi::{CString, CStr};
    use std::os::raw::{c_char};
    use crate::rust_greetings;

    #[no_mangle]
    pub extern fn ios_greetings(to: *const c_char) -> *mut c_char {

        let input = rust_greetings(char_str(to));

        CString::new(input).unwrap().into_raw()
    }

    pub extern fn iso_greeting_free(s: *mut c_char) {
        unsafe {
            if s.is_null() { return }
            CString::from_raw(s)
        };
    }
}
```
TOML
```toml
# Cargo.toml
[package]
name = "greetings"
version = "0.1.0"
authors = ["Hasan Yousef"]
edition = "2018"

[dependencies]

[target.'cfg(target_arch="wasm32")'.dependencies]
wasm-bindgen = "0.2.29"

[target.'cfg(target_os="android")'.dependencies]
jni = { version = "0.5", default-features = false }

[lib]
name = "rust_greetings"
crate-type = ["cdylib", "dylib", "staticlib"] 
# Android: dylib
# iOS: cdylib [armv7s-apple-ios] and staticlib [Others]
# WASM: cdylib
```

Below is to manage the linker for compiling Android targets

Cargo config:
```config
# .cargo/config
[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"

[target.x86_64-linux-android]
ar = "NDK/x86/bin/x86_64-linux-android-ar"
linker = "NDK/x86_64/bin/x86_64-linux-android-clang"
```

Below are for running the WASM, which is already explained (here) [https://dev.to/h_ajsf/rust--wasm-using-bindgen-49b4]

webpack.config.js

```javascript
// webpack.config.js
const path = require("path");

module.exports = {
  entry: "./index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "index.js",
  },
  mode: "development"
};
```

package.json

```json
// package.json
{
  "name": "utils2",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.27.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  }
}
```

index.js

```javascript
import("./pkg/greetings").then(wasmModule => {
  let gr = wasmModule.wasm_greetings("Karam sweet")
  console.log(gr);
});
```

index.html

```html
<!DOCTYPE html>
<html>
<head>
    <script src="./index.js"></script>
    <head>
<body></body>
<html>
```