Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support mixed Rust & C++ target and Cargo dependences #1799

Closed
xq114 opened this issue Nov 4, 2021 · 14 comments
Closed

Support mixed Rust & C++ target and Cargo dependences #1799

xq114 opened this issue Nov 4, 2021 · 14 comments

Comments

@xq114
Copy link
Contributor

xq114 commented Nov 4, 2021

你在什么场景下需要该功能?

https://cxx.rs/build/other.html

需要在c++中调用rust库/函数

描述可能的解决方案

target("rustlib")
	set_kind("static")
	add_files("src/*.rs")

target("foo")
	set_kind("binary")
	add_rules("rust.cxxbridge")
	add_files("bind/*.rs") -- generate *.rs.h and *.rs.cc, then compile *.rs.cc into the target
	add_deps("rustlib") -- link to rust library only
	add_files("src/*.cpp")

注意必须先生成所有的头文件完毕后再编译.cc文件,否则可能出现头文件缺失。如果使用buildcmd并行实现的话,中间的同步是个问题

其他信息

例子见 https://cxx.rs/tutorial.html#calling-a-rust-function-from-c

@waruqi
Copy link
Member

waruqi commented Nov 4, 2021

buildcmd并行实现的话,中间的同步是个问题

走before_buildcmd_file/before_build_file 不会有这个问题, 会提前执行

@waruqi waruqi added this to the v2.6.1 milestone Nov 5, 2021
@kassane
Copy link

kassane commented Nov 7, 2021

Perhaps it would be less complicated to adhere to a general version of what would most apply to xmake than to focus on one api only.
For example, try using cbindgen to transform the rust code into c or cpp, depending on how you would specify and then compile the project.

reference:
https://sixtyfps.io/blog/expose-rust-library-to-other-languages.html

@waruqi
Copy link
Member

waruqi commented Nov 9, 2021

What is the difference between cxxbridge and cbindgen?

@waruqi
Copy link
Member

waruqi commented Nov 9, 2021

Is there a minimal project example and makefile for rust and c/c++ mixed compilation?

@kassane
Copy link

kassane commented Nov 9, 2021

Difference:

  • cbindgen (translate rust to c/c++)[unsafe]
  • bindgen (translate c/c++ to rust) [unsafe]
  • cxxbridge is a lower level tool than the bindgens. Just as bindgen and cbindgen are built on top of extern "C", it makes sense to think about higher level tools built on top of CXX [safe] (c++ only)

@waruqi
Copy link
Member

waruqi commented Nov 9, 2021

Difference:

  • cbindgen (translate rust to c/c++)[unsafe]
  • bindgen (translate c/c++ to rust) [unsafe]
  • cxxbridge is a lower level tool than the bindgens. Just as bindgen and cbindgen are built on top of extern "C", it makes sense to think about higher level tools built on top of CXX [safe] (c++ only)

Thanks, that is to say, if it is only mixed with c++ code, should we use cxxbridge to provide better safe as much as possible?

@kassane
Copy link

kassane commented Nov 10, 2021

Using cxxbridge, you wouldn't have to worry about high-level translation, allowing you to interoperate with C++ ABI.

@waruqi
Copy link
Member

waruqi commented Nov 11, 2021

I tried to use cxxbridge to build an example of calling the rust library from c++, but it didn't work.

rust lib

- foo.rs
pub fn add(a: i32, b: i32) -> i32
{
    return a + b;
}

c++ binary

- bridge.rs
#[cxx::bridge]
mod foo {
    extern "Rust" {
        fn add(a: i32, b: i32) -> i32;
    }
}


- main.cc
#include <stdio.h>
#include "bridge.rs.h"

int main(int argc, char** argv)
{
    printf("add(1, 2) == %d\n", add(1, 2));
    return 0;
}

I run cxxbridge bridge.rs > bridge.rs.cc

it's content:

#include <cstdint>

extern "C" {
::std::int32_t cxxbridge1$add(::std::int32_t a, ::std::int32_t b
} // extern "C"

::std::int32_t add(::std::int32_t a, ::std::int32_t b) noexcept
  return cxxbridge1$add(a, b);
}

I try build and link bridge.rs.o and foo rust lib, it will raise errors:

error: Undefined symbols for architecture x86_64:
  "_cxxbridge1$add", referenced from:
      add(int, int) in bridge.rs.cc.o
ld: symbol(s) not found for architecture x86_64

Where is _cxxbridge1$add defined? Did I write foo.rs incorrectly?

@xq114
Copy link
Contributor Author

xq114 commented Nov 11, 2021

Where is _cxxbridge1$add defined? Did I write foo.rs incorrectly?

