建構系統支援透過 rust_bindgen 模組類型產生 bindgen 繫結。Bindgen 會為 C 程式庫提供 Rust FFI 繫結 (部分 C++ 支援,需要設定 cppstd 屬性)。

rust_bindgen 的基本用法

以下範例說明如何定義使用 bindgen 的模組,以及如何將該模組用作 Crate。如果您需要透過 include!() 巨集 (例如外部程式碼) 使用 bindgen 繫結,請參閱「來源產生器」頁面。

可從 Rust 呼叫的 C 程式庫範例

以下是定義在 Rust 中使用的結構體和函式的 C 程式庫範例。

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 繫結。
  • 可避免混合使用靜態和動態連結時發生的菱形連結問題。

通常,您應在與綁定項目相同的目錄樹狀結構中,為產生的模組提供安全的包裝函式程式庫,以供其他開發人員使用。如果這不適用於您的用途,您可以將其他套件新增至瀏覽權限。新增其他瀏覽範圍時,請注意不要新增兩個日後可能會連結至相同程序的範圍,否則連結可能會失敗。

值得注意的 rust_bindgen 屬性

除了適用於所有模組的重要通用屬性,下方定義的屬性也是如此。這些方法對 Rust bindgen 模組特別重要,或會顯示 rust_bindgen 模組類型的特殊行為。

stem、name、crate_name

rust_bindgen 會產生程式庫變化版本,因此這些變化版本會與 rust_library 模組共用 stemnamecrate_name 屬性的相同需求。如需參考資料,請參閱「值得注意的 Rust 程式庫屬性」。

wrapper_src

這是包裝函式標頭檔案的相對路徑,其中包含這些繫結所需的標頭。檔案副檔名會決定如何解讀標頭,並決定預設要使用的 -std 標記。這被假設為 C 標頭,「除非」擴充功能為 .hh.hpp。如果 C++ 標頭必須具有其他副檔名,請設定 cpp_std 屬性來覆寫預設行為,該行為會假設檔案是 C 檔案。

source_stem

這是產生的來源檔案的檔案名稱。即使您將繫結用於 Crate,也必須定義這個欄位,因為 stem 屬性只會控制產生版本的輸出檔案名稱。如果模組依附於多個來源產生器 (例如 bindgenprotobuf) 做為來源,而非透過 rustlibs 做為 Crate,則您必須確保所有來源產生器 (該模組的依附元件) 都有不重複的 source_stem 值。相依模組會將 srcs 中定義的所有 SourceProvider 依附元件來源複製到通用的 OUT_DIR 目錄,因此 source_stem 中的衝突會導致產生的來源檔案在 OUT_DIR 目錄中覆寫。

c_std

這是一個字串,代表要使用的 C-standard 版本。有效值如下:

  • 特定版本,例如 "gnu11"
  • "experimental"build/soong/cc/config/global.go 中建構系統定義的值,可能會在可用時使用草稿版本 (例如 C++1z)
  • 未設定或 "",表示應使用建構系統的預設值。

如果設定此值,系統會忽略檔案副檔名,並假設標頭為 C 標頭。這項屬性無法與 cpp_std 同時設定。

cpp_std

cpp_std 是字串,代表要使用的 C 標準版本。有效值:

  • 特定版本,例如 "gnu++11"
  • "experimental"build/soong/cc/config/global.go 中建構系統定義的值,可能會在可用時使用草稿版本 (例如 C++1z)
  • 未設定或 "",表示應使用建構系統的預設值。

如果設定此值,系統會忽略檔案副檔名,並假設標頭為 C++ 標頭。這項屬性無法與 c_std 同時設定。

cflags

cflags 會提供字串清單,其中包含正確解讀標頭所需的 Clang 標記。

custom_bindgen

對於進階用途,bindgen 可用作程式庫,提供可在自訂 Rust 二進位檔中操控的 API。custom_bindgen 欄位會採用 rust_binary_host 模組的模組名稱,該模組會使用 bindgen API,而非一般 bindgen 二進位檔。

這個自訂二進位檔必須預期引數的方式與 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: truestatic_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"],

        // The include path to the header file in the generated c file is
        // relative to build top.
        include_dirs: ["."],
    }