ソース生成ツール

このページでは、生成されたソースのサポートと、それをビルドシステムで使用する方法の概要について説明します。

すべてのソース生成ツールは、同様のビルドシステム機能を備えています。ビルドシステムでは、bindgen、AIDL インターフェース、および protobuf インターフェースを使用した C バインディングの生成が、ソース生成としてサポートされています。

生成されたソースからのクレート

ソースコードを生成するすべての Rust モジュールは、rust_library として定義された場合とまったく同じように、クレートとして使用できます(つまり rustlibsrlibsdylibs プロパティで依存関係として定義できます)。プラットフォーム コードでは、生成されたソースをクレートとして使用するのが最適な使用パターンです。生成されたソースの取り込みに include! マクロを使用することも可能ですが、このマクロの本来の目的は、external/ にあるサードパーティ コードをサポートすることです。

プラットフォーム コードで genrule モジュールを使用して独自の方法でソースを生成する場合など、生成されたソースを include!() マクロを介して使用することもまだあります。

include!() を使用して生成されたソースを含める

生成されたソースをクレートとして使用する方法については、各モジュール ページの例をご覧ください。このセクションでは、生成されたソースを include!() マクロを使用して参照する方法を説明します。このプロセスは、どのソース生成ツールでもほぼ同じです。

前提条件

この例では、rust_bindgen モジュール(libbuzz_bindgen)を定義し、include!() マクロを使用するために生成されたソースを含める手順に進める状態になっていることを前提としています。この条件を満たしていない場合は、rust_bindgen モジュールの定義に進んで libbuzz_bindgen を作成してから、ここに戻ってください。

なお、ビルドファイルのこの部分は、他のソース生成ツールにも使えます。

生成されたソースを含める手順

以下の内容で external/rust/hello_bindgen/Android.bp を作成します。

rust_binary {
   name: "hello_bzip_bindgen_include",
   srcs: [
         // The primary rust source file must come first in this list.
         "src/lib.rs",

         // The module providing the bindgen bindings is
         // included in srcs prepended by ":".
         ":libbuzz_bindgen",
    ],

    // Dependencies need to be redeclared when generated source is used via srcs.
    shared_libs: [
        "libbuzz",
    ],
}

以下の内容で external/rust/hello_bindgen/src/bindings.rs を作成します。

#![allow(clippy::all)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
#![allow(missing_docs)]

// Note that "bzip_bindings.rs" here must match the source_stem property from
// the rust_bindgen module.
include!(concat!(env!("OUT_DIR"), "/bzip_bindings.rs"));

以下の内容で external/rust/hello_bindgen/src/lib.rs を作成します。

mod bindings;

fn main() {
    let mut x = bindings::foo { x: 2 };
    unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}

生成されたソースにクレートを使用する理由

C/C++ コンパイラと異なり、rustc はバイナリまたはライブラリへのエントリ ポイントを表す単一のソースファイルのみを受け入れます。ソースツリーは、必要なソースファイルがすべて自動的に検出されるように構成されている必要があります。つまり、生成されたソースをソースツリーに配置するか、ソースの include ディレクティブを介して指定する必要があります。

include!("/path/to/hello.rs");

Rust コミュニティは、この違いに対応するために、build.rs スクリプトと Cargo ビルド環境に関する前提条件を利用しています。ビルド時に、cargo コマンドは OUT_DIR 環境変数を設定し、そこに build.rs スクリプトによって生成されたソースコードを配置することになっています。ソースコードを含めるには、次のコマンドを使用します。

include!(concat!(env!("OUT_DIR"), "/hello.rs"));

このコマンドは各モジュールの出力先が個別の out/ ディレクトリになるため、Soong はこの課題を克服する必要があります1。依存関係が生成したソースの出力先は、1 つの OUT_DIR ではありません。

AOSP プラットフォーム コードで、生成されたソースをインポート可能なクレートにパッケージ化するほうが望ましい理由は、次のとおりです。

  • 生成されたソースファイル名が競合しないようにする。
  • メンテナンスが必要なツリー全体でチェックインされるボイラープレート コードを減らす。生成されたソースのコンパイルをクレートにするために必要なボイラープレートを一元的に管理できます。
  • 生成されたコードと周辺のクレートの間の暗黙的な2 インタラクションを避ける。
  • よく使用される生成されたソースを動的にリンクすることで、メモリとディスクへの負荷が軽減される。

結果として、Android の Rust ソース生成モジュールのすべてのタイプでは、クレートとしてコンパイルして使用できるコードが生成されます。 生成されたソースのあるモジュールに対するすべての依存関係を、Cargo と同様に、単一のディレクトリへモジュールごとにコピーすれば、Soong はそのまま変更なしでサードパーティのクレートをサポートできます。このような場合、Soong は、モジュールのコンパイル時に OUT_DIR 環境変数をそのディレクトリに設定して、生成されたソースが見つかるようにします。 ただし前述の理由から、このメカニズムをプラットフォーム コードで使用するのは、絶対に必要な場合だけにすることをおすすめします。


  1. 生成されたソースへのパスがコンパイラに直接渡されるため、C/C++ や同様の言語では問題はありません。 

  2. include! はテキストを含めることができるので、包含する名前空間の値を参照したり、名前空間を変更したり、#![foo] のような構造を使用したりすることもあります。このような暗黙的なインタラクションの保守は困難な場合があります。他のクレートとのインタラクションがどうしても必要な場合は、できる限りマクロを使うようにします。