From the examples it seems that the implementation of the rust functions must be written along with their declaration in the bridge.rs file, see https://github.com/XiangpengHao/cxx-cmake-example/blob/master/rust_part/src/lib.rs

@waruqi
Copy link
Member

waruqi commented Nov 11, 2021

https://github.com/XiangpengHao/cxx-cmake-example/blob/master/rust_part/src/lib.rs

Putting it together still does not generate the definition of _cxxbridge1$add

@waruqi
Copy link
Member

waruqi commented Nov 11, 2021

#[cxx::bridge]
mod foo {
    extern "Rust" {
        fn add(a: i32, b: i32) -> i32;
    }
}

pub fn add(a: i32, b: i32) -> i32
{
    return a + b;
}

Maybe need use rustc to build crate:staticlib, libfoo.a, it will contain _cxxbridge1$add, but it need add some deps, e.g. cxx

$ /usr/local/bin/rustc -C opt-level=3 --crate-type=staticlib -o build/macosx/x86_64/release/libfoo.a src/foo.rs
error: error[E0433]: failed to resolve: use of undeclared crate or module `cxx`
 --> src/foo.rs:1:3
  |
1 | #[cxx::bridge]
  |   ^^^ use of undeclared crate or module `cxx`

@waruqi
Copy link
Member

waruqi commented Nov 12, 2021

I probably know how to support it, but first I need to let xmake support rust's dependency package integration.

At present, we can only pull the rust dependency library through cargo, but this is equivalent to having executed cargo build to build a complete rust project. This does not seem to require xmake to do anything.

@xq114
Copy link
Contributor Author

xq114 commented Nov 12, 2021

I probably know how to support it, but first I need to let xmake support rust's dependency package integration.

At present, we can only pull the rust dependency library through cargo, but this is equivalent to having executed cargo build to build a complete rust project. This does not seem to require xmake to do anything.

All right, the rust part can be built by invoking cargo, but the linkage of the produced library and existing C++ target is still on xmake. BTW, it would be good if rust libraries (for example rav1e https://github.com/xiph/rav1e , which has a set of C API) can be integrated in xmake-repo with something like import("package.tools.cargo").install(package)

@waruqi
Copy link
Member

waruqi commented Nov 13, 2021

I have supported it and support add cargo dependences to xmake targets

Add cargo package dependences

example: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cargo_deps

add_rules("mode.release", "mode.debug")
add_requires("cargo::base64 0.13.0")
add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}})

target("test")
    set_kind("binary")
    add_files("src/main.rs")
    add_packages("cargo::base64", "cargo::flate2")

Use cxxbridge to call rust in c++

example: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cxx_call_rust_library

add_rules("mode.debug", "mode.release")

add_requires("cargo::cxx 1.0")

target("foo")
    set_kind("static")
    add_files("src/foo.rs")
    set_values("rust.cratetype", "staticlib")
    add_packages("cargo::cxx")

target("test")
    set_kind("binary")
    add_rules("rust.cxxbridge")
    add_deps("foo")
    add_files("src/main.cc")
    add_files("src/bridge.rsx")

foo.rs

#[cxx::bridge]
mod foo {
    extern "Rust" {
        fn add(a: i32, b: i32) -> i32;
    }
}

pub fn add(a: i32, b: i32) -> i32 {
    return a + b;
}

bridge interface file in c++, bridge.rsx

#[cxx::bridge]
mod foo {
    extern "Rust" {
        fn add(a: i32, b: i32) -> i32;
    }
}

main.cc

#include <stdio.h>
#include "bridge.rs.h"

int main(int argc, char** argv) {
    printf("add(1, 2) == %d\n", add(1, 2));
    return 0;
}

Call c++ in rust

example: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/rust_call_cxx_library

add_rules("mode.debug", "mode.release")

target("foo")
    set_kind("static")
    add_files("src/foo.cc")

target("test")
    set_kind("binary")
    add_deps("foo")
    add_files("src/main.rs")

main.rs

extern "C" {
	fn add(a: i32, b: i32) -> i32;
}

fn main() {
    unsafe {
	    println!("add(1, 2) = {}", add(1, 2));
    }
}

foo.cc

extern "C" int add(int a, int b) {
    return a + b;
}

@waruqi waruqi changed the title support mixed rust & c++ target Support mixed rust & c++ target and cargo dependences Nov 13, 2021
@waruqi waruqi changed the title Support mixed rust & c++ target and cargo dependences Support mixed Rust & C++ target and Cargo dependences Nov 13, 2021
@waruqi waruqi closed this as completed Nov 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants