构建系统支持通过 rust_bindgen
模块类型生成 bindgen 绑定。bindgen 为 C 库提供 Rust FFI 绑定(对 C++ 只能提供部分有限支持,需要设置 cppstd
属性)。
基本 rust_bindgen 用法
下面的示例展示了如何定义一个使用 bindgen 的模块,以及如何将该模块用作 crate。如果您需要通过 include!()
宏使用 bindgen 绑定(例如为了包含外部代码),请参阅源代码生成器页面。
要从 Rust 进行调用的 C 库示例
下面的 C 库示例定义了要在 Rust 中使用的结构体和函数。
external/rust/libbuzz/libbuzz.h
typedef struct foo {
int x;
} foo;
void fizz(int i, foo* cs);
external/rust/libbuzz/libbuzz.c
#include <stdio.h>
#include "libbuzz.h"
void fizz(int i, foo* my_foo){
printf("hello from c! i = %i, my_foo->x = %i\n", i, my_foo->x);
}
定义 rust_bindgen 模块
定义一个包含所有相关头文件的封装容器头文件 external/rust/libbuzz/libbuzz_wrapper.h
:
// Include headers that are required for generating bindings in a wrapper header.
#include "libbuzz.h"
将 Android.bp
文件定义为 external/rust/libbuzz/Android.bp
:
cc_library {
name: "libbuzz",
srcs: ["libbuzz.c"],
}
rust_bindgen {
name: "libbuzz_bindgen",
// Crate name that's used to generate the rust_library variants.
crate_name: "buzz_bindgen",
// Path to the wrapper source file.
wrapper_src: "libbuzz_wrapper.h",
// 'source_stem' controls the output filename.
// This is the filename that's used in an include! macro.
//
// In this case, we just use "bindings", which produces
// "bindings.rs".
source_stem: "bindings",
// Bindgen-specific flags and options to customize the bindings.
// See the bindgen manual for more information.
bindgen_flags: ["--verbose"],
// Clang flags to be used when generating the bindings.
cflags: ["-DSOME_FLAG"],
// Shared, static, and header libraries which export the necessary
// include directories must be specified.
//
// These libraries will also be included in the crate if static,
// or propagated to dependents if shared.
// static_libs: ["libbuzz"]
// header_libs: ["libbuzz"]
shared_libs: ["libbuzz"],
}
如需详细了解如何使用 bindgen 标志,请参阅有关自定义生成的绑定的 bindgen 手册部分。
如果您已使用过此部分定义 rust_bindgen
模块作为使用 include!()
宏的先决条件,请返回到“源代码生成器”页面的先决条件部分。如果没有,请继续后面的部分。
使用绑定作为 crate
请使用以下内容创建 external/rust/hello_bindgen/Android.bp
:
rust_binary {
name: "hello_bindgen",
srcs: ["main.rs"],
// Add the rust_bindgen module as if it were a rust_library dependency.
rustlibs: ["libbuzz_bindgen"],
}
请使用以下内容创建 external/rust/hello_bindgen/src/main.rs
:
//! Example crate for testing bindgen bindings
fn main() {
let mut x = buzz_bindgen::foo { x: 2 };
unsafe { buzz_bindgen::fizz(1, &mut x as *mut buzz_bindgen::foo) }
}
最后,调用 m hello_bindgen
以构建二进制文件。
测试 Bindgen 绑定
bindgen 绑定通常包含许多生成的布局测试,用于防止内存布局不匹配。AOSP 建议您为这些测试定义一个测试模块,并将这些测试作为项目常规测试套件的一部分运行。
可以通过在 external/rust/hello_bindgen/Android.bp
中定义 rust_test
模块,轻松为这些测试生成测试二进制文件:
rust_test {
name: "bindings_test",
srcs: [
":libbuzz_bindgen",
],
crate_name: "buzz_bindings_test",
test_suites: ["general-tests"],
auto_gen_config: true,
// Be sure to disable lints as the generated source
// is not guaranteed to be lint-free.
clippy_lints: "none",
lints: "none",
}
可见性和关联
生成的绑定通常非常小,因为它们由类型定义、函数签名和相关常量组成。因此,动态关联这些库通常会浪费时间和资源。我们已经停用了这些模块的动态关联,以便通过 rustlibs
使用它们会自动选择静态变体。
默认情况下,rust_bindgen
模块的 visibility
属性为 [":__subpackages__"]
,这将只允许同一 Android.bp
文件中的模块或目录层次结构中位于其下的模块查看。这有以下两个用途:
- 它不建议在树中的其他位置使用原始 C 绑定。
- 它可避免混合使用静态和动态关联时出现的菱形链接问题。
通常,您应该在生成的模块(您已将其添加到绑定所在的目录树中,供其他开发者使用)周围提供一个安全的封装容器库。如果这不适用于您的使用场景,您可以向 visibility 添加其他软件包。在添加其他可见性作用域时,请注意不要添加两个可能在日后关联到同一进程的作用域,因为这样做可能会导致无法关联。
重要的 rust_bindgen 属性
除了适用于所有模块的重要的通用属性之外,还有下面定义的一些属性。这些属性要么对 Rust bindgen 模块特别重要,要么具有特定于 rust_bindgen
模块类型的独有行为。
stem、name、crate_name
rust_bindgen
会生成库变体,因此它们对 stem
、name
和 crate_name
属性的要求与 rust_library
模块相同。有关参考信息,请参阅重要的 Rust 库属性。
wrapper_src
这是封装容器头文件的相对路径,该文件中包含这些绑定所需的头文件。文件扩展名决定了如何解读该头文件,也决定了默认使用的 -std
标志。除非扩展名为 .hh
或 .hpp
,否则系统会假定此文件是 C 头文件。如果您的 C++ 头文件必须使用其他扩展名,请设置 cpp_std
属性,以替换假定该文件为 C 文件的默认行为。
source_stem
这是生成的源代码文件的文件名。即使您使用绑定作为 crate,也必须定义此字段,因为 stem
属性只能控制生成的库变体的输出文件名。如果某个模块依赖于多个源代码生成器(例如 bindgen
和 protobuf
)提供源代码而不是通过 rustlibs
用作 crate,您必须确保作为该模块依赖项的所有源代码生成器都具有唯一的 source_stem
值。依赖这些依赖项的模块会将 srcs
中定义的所有 SourceProvider
依赖项中的源代码复制到通用的 OUT_DIR
目录,因此 source_stem
冲突会导致生成的源代码文件在 OUT_DIR
目录中被覆盖。
c_std
这是一个字符串,表示要使用的 C 标准版本。下面列出了有效的值:
- 特定版本,例如
"gnu11"
。 - 构建系统在
build/soong/cc/config/global.go
中定义的"experimental"
值,可在草稿版(例如 C++1z)发布后使用相应的版本。 - 未设置或
""
,这表示应使用构建系统的默认值。
如果设置了此属性,系统会忽略文件扩展名,而假定头文件是 C 头文件。此属性不能与 cpp_std
同时设置。
cpp_std
cpp_std
是一个字符串,表示要使用的 C 标准版本。有效值:
- 特定版本,例如
"gnu++11"
- 构建系统在
build/soong/cc/config/global.go
中定义的"experimental"
值,可在草稿版(例如 C++1z)发布后使用相应的版本。 - 未设置或
""
,这表示应使用构建系统的默认值。
如果设置了此属性,系统会忽略文件扩展名,而假定头文件是 C++ 头文件。此属性不能与 c_std
同时设置。
cflags
cflags
提供一个字符串列表,列出正确解读头文件所需的 Clang 标志。
custom_bindgen
在高级用例中,bindgen 可以用作库,提供可作为自定义 Rust 二进制文件一部分进行操作的 API。custom_bindgen
字段接受使用 bindgen API 而不是常规 bindgen
二进制文件的 rust_binary_host
模块的模块名称。
此自定义二进制文件必须采用与 bindgen
类似的方式接受参数,例如
$ my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]
其中大部分操作由 bindgen
库本身处理。如需查看此用法的示例,请访问 external/rust/crates/libsqlite3-sys/android/build.rs。
此外,整套库属性都可以用来控制库的编译,但通常不需要定义或更改这些属性。
handle_static_inline 和 static_inline_library
这两个属性旨在一起使用,并允许为可包含在导出的 bindgen 绑定中的静态内联函数生成封装容器。
如需使用它们,请设置 handle_static_inline: true
,并将 static_inline_library
设置为相应的 cc_library_static
,后者会将 rust_bindgen
模块定义为源输入。
用法示例:
rust_bindgen {
name: "libbindgen",
wrapper_src: "src/any.h",
crate_name: "bindgen",
stem: "libbindgen",
source_stem: "bindings",
// Produce bindings for static inline fucntions
handle_static_inline: true,
static_inline_library: "libbindgen_staticfns"
}
cc_library_static {
name: "libbindgen_staticfns",
// This is the rust_bindgen module defined above
srcs: [":libbindgen"],
include_dirs: ["src/"],
}