Giter Site home page Giter Site logo

afoxer-com / rsbind Goto Github PK

View Code? Open in Web Editor NEW
357.0 10.0 29.0 22.43 MB

Invoke rust services just like you write it in native language.

License: Apache License 2.0

Java 3.43% Swift 11.87% C 0.39% Shell 0.08% Rust 82.68% Ruby 1.55%
rust mobile android ios binding ffi uniffi

rsbind's Introduction

Test crates.io

Discord | QQ

Rsbind

Rsbind provide tools to bind rust trait with other language and export library artifact directly. It also help you invoke rust services just like you write it in native language.
It generate bindings from a Rust package and packaged to android aar or iOS lib or other library artifact. You don't need to write jni or other ffi code with this tool.

The tool may be useful for the people who want to use Rust as a cross-platform language and exporting multiple artifact for each platform.

Quick Start

Suppose you are writing two services in Rust and invoke from iOS and Android.
First you need write several Rust traits stands for your services.

pub trait Services: Send + Sync {
    fn get_login_service() -> Box<dyn LoginService>;
    fn get_upload_service() -> Box<dyn UploadService>;
}

pub trait LoginService: Send + Sync {
    fn login(&self, user_name: String, pwd: String) -> Box<dyn Future>;
}

pub trait Future: Send + Sync {
    fn get(&self) -> bool;
}

pub trait UploadService: Send + Sync {
    fn upload(&self, path: String, listener: Box<dyn UploadProgress>) -> i64;
}

pub trait UploadProgress : Send + Sync {
    fn on_progress(&self, id: i64, process: i64, total: i64);
}

Then you can implement your trait to achive your services.

pub struct ServiceHolder {}

impl Services for ServiceHolder {
    fn get_login_service() -> Box<dyn LoginService> {
        Box::new(LoginServiceImp {})
    }

    fn get_upload_service() -> Box<dyn UploadService> {
        Box::new(UploadServiceImp {})
    }
}

pub struct LoginServiceImp {}

impl LoginService for LoginServiceImp {
    fn login(&self, user_name: String, pwd: String) -> Box<dyn Future> {
        struct FutureImp {
            pub user_name: String,
            pub pwd: String,
        }
        impl Future for FutureImp {
            fn get(&self) -> bool {
                let handle = thread::spawn(move || {
                    // do your login
                    true
                });
                handle.join().unwrap()
            }
        }
        Box::new(FutureImp { user_name, pwd })
    }
}

pub struct UploadServiceImp {}

impl UploadService for UploadServiceImp {
    fn upload(&self, path: String, listener: Box<dyn UploadProgress>) -> i64 {
        thread::spawn(move || {
            // doing uploading
            listener.on_progress(99999, 10, 12345);
        });

        99999
    }
}

After that, run rsbind command to generate iOS and Android library artifact.

rsbind . android all
rsbind . ios all

Then with iOS library, you can invoke service from swift directly.

let loginService = RustLib.newServices().getLoginService();
let future = loginService.login(user_name: "sidney.wang", pwd: "88888888")
let result = future.get();
print("login result = \(result)")

class Listener : UploadProgress {
    func onProgress(id: Int64, process: Int64, total: Int64) {
        print("Progress is \(process)/\(total)")
    }
}
let uploadService = RustLib.newServices().getUploadService();
uploadService.upload(path: "to/your/path", listener: Listener())

In Android, it is very similar, just run java code.

LoginService loginService = RustLib.newServices().getLoginService();
Future future = loginService.login("sidney.wang", "88888888");
boolean result = future.get();
Log.i(TAG, "login result is " + result);

UploadService uploadService = RustLib.newServices().getUploadService();
uploadService.upload("to/your/path", new UploadProgress() {
    @Override
    public void onProgress(long id, long process, long total) {
        Log.i(TAG, "upload process is " + process);
    }
});

Step by step.

  1. Setup rust environment.
  2. Install 'rsbind'. cargo install --git https://github.com/rs-bind/rsbind.git --force -- rsbind
  3. Create a Rust library, which contains mod for exposing your services.
  • structures:
    Rsbind mod
    rsbind mod is where you expose your services.
    In this structure, api and implementation is all in one mod, rsbind will parse all trait and impl, and generate binding code. You need move unnecessary code into other files.

