Soong ビルドシステム

Android 7.0 リリースより前、ビルドルールの記述と実行には GNU Make のみが使用されていました。Make ビルドシステムは広くサポートされ、使用されていますが、Android の基準では低速で、エラーが発生しやすく、スケーラブルでなく、テストが困難です。Soong ビルドシステムは、Android ビルドに必要な柔軟性をもたらします。

Soong とは何か

Soong ビルドシステムは、Android 7.0(Nougat)で Make に代わって導入されました。開発はまだ進行中です。

一般的な手順については、Android オープンソース プロジェクト(AOSP)の Android Make ビルドシステムの説明をご覧ください。Make から Soong への移行に必要な変更については、Android.mk 作成者のためのビルドシステムの変更点をご覧ください。

Soong 構成(ブループリント、.bp)ファイルの例については、シンプルなビルド構成をご覧ください。完全な詳細については、Soong リファレンス ファイルをご覧ください。

Android.bp ファイル形式

Android.bp ファイルの設計はシンプルです。条件文または制御フロー文は含まれません。すべての複雑性は、Go で記述されたビルドロジックによって処理されます。Android.bp ファイルの構文とセマンティクスは、可能な範囲で Bazel BUILD ファイル を模倣しています。

モジュール

Android.bp ファイル内のモジュールは、最初がモジュール タイプで、次に name: "value", 形式のプロパティのセットが続きます。

cc_binary {
        name: "gzip",
        srcs: ["src/test/minigzip.c"],
        shared_libs: ["libz"],
        stl: "none",
    }
    

すべてのモジュールには name プロパティが必要であり、値はすべての Android.bp ファイル全体で一意でなければなりません。ただし、名前空間と事前ビルド済みモジュールの プロパティ値は例外です(繰り返し可能です)。

有効なモジュール タイプとそのプロパティのリストについては、Soong モジュール リファレンスをご覧ください。

データの種類

変数とプロパティは厳格に型付けされます。変数は最初の割り当てに基づいて動的に型付けされ、プロパティはモジュール タイプによって静的に設定されます。サポートされるタイプは次のとおりです。

  • ブール値(true または false
  • 整数(int
  • 文字列("string"
  • 文字列のリスト(["string1", "string2"]
  • マップ({key1: "value1", key2: ["value2"]}

マップには、ネストされたマップを含む、任意の型の値を含めることが可能です。リストとマップでは、最後の値の後にカンマが付くことがあります。

glob

ファイルのリストを取得するプロパティ(srcs など)は glob パターンも取得できます。glob パターンには、通常の UNIX ワイルドカード * を使用できます(例: *.java)。glob パターンには、パス要素として単一の ** ワイルドカードを指定できます。これはゼロ個以上のパス要素に一致します。たとえば、java/**/*.javajava/Main.javajava/com/android/Main.java の両方に一致します。

変数

Android.bp ファイルには、以下のように最上位の変数割り当てが含まれる場合があります。

gzip_srcs = ["src/test/minigzip.c"],
    cc_binary {
        name: "gzip",
        srcs: gzip_srcs,
        shared_libs: ["libz"],
        stl: "none",
    }
    

変数のスコープは、変数が宣言されたファイルの残りの部分と、子ブループリント ファイルに限定されます。変数は 1 つの例外を除いて不変です。その例外とは、変数が参照される前に限り、+= による代入を使用して変数を先頭に付加できる場合です。

コメント

Android.bp ファイルには、C スタイルの複数行コメント(/* */)と C++ スタイルの単一行コメント(//)を記述できます。

演算子

+ 演算子を使用して、文字列、文字列のリスト、マップを先頭に付加できます。+ 演算子を使用して、整数を加算できます。マップを先頭に付加すると両方のマップのキーが結合され、両方のマップに存在するすべてのキーの値が先頭に付加されます。

条件文

Soong は、Android.bp ファイルの条件文をサポートしていません。代わりに、条件文を必要とするビルドルールの複雑性は Go で処理されます。Go では高度な言語機能を使用でき、条件文によって導入される暗黙的な依存関係をトラッキングできます。ほとんどの条件文は map プロパティに変換され、マップ内の値の 1 つが選択され、最上位のプロパティの先頭に付加されます。

たとえば、アーキテクチャ固有のファイルをサポートするには、次のようにします。

cc_library {
        ...
        srcs: ["generic.cpp"],
        arch: {
            arm: {
                srcs: ["arm.cpp"],
            },
            x86: {
                srcs: ["x86.cpp"],
            },
        },
    }
    

フォーマッタ

Soong には、gofmt に似たブループリント ファイル用の正規フォーマッタが含まれています。現在のディレクトリ内の Android.bp ファイルをすべて再帰的に再フォーマットするには、次のコマンドを実行します。

bpfmt -w .
    

正規フォーマットには、4 個のスペースのインデント、複数要素リストの各要素の後の改行、リストとマップの末尾のカンマなどがあります。

特殊モジュール

特殊モジュール グループには、固有の特徴を持つものがあります。

デフォルト モジュール

デフォルト モジュールを使用すると、複数のモジュールで同じプロパティを繰り返すことができます。以下は、その例です。

cc_defaults {
        name: "gzip_defaults",
        shared_libs: ["libz"],
        stl: "none",
    }

    cc_binary {
        name: "gzip",
        defaults: ["gzip_defaults"],
        srcs: ["src/test/minigzip.c"],
    }
    

事前ビルド済みモジュール

一部の事前ビルド済みモジュール タイプでは、ソースベースの対応するモジュールと同じ名前を使用できます。たとえば、同じ名前の cc_binary がすでに存在する場合、foo という名前の cc_prebuilt_binary が存在する可能性があります。これにより、デベロッパーは、最終プロダクトに含めるバージョンを柔軟に選択できます。ビルド構成に両方のバージョンが含まれている場合、事前ビルド済みモジュール定義の prefer フラグ値により、優先されるバージョンが決まります。一部の事前ビルド済みモジュールには、名前の先頭が prebuilt ではないものもあります(例: android_app_import)。

名前空間モジュール

Android が完全に Make を Soong に切り替えるまでは、Make プロダクト構成で PRODUCT_SOONG_NAMESPACES 値を指定する必要があります。その値は、Soong から Make にエクスポートされて m コマンドでビルドされる名前空間のスペース区切りリストでなければなりません。Android が Soong への変換を完了すると、名前空間を有効にするための詳細な仕様が変わる可能性があります。

Soong では、各モジュールが別々の名前空間内で宣言されていれば、異なるディレクトリ内のモジュールで同じ名前を指定できます。名前空間は次のようにして宣言できます。

soong_namespace {
        imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
    }
    

名前空間には name プロパティがなく、そのパスが自動的に名前として割り当てられます。

各 Soong モジュールには、ツリー内の場所に基づいて名前空間が割り当てられます。各 Soong モジュールは、現在のディレクトリまたは最も近い祖先ディレクトリの Android.bp ファイルにある soong_namespace で定義された名前空間に存在すると見なされます。そのような soong_namespace モジュールが見つからない場合、モジュールは暗黙的なルート名前空間に存在すると見なされます。

たとえば、名前空間 I1、I2、I3… をインポートする名前空間 N 内のモジュール M によって宣言された依存関係 D を Soong が解決しようとする例を考えてみましょう。

  1. D が //namespace:module の形式の完全修飾名である場合、指定されたモジュール名に対する指定された名前空間のみが検索されます。
  2. そうでない場合、Soong はまず、名前空間 N で宣言された D という名前のモジュールを探します。
  3. そのようなモジュールが存在しない場合、Soong は名前空間 I1、I2、I3… で D という名前のモジュールを探します。
  4. 最後に、Soong はルート名前空間を探します。