构建系统支持通过 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 会生成库变体,因此它们对 stemnamecrate_name 属性的要求与 rust_library 模块相同。有关参考信息,请参阅重要的 Rust 库属性

wrapper_src

这是封装容器头文件的相对路径,该文件中包含这些绑定所需的头文件。文件扩展名决定了如何解读该头文件,也决定了默认使用的 -std 标志。除非扩展名为 .hh.hpp,否则系统会假定此文件是 C 头文件。如果您的 C++ 头文件必须使用其他扩展名,请设置 cpp_std 属性,以替换假定该文件为 C 文件的默认行为。

source_stem

这是生成的源代码文件的文件名。即使您使用绑定作为 crate,也必须定义此字段,因为 stem 属性只能控制生成的库变体的输出文件名。如果某个模块依赖于多个源代码生成器(例如 bindgenprotobuf)提供源代码而不是通过 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/"],
    }