Maybe you want to split your api and implementation, then you can use these two structure.

  • First structure:
    alt First structure picture
  • Second structure:
    alt Second structure picture

You can put your interface to contract module and implemation to imp module. Expose these two modules in lib.rs.

// such as your code in contract dir as below:
pub trait YourContract : Send + Sync {
    fn test_simple(arg1: i32, arg2: String) -> String;
    fn test_callback(arg: Box<dyn Callback>);
    fn test_struct() -> StructSimple;
    fn test_return_callback() -> Box<dyn Callback>;
}

pub trait Callback : Send + Sync {
    fn on_callback(&self, arg1: i64, arg2: String);
}

pub struct StructSimple {
    pub arg3: String,
    pub arg4: bool,
}
// Your implementation is as below
pub struct YourImplemetation {}

impl YourContract for YourImplemetation {
    fn test_simple(arg1: i32, arg2: String) -> String {
        format!("Your test_simple result is {}_{}", arg1, arg2)
    }

    fn test_callback(arg: Box<dyn Callback>) {
        arg.on_callback(123i64, "hello callback".to_owned());
    }

    fn test_struct() -> StructSimple {
        StructSimple {
            arg1: "struct".to_owned(),
            arg2: true
        }
    }

    fn test_return_callback() -> Box<dyn Callback> {
        struct Instance{}
        impl Callback for Instance {
            fn on_callback(&self, arg1: i64, arg2: String) {

            }
        }
        Box::new(Instance{})
    }
}
  1. Run rsbind command as below. Then the generated code will be in _gen directory and aar/framework will be in target directory.

Rsbind usage:

rsbind path-of-project android/ios/mac/jar/all  ast/bridge/artifact/header/build/all
  • ast: generate simplified ast files with json format to _gen/ast.
  • bridge: generate c methods to expose our interface to _gen/[ios/android/mac/jar]_bridge.
  • artifact: generate java/swift wrapper and c header, and then put then into a project(_gen/[ios/android/mac/jar]_artifact).
  • build: build bridge modules and copy output to artifact project and then build artifact project.
  • all: run all the steps for binding.
  1. It will generate java files packaged in aar or cocoapods lib, then you can integrated them to your android/iOS project and call the functions. For android, you can call like as below:
YourContract instance = RustLib.newYourContract();
instance.testCallback(new Callback(){
       void onCallback(long arg1, String arg2) {
           // do your things.
       }
})

Swift is very similar.

Configuration

You can create a file named Rsbind.toml to add some configuration.

[android]
rustc_param = ""
arch = ["armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android"]
release = true
namespace = "com.afoxer.xxx.ffi"
so_name = "demo"
ext_lib = []
features_def = ["xxxx=[]"]
#contract_name = "android_api"
#imp_name = "android_imp"

[ios]
rustc_param = ""
arch = ["aarch64-apple-ios", "x86_64-apple-ios"]
release = true
features_def = []
#contract_name = "ios_api"
#imp_name = "ios_imp"

[mac]
rustc_param = ""
release = true
features_def = []
#contract_name = "mac_api"
#imp_name = "mac_imp"

[jar]
rustc_param = ""
release = true
namespace = "com.afoxer.xxx.ffi"
so_name = "demo"
#ext_lib = []
#features_def = ["xxxx=[]"]
#contract_name = "jar_api"
#imp_name = "jar_imp"

Supported Types

Trait:

  • Normal trait is a trait which functions have no &self parameter.
  • Callback is a trait which functions have &self parameter.
type return/argument Note
i8/u8 return and argument
i32/u32 return and argument
i64/u64 return and argument
f32 return and argument
f64 return and argument
bool return and argument
String return and argument
struct return and argument
Vec<i8/u8/i32/u32/i64/u64
/f32/f64/bool/String/struct>
return and argument
Box<dyn Callback> return and argument Callback is a trait which functions have &self.
Yes! you can pass callback in Callback itself

Struct can support all the types above except Callback.

It is different to define a callback and a normal trait. It should contains &self in every callback but not in normal trait.

Callback:

pub trait Callback : Send + Sync {
    fn on_callback(&self, arg1: i32, arg2: String, arg3: bool, arg4: f32, arg5: f64) -> i32;
    fn on_callback2(&self, arg1: bool) -> bool;
    fn on_callback_complex(&self, arg1: StructSimple) -> bool;
    fn on_callback_arg_vec(&self, arg1: Vec<StructSimple>) -> bool;
    fn on_callback_arg_vec_simple(&self, arg1: Vec<String>) -> bool;
}

