xref: /aosp_15_r20/external/bazelbuild-rules_rust/examples/bzlmod/ffi/README.md (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
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