1*d4726bddSHONG Yifan# Rust FFI 2*d4726bddSHONG Yifan 3*d4726bddSHONG YifanIn case of an existing C++, Rust can call into the C++ function via FFI. 4*d4726bddSHONG YifanWith Bazel, this is straightforward. However, your C++ API needs an extern "C" 5*d4726bddSHONG Yifandeclaration to generate the C compatibility required for FFI. 6*d4726bddSHONG Yifan 7*d4726bddSHONG Yifan## Setup 8*d4726bddSHONG Yifan 9*d4726bddSHONG YifanThe setup is twofold, the Rust rules are declared in the MODULE file, 10*d4726bddSHONG Yifanbut the rules_cc are not yet available in the Bazelmod format and thus are declared in 11*d4726bddSHONG Yifanthe WORKSPACE.bzlmod file. 12*d4726bddSHONG Yifan 13*d4726bddSHONG YifanIn your MODULE.bazel file, ensure to have the following entry: 14*d4726bddSHONG Yifan 15*d4726bddSHONG Yifan```starlark 16*d4726bddSHONG Yifanmodule( 17*d4726bddSHONG Yifan name = "ffi", 18*d4726bddSHONG Yifan version = "0.0.0" 19*d4726bddSHONG Yifan) 20*d4726bddSHONG Yifan############################################################################### 21*d4726bddSHONG Yifan# B A Z E L C E N T R A L R E G I S T R Y # https://registry.bazel.build/ 22*d4726bddSHONG Yifan############################################################################### 23*d4726bddSHONG Yifan# https://github.com/bazelbuild/rules_rust/releases 24*d4726bddSHONG Yifanbazel_dep(name = "rules_rust", version = "0.46.0") 25*d4726bddSHONG Yifan 26*d4726bddSHONG Yifan############################################################################### 27*d4726bddSHONG Yifan# T O O L C H A I N S 28*d4726bddSHONG Yifan############################################################################### 29*d4726bddSHONG Yifan# Rust toolchain 30*d4726bddSHONG YifanRUST_EDITION = "2021" 31*d4726bddSHONG YifanRUST_VERSION = "1.79.0" 32*d4726bddSHONG Yifan 33*d4726bddSHONG Yifanrust = use_extension("@rules_rust//rust:extensions.bzl", "rust") 34*d4726bddSHONG Yifanrust.toolchain( 35*d4726bddSHONG Yifan edition = RUST_EDITION, 36*d4726bddSHONG Yifan versions = [RUST_VERSION], 37*d4726bddSHONG Yifan) 38*d4726bddSHONG Yifanuse_repo(rust, "rust_toolchains") 39*d4726bddSHONG Yifanregister_toolchains("@rust_toolchains//:all") 40*d4726bddSHONG Yifan``` 41*d4726bddSHONG Yifan 42*d4726bddSHONG YifanThen, create or open the WORKSPACE.bzlmod file and add the CC rules: 43*d4726bddSHONG Yifan 44*d4726bddSHONG Yifan```starlark 45*d4726bddSHONG Yifan############################################################################### 46*d4726bddSHONG Yifan# Bzlmod and WORKSPACE can work side by side, which allows migrating dependencies 47*d4726bddSHONG Yifan# from the WORKSPACE file to Bzlmod to be a gradual process. 48*d4726bddSHONG Yifan# https://bazel.build/external/migration#hybrid-mode 49*d4726bddSHONG Yifan############################################################################### 50*d4726bddSHONG Yifan# rule http_archive 51*d4726bddSHONG Yifanload("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 52*d4726bddSHONG Yifan 53*d4726bddSHONG Yifan# rules_cc 54*d4726bddSHONG Yifan# https://github.com/bazelbuild/rules_cc/releases 55*d4726bddSHONG Yifanhttp_archive( 56*d4726bddSHONG Yifan name = "rules_cc", 57*d4726bddSHONG Yifan urls = ["https://github.com/bazelbuild/rules_cc/releases/download/0.0.10-rc1/rules_cc-0.0.10-rc1.tar.gz"], 58*d4726bddSHONG Yifan sha256 = "d75a040c32954da0d308d3f2ea2ba735490f49b3a7aa3e4b40259ca4b814f825", 59*d4726bddSHONG Yifan) 60*d4726bddSHONG Yifan``` 61*d4726bddSHONG Yifan 62*d4726bddSHONG Yifan 63*d4726bddSHONG Yifan## C++ Target 64*d4726bddSHONG Yifan 65*d4726bddSHONG YifanAssuming you have a C++ library that defines a simple func() you declare it as a regular CC library in the BUILD file: 66*d4726bddSHONG Yifan 67*d4726bddSHONG Yifan```starlark 68*d4726bddSHONG Yifanload("@rules_cc//cc:defs.bzl", "cc_import", "cc_library") 69*d4726bddSHONG Yifan 70*d4726bddSHONG Yifancc_library( 71*d4726bddSHONG Yifan name = "nonstandard_name_cc_lib", 72*d4726bddSHONG Yifan srcs = ["c/cc_library.cc"], 73*d4726bddSHONG Yifan) 74*d4726bddSHONG Yifan``` 75*d4726bddSHONG Yifan 76*d4726bddSHONG YifanIn some cases, you have to deal with non standard naming. In that case you define a 77*d4726bddSHONG Yifancustom gen_rule to take of that and then define a cc_import. 78*d4726bddSHONG Yifan 79*d4726bddSHONG Yifan```starlark 80*d4726bddSHONG Yifanload("@rules_cc//cc:defs.bzl", "cc_import", "cc_library") 81*d4726bddSHONG Yifan 82*d4726bddSHONG Yifangenrule( 83*d4726bddSHONG Yifan name = "nonstandard_name_gen", 84*d4726bddSHONG Yifan srcs = [":nonstandard_name_cc_lib"], 85*d4726bddSHONG Yifan outs = ["nonstandard_name_gen.a"], 86*d4726bddSHONG Yifan # Copy the first member (libnonstandard_name_cc_lib.a) from the srcs to the 87*d4726bddSHONG Yifan # output nonstandard_name_gen.a. 88*d4726bddSHONG Yifan cmd = "cp $$(awk '{print $$1}' <<< '$(SRCS)') $@", 89*d4726bddSHONG Yifan) 90*d4726bddSHONG Yifan 91*d4726bddSHONG Yifancc_import( 92*d4726bddSHONG Yifan name = "static_cclib", 93*d4726bddSHONG Yifan static_library = "nonstandard_name_gen.a", 94*d4726bddSHONG Yifan) 95*d4726bddSHONG Yifan``` 96*d4726bddSHONG Yifan 97*d4726bddSHONG Yifan## Rust Callsite 98*d4726bddSHONG Yifan 99*d4726bddSHONG YifanOn the Rust side, interestingly, you just declare the cc_import as a dependency of 100*d4726bddSHONG Yifanyour Rust target. 101*d4726bddSHONG Yifan 102*d4726bddSHONG Yifan```starlark 103*d4726bddSHONG Yifanload("@rules_rust//rust:defs.bzl", "rust_shared_library") 104*d4726bddSHONG Yifan 105*d4726bddSHONG Yifan# A rust_shared_library (forcing the use of pic) that depends on a native 106*d4726bddSHONG Yifan# linker library with only a static_library member. 107*d4726bddSHONG Yifanrust_shared_library( 108*d4726bddSHONG Yifan name = "rust_shared_lib_with_static_dep", 109*d4726bddSHONG Yifan srcs = ["src/rust_shared_lib_with_static_dep.rs"], 110*d4726bddSHONG Yifan deps = [":static_cclib"], 111*d4726bddSHONG Yifan) 112*d4726bddSHONG Yifan``` 113*d4726bddSHONG Yifan 114*d4726bddSHONG YifanThen in your Rust source file, your create a FFI binding and wrap the call to it into unsafe. You can do that because the Rust standard library provides all the c raw types for FFI so you just import them and unsafe informs the Rust borrow checker to hold off certain checks. The public Rust function f() can then be used in regular Rust code. 115*d4726bddSHONG Yifan 116*d4726bddSHONG Yifan```rust 117*d4726bddSHONG Yifanuse std::os::raw::c_int; 118*d4726bddSHONG Yifan 119*d4726bddSHONG Yifanextern "C" { 120*d4726bddSHONG Yifan pub fn func() -> c_int; 121*d4726bddSHONG Yifan} 122*d4726bddSHONG Yifan 123*d4726bddSHONG Yifanpub fn f() { 124*d4726bddSHONG Yifan println!("hi {}", 125*d4726bddSHONG Yifan unsafe { 126*d4726bddSHONG Yifan func() 127*d4726bddSHONG Yifan } 128*d4726bddSHONG Yifan ); 129*d4726bddSHONG Yifan} 130*d4726bddSHONG Yifan``` 131*d4726bddSHONG Yifan 132*d4726bddSHONG YifanAnd with that, you build your FFI target as usual: 133*d4726bddSHONG Yifan 134*d4726bddSHONG Yifan`bazel build //...` 135*d4726bddSHONG Yifan 136*d4726bddSHONG Yifan 137*d4726bddSHONG Yifan 138