ビルドシステムは、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.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 モジュールは [":__subpackages__"] という visibility プロパティを持ちます。これにより、同じ Android.bp ファイル内のモジュールまたはディレクトリ階層にあるその下のモジュールのみがこのプロパティを参照できます。これには、次の 2 つの目的があります。
- ツリーの他の場所で未加工の C バインディングを使用することは推奨されない。
- 静的リンクと動的リンクが混在するダイヤモンド リンクの問題を回避できる。
通常は、生成されたモジュールの周囲に安全なラッパー ライブラリを提供し、他のデベロッパーが使用するバインディングと同じディレクトリ ツリーに追加しておく必要があります。この方法がユースケースで使用できない場合は、他のパッケージを visibility に追加してください。 visibility のスコープを追加する際は、今後、同じプロセスにリンクされる 2 つのスコープを追加しないように注意してください。リンクできなくなる可能性があります。
重要な rust_bindgen プロパティ
以下に定義されているプロパティは、すべてのモジュールに適用される重要な共通プロパティに加えて適用されます。これらは、Rust bindgen モジュールにとって特に重要なものか、rust_bindgen モジュール タイプに固有の動作を示すものです。
stem、name、crate_name
rust_bindgen はライブラリ バリアントを生成するため、stem、name、crate_name の各プロパティの rust_library モジュールと同じ要件を共有します。詳しくは、Rus ライブラリの重要なプロパティをご覧ください。
wrapper_src
このようなバインディングに必要なヘッダーを含むラッパー ヘッダー ファイルへの相対パスです。ファイル拡張子によって、ヘッダーの解釈方法とデフォルトで使用する -std フラグが決まります。拡張子が .hh または .hpp である場合を除き、C ヘッダーとみなされます。C++ ヘッダーに他の拡張子が必要な場合は、cpp_std プロパティを設定して、ファイルが C ファイルであると仮定するデフォルトの動作をオーバーライドします。
source_stem
生成されるソースファイルのファイル名です。バインディングをクレートとして使用する場合でも、stem プロパティは生成されるライブラリ バリアントの出力ファイル名のみを制御するため、このフィールドを定義する必要があります。
モジュールが rustlibs を介したクレートとしてではなく、ソースとして複数のソース生成ツール(bindgen や protobuf など)に依存している場合は、そのモジュールの依存関係にあるすべてのソース生成ツールが一意の 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"],
// The include path to the header file in the generated c file is
// relative to build top.
include_dirs: ["."],
}