ビルドシステムは、rust_bindgen モジュール タイプによる bindgen バインディングの生成をサポートしています。Bindgen は、C ライブラリへの Rust FFI バインディングを提供します(一部の制限された C++ サポートでは、cppstd プロパティの設定が必要です)。

rust_bindgen の基本的な使用方法

以下は、bindgen を使用するモジュールを定義する方法と、そのモジュールをクレートとして使用する方法の例です。外部コードなど、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 マニュアルの Customizing Generated Bindings セクションをご覧ください。

このセクションで、include!() マクロを使用するための前提条件として rust_bindgen モジュールを定義した場合は、ソース生成ツールのページの前提条件に戻ります。そうでない場合は、次のセクションに進みます。

バインディングをクレートとして使用する

次の内容で 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.bprust_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 モジュールは [":__subpackages__"] という visibility プロパティを持ちます。これにより、同じ Android.bp ファイル内のモジュールまたはディレクトリ階層にあるその下のモジュールのみがこのプロパティを参照できます。これには、次の 2 つの目的があります。

  • ツリーの他の場所で未加工の C バインディングを使用することは推奨されない。
  • 静的リンクと動的リンクが混在するダイヤモンド リンクの問題を回避できる。

通常は、生成されたモジュールの周囲に安全なラッパー ライブラリを提供し、他のデベロッパーが使用するバインディングと同じディレクトリ ツリーに追加しておく必要があります。この方法がユースケースで使用できない場合は、他のパッケージを visibility に追加してください。 visibility のスコープを追加する際は、今後、同じプロセスにリンクされる 2 つのスコープを追加しないように注意してください。リンクできなくなる可能性があります。

重要な rust_bindgen プロパティ

以下に定義されているプロパティは、すべてのモジュールに適用される重要な共通プロパティに加えて適用されます。これらは、Rust bindgen モジュールにとって特に重要なものか、rust_bindgen モジュール タイプに固有の動作を示すものです。

stem、name、crate_name

rust_bindgen はライブラリ バリアントを生成するため、stemnamecrate_name の各プロパティの rust_library モジュールと同じ要件を共有します。詳しくは、Rus ライブラリの重要なプロパティをご覧ください。

wrapper_src

このようなバインディングに必要なヘッダーを含むラッパー ヘッダー ファイルへの相対パスです。ファイル拡張子によって、ヘッダーの解釈方法とデフォルトで使用する -std フラグが決まります。拡張子が .hh または .hpp である場合を除き、C ヘッダーとみなされます。C++ ヘッダーに他の拡張子が必要な場合は、cpp_std プロパティを設定して、ファイルが C ファイルであると仮定するデフォルトの動作をオーバーライドします。

source_stem

生成されるソースファイルのファイル名です。バインディングをクレートとして使用する場合でも、stem プロパティは生成されるライブラリ バリアントの出力ファイル名のみを制御するため、このフィールドを定義する必要があります。 モジュールが rustlibs を介したクレートとしてではなく、ソースとして複数のソース生成ツール(bindgenprotobuf など)に依存している場合は、そのモジュールの依存関係にあるすべてのソース生成ツールが一意の source_stem 値を持つようにする必要があります。依存モジュールは、srcs で定義されているすべての SourceProvider 依存関係から共通の OUT_DIR ディレクトリにソースをコピーするため、source_stem が競合すると、生成されるソースファイルが OUT_DIR ディレクトリで上書きされます。

c_std

使用する C 標準バージョンを表す文字列です。有効な値は以下のとおりです。

  • 特定のバージョン("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 バイナリの代わりに bindgen API を使用します。

このカスタム バイナリの引数は、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

この 2 つのプロパティは、合わせて使用するためのものであり、静的インライン関数のラッパーの生成を実現します(エクスポートされた 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/"],
    }