Normal trait:

pub trait TestContract1 : Send + Sync {
    fn test_arg_vec(arg: Vec<String>) -> i32;
    fn test_return_vec(arg: u8) -> Vec<i32>;
    fn test_arg_callback(arg: Box<dyn Callback>) -> u8;
    fn test_bool(arg1: bool) -> bool;
    fn test_struct() -> StructSimple;
    fn test_struct_vec() -> Vec<StructSimple>;
}

rsbind's People

Contributors

afoxer-com avatar atul9 avatar sidneywang avatar wangxinsidney avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rsbind's Issues

llvm-strip error

process '"cargo" "rustc" "--target" "x86_64-linux-android" "--lib" "--target-dir" "target" "--release" "--" "-L" "target/cargo-apk-temp-extra-link-libraries"' finished with: exit status: 0
/usr/local/Caskroom/android-ndk/23b/android-ndk-r23b/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-strip: error: 'target/x86_64-linux-android/release/demo': No such file or directory
/usr/local/Caskroom/android-ndk/23b/android-ndk-r23b/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-strip: error: 'target/x86_64-linux-android/release/demo': No such file or directory
process '"/usr/local/Caskroom/android-ndk/23b/android-ndk-r23b/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-strip" "-s" "target/x86_64-linux-android/release/demo"' finished with: exit status: 1

对.a文件的体积影响很大

你好,我这边手头的核心代码本身生成的.a size在优化后是2.6mb, 发现用rsbind生成bridge代码后再统一生成.a文件 size直接就涨到9mb, 是否是引用了额外的std symbol导致的,请问有没有什么手段能把目前用不到的一些bridge能力和相关依赖去掉。。

运行rsbind . android all后报这个错误,是什么原因?

ndk是r23b
process '"/opt/Android/ndk-r21/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-strip" "-s" "target/armv7-linux-androideabi/release/libmy_project_android_bridge_prj.so"' finished with: exit status: 0
thread 'main' panicked at 'generate failed: Error(NdkBuild(Error(Ndk(UnsupportedTarget), State { next_error: None, backtrace: InternalBacktrace { backtrace: None } })), State { next_error: None, backtrace: InternalBacktrace { backtrace: None } })', rsbind/src/main.rs:68:10
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
谢谢了!

iOS Demo Failed

module compiled with Swift 4.2.1 cannot be imported by the Swift 5.0.1 compiler

"test_arg_callback"在android项目中调用出现闪退,其他方法都能正常调用

你好,我在android demo工程中尝试callback调用,必现闪退,其他test方法调用倒是ok的,调用代码和闪退信息如下:

`TestContract1.test_arg_callback(new Callback() {
@OverRide
public byte on_callback_u8(byte arg1) {
return 0;
}

        @Override
        public byte on_callback_i8(byte arg1) {
            return 0;
        }

        @Override
        public int on_callback(int arg1, String arg2, boolean arg3, float arg4, double arg5) {
            return 0;
        }

        @Override
        public boolean on_callback2(boolean arg1) {
            return false;
        }

        @Override
        public boolean on_callback_complex(StructSimple arg1) {
            return false;
        }

        @Override
        public boolean on_callback_arg_vec(StructSimple[] arg1) {
            return false;
        }

        @Override
        public boolean on_callback_arg_vec_simple(String[] arg1) {
            return false;
        }

        @Override
        public void on_empty_callback() {

        }
    });`

崩溃信息
A/libc: Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 10286 (ple.com.android), pid 10286 (ple.com.android)

He changed the configuration of the new edition.

https://developer.android.com/ndk/guides/other_build_systems

As of NDK r19, the toolchains installed by default with the NDK may be used in-place. The make_standalone_toolchain.py script is no longer needed for interfacing with arbitrary build systems.

To ensure that you build for the correct architecture, either pass the appropriate target with -target when invoking Clang, or invoke the target-prefixed Clang. For example, to compile for 64-bit ARM Android with a minSdkVersion of 21, either of the following will work and you may use whichever you find most convenient:

How can I debug Rust on Android

I'm trying to develop Android apps by Rust. But when I try to debug it, I can't find the direct way to touch this, only by command lines.
Is there any way more?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.