Android* 開発者向けラーニングシリーズ 8: インテル® プロセッサー向け Android* OS のビルド

同カテゴリーの次の記事

Android* 開発者向けラーニングシリーズ 9: SVG (Scalable Vector Graphics*) ライブラリーを使用してインテル® アーキテクチャー向け Android* でグラフィックをレンダリング

この記事は、インテル® デベロッパー・ゾーンに掲載されている「Intel for Android* Developers Learning Series #8: Building the Android* OS for Intel Processors」の日本語参考訳です。


現在、インテルから提供されているデバイスがないため、ここでは次の 2 つのエミュレーションされたターゲットのイメージを使用します: ターゲット用の QEMU ベースのエミュレーターのイメージと VirtualBox 用のターゲットのイメージ。(注: 詳細は、http://source.android.com/source/initializing.html (英語) と http://source.android.com/source/downloading.html (英語) を参照してください。これらのページは、ツールと環境に関する一般的な問題の解決に役立ちます。)

1.  Android* 用の GNU* コンパイラーによる Android* イメージのビルド

ARM* エミュレーター・イメージをビルドする手順は、そのままインテルの x86 ターゲットにも当てはまります。ここでは、この手順について簡単に説明します。

1.1.  ワークスペースの準備

$ mkdir <WORKSPACE_DIRECTORY>
$ cd <WORKSPACE_DIRECTORY>
$ repo init -u https://android.googlesource.com/platform/manifest -b android-4.1.1_r3

注: ほかのブランチ (例えば master など) を使用することもできます。

$ repo sync

1.2.  ビルド環境の設定


Android* のページでは full_x86 ターゲットについて触れていませんが (注: 詳細は、http://source.android.com/source/building.html (英語) を参照してください。)、引数なしで lunch コマンドを実行すると利用可能なターゲットが表示されます。
$ source build/envsetup.sh
$ lunch (または lunch full_x86-eng)

1.3.  イメージのビルド

並列ジョブ数を指定し、make コマンドを実行します。 

$ make –j <NUM_JOBS>

正しくビルドされたかを確認するには、emulator を起動します。図 8.1 のように、[Applications] > [Settings] > [About Phone] からシステム情報を表示します。

図 8.1   [About Phone] のシステム情報

Virtual Box* 用のイメージをビルドするには、vbox_x86-eng を指定して lunch コマンドを実行します。 

$ source build/envsetup.sh

$ lunch vbox_x86-eng

$ make -j <NUM_JOBS> android_disk_vdi

2. カーネルのビルド

ARM* エミュレーター用と x86 ターゲットのカーネルのビルド手順は異なります。正しくコンパイラーを指定しなければなりません。Android* アプリケーション・バイナリー・インターフェイス (ABI) と Linux* ABI は異なるため、Android* 用のデフォルトの GCC* コンパイラー 4.4.3 や 4.6 をそのままカーネルのビルドに使えません。重要な違いは、Android* システム向けのコンパイラーはデフォルトでメモリー位置に依存しないコード (PIC: Position Independent Code) を生成します。Android* 用の GNU* コンパイラーまたはインテル® C++ コンパイラーでカーネルをビルドする場合は、コンパイル時に –mno-android または –fno-PIC オプションを指定する必要があります。

ここでは、エミュレーター用のカーネルのビルド手順を簡単に示します。すでに <AOSP WORKSPACE> ディレクトリーでエミュレーターをビルド済みであると仮定します。ターゲット・コンパイラーはワークスペースから取得されます。

ワークスペースの準備:

$ git clone http://android.googlesource.com/kernel/goldfish.git goldfish-kernel

$ git branch goldfish remotes/origin/android-goldfish-2.6.29

$ git checkout goldfish

エミュレーター用のカーネルの設定: 

$ make ARCH=x86 goldfish_defconfig

Virtual Box 用のカーネルの設定:

$ make ARCH=x86 vbox_defconfig.

32 ビット・システム用のカーネルを明示的に指定するため、ARCH=x86 を追加しています。

これでカーネルをビルドする準備は整いました。次のセクションでは、2 つのコンパイラーを使用してカーネルをビルドします。

2.1. Android* 用 GNU* コンパイラーによるカーネルのビルド

カーネルをビルドするには、コンパイラー、各種ツール、リンカー、アセンブラーなどのパスを指定する必要があります。

すべてのパスを設定する最も簡単な方法は、CROSS_COMPILE 環境変数を定義することです。前述のように、PIC の生成を無効にするため –mno-android オプションも追加します。ここでは、CC 変数を再定義することで、コンパイラーが起動されるたびに –mno-andriod が使用されるようにします。

$ export CROSS_COMPILE=<AOSP WORKSPACE>/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.4.3/bin/i686-linux-android-

$ make ARCH=x86 CC=”${CROSS_COMPILE}gcc -mno-android” bzImage 

ビルドしたカーネルを使用してエミュレーターを実行します。

$ emulator –kernel arch/x86/boot/bzImage

新しいカーネルで Android* エミュレーターが実行されます (図 8.2 を参照)。

図 8.2   新しいカーネルで Android* エミュレーターが実行

Virtual Box* イメージを使用する場合は、新しいカーネルを make の引数に指定して、システムイメージ全体をリビルドする必要があります。

$ cd <AOSP WORKSPACE >;

$ rm out/target/product/vbox_x86/kernel

$ make -j <NUM_JOBS> LOCAL_KERNEL=<path to goldfish-kernel directory>/arch/x86/boot/bzImage android_disk_vdi

2.2. Android* 用のインテル® C++ コンパイラーによるカーネルのビルド

インテル® C++ コンパイラーのビルド手順はやや複雑です。ここでは、すでにエミュレーター用のカーネルが設定済みであると仮定します。

$ export CROSS_COMPILE=<AOSP WORKSPACE>/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.4.3/bin/i686-linux-android-

$ export ANDROID_GNU_X86_TOOLCHAIN=$(dirname $CROSS_COMPILE)/../

$ make ARCH=x86 CC=”<path to compiler>/icc -fno-PIC -falign-stack=assume-4-byte -diag-disable 47,1419,311,147,175 -mno-sse -mno-sse2″ bzImage

インテル® コンパイラーには GCC* のヘッダーファイルが必要なため、ANDROID_GNU_X86_TOOLCHAIN 環境変数は GCC* インストール・フォルダーのルートに設定します。次のオプションも追加します。

·       -fno-PIC: PIC 生成を無効にします。

·       -diag-disable 47,1419,311,147,175 : カーネルの makefile で –Werror オプションによりビルドが失敗しないようにします。インテル® コンパイラーは通常、GCC* コンパイラーよりも多くの警告を出力します。

·       -mno-sse -mno-sse2: arch/x86/boot サブディレクトリー以下の一部のファイルをコンパイルするのに必要です。これらのオプションは、ほとんどのソースファイルで指定されます。この問題については、すでにカーネル開発者に報告済みです。

verbose モードで新しいカーネルを使用してエミュレーターを実行する場合は、次のコマンドを使用します。 

$ emulator –kernel arch/x86/boot/bzImage –show-kernel

出力結果からこのカーネルは gcc 4.4.3 互換モードの icc でビルドされたことが分かります。

インテル® Atom™ プロセッサー向けのインテル固有の最適化を有効にするには、arch/x86/Makefile_32.cpu ファイルを変更する必要があります。

·       次の行を削除します: cflags-$(CONFIG_X86_GENERIC);

·       次の行を追加します: cflags-$(CONFIG_MCORE2) += $(call cc-option,-xSSSE3_ATOM)

残りのオプションに -xSSSE3_ATOM を追加するだけでは、–march=i686 のような GNU* コンパイラーのオプションによりオーバーライドされてしまいます。

3. Android* 用のインテル® C++ コンパイラーによるイメージのビルド

Android* ビルドシステムは、きめ細やかなコンポーネントの設定ができるように設計されています。例えば、コンポーネント単位でパフォーマンスを向上させる特別なコンパイラー・オプションを指定することができます。コンポーネントの Android.mk ファイルで LOCAL_CC 変数を定義することで、C コンパイラーをオーバーライドできます。唯一の制限は、コンパイラーがグローバル・コンパイラー・オプションをサポートしなければならないことです。コンパイラーがすべてのオプションをサポートしていない場合、Android* ビルドシステムで微調整が必要になります。

Android* ビルドシステムは、グローバルなコンパイラーの切り替えにも対応しており、特定のコンパイラーの調整は主に build/core/combo/TARGET_linux-x86.mk ファイルで行います。コンパイラー、アセンブラー、各種ツール、およびバイナリーツールのパスはここで指定されています。このファイルでは、デフォルトのコンパイラー・オプションも指定されています。

3.1. インテル® コンパイラーの統合

ここでは、インテル® コンパイラーをデフォルトに設定します。最初に、build/core/combo/TARGET_linux-x86.mk ファイルを変更します。

TARGET_TOOLCHAIN_ROOT 変数をコンパイラーのインストール・ディレクトリーのルートに設定します。 

TARGET_TOOLCHAIN_ROOT := prebuilts/icc

ここでは、インテル® コンパイラーが prebuilts/icc ディレクトリーにあると仮定します。環境変数 ANDROID_GNU_X86_TOOLCHAIN を GNU* ツールのトップレベル・ディレクトリーに初期化します。

export ANDROID_GNU_X86_TOOLCHAIN:=$(abspath prebuilts/gcc/$(HOST_PREBUILT_TAG)/x86/i686-android-linux-4.4.3)

ツールとコンパイラーのパスも変更する必要があります。

TARGET_TOOLS_PREFIX :=$(TARGET_TOOLCHAIN_ROOT)/bin/

TARGET_CC:=$(TARGET_TOOLS_PREFIX)icc$(HOST_EXECUTABLE_SUFFIX)

TARGET_CXX:=$(TARGET_TOOLS_PREFIX)icpc$(HOST_EXECUTABLE_SUFFIX)

TARGET_AR:=$(TARGET_TOOLS_PREFIX)xiar$(HOST_EXECUTABLE_SUFFIX)

TARGET_LD:=$(TARGET_TOOLS_PREFIX)xild$(HOST_EXECUTABLE_SUFFIX)

TARGET_OBJCOPY:=$(ANDROID_GNU_X86_TOOLCHAIN)/bin/i686-android-linux-objcopy$(HOST_EXECUTABLE_SUFFIX)

TARGET_STRIP:=$(ANDROID_GNU_X86_TOOLCHAIN)/bin/i686-android-linux-strip$(HOST_EXECUTABLE_SUFFIX)

TARGET_GLOBAL_CFLAGS 変数を確認し、GNU* コンパイラーのオプションを対応するインテル® コンパイラーのオプションに変更して、アーキテクチャー固有のオプションを追加します。

TARGET_GLOBAL_CFLAGS 変数を変更する際、clang コンパイラーのオプションも必ず変更してください。clang でインテル固有の –xSSSE3_ATOM オプションを削除するには、build/core/llvm_config.mk ファイルに行 $(call clang-flags-subst,-xSSSE3_ATOM) を挿入します。 

次に、インテル® コンパイラーのライブラリーに対応する事前ビルド済みコンポーネントを設定します。ここでは、libimf ライブラリーの共有バージョンについてのみ考えてみます。prebuilts/icc/Android.mk ファイルを作成し、次の行を追加します。

LOCAL_PATH := /

include $(CLEAR_VARS)

LOCAL_SYSTEM_SHARED_LIBRARIES:=

LOCAL_MODULE := libimf

LOCAL_MODULE_SUFFIX:=.so

LOCAL_STRIP_MODULE:=true

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE_CLASS := SHARED_LIBRARIES

LOCAL_SRC_FILES := $(shell env ANDROID_GNU_X86_TOOLCHAIN=$(ANDROID_GNU_X86_TOOLCHAIN) $(TARGET_CC) -print-file-name=libimf.so)

include $(BUILD_PREBUILT)

 

スタティック・ライブラリーの場合、LOCAL_MODULE_SUFFIX、LOCAL_SRC_FILESLOCAL_MODULE_CLASS は通常の方法で更新されるべきです。以前の Android* ビルドシステムは事前ビルド済みモジュール名の重複に正しく対応できないため、同じライブラリーの共有バージョンとスタティック・バージョンでは LOCAL_MODULE 変数で異なる値を使用することを推奨します。

LOCAL_PATH の値が通常とは異なります。ほとんどの場合、Android.mk ファイルの LOCAL_PATHmy-dir マクロの呼び出し $(call my-dir) に初期化されます。LOCAL_SRC_FILES 変数で指定されているライブラリーのパスが絶対パスであるため、LOCAL_PATH は ‘/’ に初期化されています。ライブラリーを手動でコピーしないで済むように、ライブラリーの場所を照会する -print-file-name オプションと一緒にコンパイラーを起動します。

多くの共有ライブラリーと静的にリンクされていない実行ファイルはインテルの共有ライブラリーに依存するため、依存ライブラリーがこれらを見つけられなければなりません。例えば、Virtual Box* イメージのインストーラーにはインテルのライブラリーが含まれており、これらを bootable/diskinstaller/config.mk ファイルに追加する必要があります。

installer_base_files += libimf libintlc libsvml 

build/core/combo/TARGET_linux-x86.mk ファイルの TARGET_DEFAULT_SYSTEM_SHARED_LIBRARIES 環境変数は、デフォルトでリンクされる共有ライブラリーのリストを定義します。インテルの共有ライブラリーはすべてこの変数に追加します。

イメージのビルドを開始する前に、bionic ディレクトリーのすべてのモジュールの LOCAL_CFLAGS 変数に –ffreestanding オプションを追加します。このオプションは、bionic ライブラリーとダイナミック・リンカーがコンパイラーのライブラリーに依存しないようにします。 

イメージをビルドします。

$ make –j 4 SHOW_COMMANDS=1

ビルドは恐らく失敗するでしょう。これには、いくつかの理由があります。1 つ目の理由は、リンク時に一部のコンポーネントがインテル® コンパイラーのライブラリーを必要とするためです。これは、インテル® コンパイラーのライブラリーを TARGET_DEFAULT_SYSTEM_SHARED_LIBRARIES に追加することでたいていは解決できますが、場合によっては解決できないこともあります。コンポーネントが静的にリンクされた実行ファイルの場合、またはコンポーネントがデフォルト設定をオーバーライドする場合は、インテル® コンパイラーのライブラリーを LOCAL_SYSTEM_SHARED_LIBRARIES 変数または LOCAL_STATIC_LIBRARIES 変数に追加する必要があります。

2 つ目の理由は、インテル® コンパイラーは GCC* コンパイラーよりも多くの警告を出力し、-Werror によりいくつかのコンポーネントがビルドに失敗するためです。-diag-disable オプションを使用して、build/core/combo/TARGET_linux-x86.mk ファイルの TARGET_GLOBAL_CFLAGS 変数で対応する警告を無効にします。

3 つ目の理由は、GNU* コンパイラーとインテル® コンパイラーのソース互換性の問題です。Android* ソースには、インテル® コンパイラーで許可されていない非標準の構造があります。我々の行った検証でこれらの問題を修正し、Google* に報告しました。また、GNU* コンパイラーとインテル® コンパイラーのバイナリー互換性の問題も見つかり、回避策をソースに加えました。

3.2. 柔軟なビルドシステムの設定

前述の統合の説明は単純明快ですが、コンポーネント用にコンパイラーを変更するにはいくつかの手順が必要なため、テストには適していません。そこで、何回かテストを行った後、コンポーネントごとにコンパイラーを再設定した、より簡単なものを実装しました。 

同様のシステムを LLVM clang コンパイラー向けの Android* ソースにも実装しました。clang でコンポーネントをコンパイルするには、LOCAL_CLANG 変数の値を “true” に設定するだけで済みます。clang を有効にすると、build ディレクトリーの次の 3 ファイルに影響します。

·       core/llvm_config.mk : clang 用の各種グローバル・パラメーターおよび関数が定義されています。

·       core/clear_vars.mk : LOCAL_CLANG 変数のデフォルト値が定義されています。

·       core/binary.mk : コンパイラーのオプション、リンカーのオプションなどに関連するすべてのロジックが実装されています。

clang コンパイラー向けの変更は一目瞭然なので、是非確認してください。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

関連記事