スタンドアロン ツールチェーン

Android NDK で提供されるツールチェーンを個別に使用するか、既存の IDE でプラグインとして使用することができます。 この柔軟性は、独自のビルドシステムが既にあり、ビルドシステムのサポートを Android に追加するためにクロスコンパイラーを呼び出す機能のみが必要な場合に有用です。

一般的なユースケースは、CC 環境変数のクロスコンパイラーを必要とするオープンソース ライブラリの設定スクリプトを呼び出すことです。

: このページでは、コンパイル、リンク、および低レベル アーキテクチャについて熟知していることを前提としています。 また、ほとんどのユースケースでは、このページで説明する手法は必要ありません。 ほとんどのケースでスタンドアロン ツールチェーンの使用を控え、NDK ビルドシステムを使用することをお勧めします。

ツールチェーンの選択

最初に、スタンドアロン ツールチェーンがターゲットとする処理アーキテクチャを決定する必要があります。 表 1 に示すように、各アーキテクチャは異なるツールチェーン名に対応しています。

表 1 さまざまな命令セット用の APP_ABI 設定。

アーキテクチャ ツールチェーン名
ARM ベース arm-linux-androideabi-<gcc-version>
x86 ベース x86-<gcc-version>
MIPS ベース mipsel-linux-android-<gcc-version>
ARM64 ベース aarch64-linux-android-<gcc-version>
X86-64 ベース x86_64-<gcc-version>
MIPS64 ベース mips64el-linux-android--<gcc-version>

Sysroot の選択

次に、sysroot(sysroot はターゲットのシステム ヘッダーやライブラリがあるディレクトリです)を定義する必要があります。 sysroot を定義するには、ネイティブ サポートのターゲットとする Android API レベルを知る必要があります。使用できるネイティブ API は、Android API レベルによって異なります。

Android API レベル向けのネイティブ API は、$NDK/platforms/ の下にあります。また、各 API レベル ディレクトリには、さまざまな CPU とアーキテクチャ用のサブディレクトリが含まれています。 次の例は、ARM アーキテクチャで、Android 5.0(API レベル 21)をターゲットとするビルド用に sysroot を定義する方法を示しています。

SYSROOT=$NDK/platforms/android-21/arch-arm
Android API レベルとそれらの API レベルでサポートされる各ネイティブ API の詳細については、Android NDK ネイティブ API をご覧ください。

コンパイラーの呼び出し

コンパイラーを呼び出す方法は 2 つあります。1 つの方法は簡単であり、ビルドシステムがほとんどの処理を実行します。 もう一方は高度な方法ですが、より複雑です。

簡単な方法

最も簡単なビルド方法は、コマンドラインから適切なコンパイラーを直接呼び出すことです。このとき、--sysroot オプションを使用して、ターゲットとするプラットフォームのシステム ファイルの場所を示します。 次に例を示します。

export CC="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/ \
linux-x86/bin/arm-linux-androideabi-gcc-4.8 --sysroot=$SYSROOT"
$CC -o foo.o -c foo.c

この方法は簡単ですが、柔軟性に欠けます。この方法では、C++ STL(STLport、libc++、または GNU libstdc++)を併用することができません。 また、例外または RTTI がサポートされません。

Clang の場合、追加の 2 つのステップを実行する必要があります。

    1. 表 2 に示すように、ターゲット アーキテクチャの適切な -target を追加します。
    2. 表 2 アーキテクチャと、-target の対応する値。

      アーキテクチャ
      armeabi -target armv5te-none-linux-androideabi
      armeabi-v7a -target armv7-none-linux-androideabi
      arm64-v8a -target aarch64-none-linux-android
      x86 -target i686-none-linux-android
      x86_64 -target x86_64-none-linux-android
      mips -target mipsel-none-linux-android
    3. 次の例のように、-gcc-toolchain オプションを追加することにより、アセンブラとリンカーのサポートを追加します。
    4. -gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64
      
    最終的に、Clang を使用してコンパイルするコマンドは、次のようになります。
    export CC="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/ \
    linux-x86/bin/arm-linux-androideabi-gcc-4.8 --sysroot=$SYSROOT" -target \
    armv7-none-linux-androideabi \
    -gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64"
    $CC -o foo.o -c foo.c
    

高度な方法

NDK は、コマンドラインからツールチェーンのカスタマイズしたインストールを実行できるようにする make-standalone-toolchain.sh シェル スクリプトを提供します。 このアプローチでは、簡単な方法で説明した手順よりも柔軟性が高くなります。

スクリプトは $NDK/build/tools/ ディレクトリにあります。ここで、$NDK は NDK のインストール ルートです。 このスクリプトの使用例は次のとおりです。

$NDK/build/tools/make-standalone-toolchain.sh \
--arch=arm --platform=android-21 --install-dir=/tmp/my-android-toolchain

このコマンドは、/tmp/my-android-toolchain/ という名前のディレクトリを作成します。このディレクトリには、android-21/arch-arm sysroot のコピーと 32 ビット ARM アーキテクチャのツールチェーン バイナリのコピーが含まれます。

ツールチェーン バイナリはホスト固有のパスに依存していない、またはそのパスを含んでいないことに注意してください。つまり、ツールチェーン バイナリはどこにでもインストールでき、必要に応じて、移動することもできます。

ビルドシステムはデフォルトで ARM ベースの 32 ビット GCC 4.8 ツールチェーンを使用します。ただし、--arch=<toolchain> をオプションとして指定することにより、異なる値を指定することができます。表 3 は、他のツールチェーンに使用する値を示しています。

表 3 ツールチェーンと、--arch を使用した対応する値。

ツールチェーン
mips64 コンパイラー --arch=mips64
mips GCC 4.8 コンパイラー --arch=mips
x86 GCC 4.8 コンパイラー --arch=x86
x86_64 GCC 4.8 コンパイラー --arch=x86_64
mips GCC 4.8 コンパイラー --arch=mips

または、--toolchain=<toolchain> オプションを使用することもできます。表 4 は、<toolchain> に対して指定できる値を示しています。

表 4 ツールチェーンと、--toolchain を使用した対応する値。

ツールチェーン
arm
  • --toolchain=arm-linux-androideabi-4.8
  • --toolchain=arm-linux-androideabi-4.9
  • --toolchain=arm-linux-android-clang3.5
  • --toolchain=arm-linux-android-clang3.6
  • x86
  • --toolchain=x86-linux-android-4.8
  • --toolchain=x86-linux-android-4.9
  • --toolchain=x86-linux-android-clang3.5
  • --toolchain=x86-linux-android-clang3.6
  • mips
  • --toolchain=mips-linux-android-4.8
  • --toolchain=mips-linux-android-4.9
  • --toolchain=mips-linux-android-clang3.5
  • --toolchain=mips-linux-android-clang3.6
  • arm64
  • --toolchain=aarch64-linux-android-4.9
  • --toolchain=aarch64-linux-android-clang3.5
  • --toolchain=aarch64-linux-android-clang3.6
  • x86_64
  • --toolchain=x86_64-linux-android-4.9
  • --toolchain=x86_64-linux-android-clang3.5
  • --toolchain=x86_64-linux-android-clang3.6
  • mips64
  • --toolchain=mips64el-linux-android-4.9
  • --toolchain=mips64el-linux-android-clang3.5
  • --toolchain=mips64el-linux-android-clang3.6
  • 注: 表 4 はすべてを網羅したリストではありません。その他の組み合わせも使用できますが、未検証です。

    以下の 2 つの方法のいずれかを使用して、Clang/LLVM 3.6 をコピーすることもできます。--toolchain オプションに -clang3.6 を付加することにより、--toolchain オプションを次の例のようにします。

    --toolchain=arm-linux-androideabi-clang3.6
    

    コマンドラインで -llvm-version=3.6 を別個のオプションとして追加することもできます。

    注: 特定のバージョンを指定する代わりに、<version> を使用することもできます。この場合、デフォルトで Clang の利用可能な最新バージョンが指定されます。

    デフォルトでは、ビルドシステムは 32 ビット ホスト ツールチェーン向けにビルドします。64 ビット ホスト ツールチェーンを指定することもできます。 表 5 は、異なるプラットフォームで -system とともに使用する値を示しています。

    表 5 ホスト ツールチェーンと、-system を使用した対応する値。

    ホスト ツールチェーン
    64 ビット Linux -system=linux-x86_64
    64 ビット MacOSX -system=darwin-x86_64
    64 ビット Windows -system=windows-x86_64
    64 ビットまたは 32 ビット命令のホスト ツールチェーンの指定に関する詳細については、64 ビットおよび 32 ビット ツールチェーンをご覧ください。

    --stl=stlport を指定して、デフォルトの libgnustl ではなく、libstlport をコピーすることができます。 このようにする場合、共有ライブラリにリンクするときは、明示的に -lstlport_shared を使用する必要があります。 この要件は、GNU libstdc++ に対して -lgnustl_shared を使用する必要があることと類似しています。

    同様に、--stl=libc++ を指定して、LLVM libc++ ヘッダーとライブラリをコピーすることができます。共有ライブラリにリンクするには、明示的に -lc++_shared を使用する必要があります。

    次の例のように、これらの設定を直接行うことができます。

    export PATH=/tmp/my-android-toolchain/bin:$PATH
    export CC=arm-linux-androideabi-gcc   # or export CC=clang
    export CXX=arm-linux-androideabi-g++  # or export CXX=clang++
    

    -install-dir オプションを省略すると、make-standalone-toolchain.sh シェル スクリプトが圧縮ファイルを tmp/ndk/<toolchain-name>.tar.bz2 に作成することに注意してください。 この圧縮ファイルによりアーカイブが容易になり、バイナリを簡単に再配布できるようになります。

    このスタンドアロン ツールチェーンには追加のメリットがあり、C++ STL ライブラリの作業コピーと実際に使用できる例外および RTTI サポートも含まれています。

    その他のオプションと詳細については、--help を使用してください。

    Clang の使用

    --llvm-version=<version> オプションを使用して、スタンドアロン インストールで Clang バイナリをインストールすることができます。<version> は、3.53.6 などの LLVM/Clang バージョン番号です。 次に例を示します。

    build/tools/make-standalone-toolchain.sh \
    --install-dir=/tmp/mydir \
    --toolchain=arm-linux-androideabi-4.8 \
    --llvm-version=3.6
    

    Clang バイナリは同じアセンブラ、リンカー、ヘッダー、ライブラリ、および C++ STL 実装に依存しているため、GCC バイナリとともにコピーされることに注意してください。

    また、この操作により、<install-dir>/bin/@ の下に clang および clang++ という名前の 2 つのスクリプトがインストールされます。 これらのスクリプトは、デフォルトのターゲット アーキテクチャ フラグを指定して実際の clang バイナリを呼び出します。 つまり、これらのスクリプトは変更なしで機能し、これらのスクリプトを指すように CC および CXX 環境変数を設定するだけで、独自のビルドでこれらのスクリプトを使用できるようになるはずです。

    Clang の呼び出し

    llvm-version=3.6 でビルドした ARM スタンドアロン インストールでは、Unix システム上の Clang の呼び出しを 1 行で行えます。 次に例を示します。

    `dirname $0`/clang36 -target armv5te-none-linux-androideabi "$@"
    

    clang++ は同じように clang++31 を呼び出します。

    ARM での Clang のターゲット

    ARM 向けにビルドするとき、Clang は、-march=armv7-a または -mthumb オプションの有無に基づいてターゲットを変更します。

    表 5 指定可能な -march 値とその結果のターゲット。

    -march 結果のターゲット
    -march=armv7-a armv7-none-linux-androideabi
    -mthumb thumb-none-linux-androideabi
    -march=armv7-a-mthumb の両方 thumbv7-none-linux-androideabi

    必要に応じて、独自の -target でオーバーライドすることもできます。

    スタンドアロン パッケージでは、Clang は事前定義された相対的な場所で asld を検索するため、-gcc-toolchain は必要ありません。

    Makefile で clangclang++ を、gccg++ に容易に置換できるはずです。 疑わしい場合は、次のオプションを追加して、これらが適切に機能することを確認してください。

    • -v: コンパイラーのドライバ問題と関連するコマンドを出力します。
    • -###: 明示的に事前定義されたオプションなどのコマンドライン オプションを出力します。
    • -x c < /dev/null -dM -E: 事前定義されたプリプロセッサ定義を出力します。
    • -save-temps: プリプロセッサで処理された *.i または *.ii ファイルを比較します。

    Clang の詳細については、http://clang.llvm.org/ にある GCC の互換性のセクションを参照してください。

    ABI の互換性

    ARM ツールチェーンが生成するマシンコードは、デフォルトで公式 Android armeabi ABI と互換性がある必要があります。

    -mthumb コンパイラー フラグを使用して、16 ビット Thumb-1 命令(デフォルトは 32 ビット ARM 命令)を強制的に生成することをお勧めします。

    armeabi-v7a ABI をターゲットにする場合は、次のフラグを設定する必要があります。

    CFLAGS= -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
    

    最初のフラグは Thumb-2 命令を有効にします。2 番目のフラグはハードウェア FPU 命令を有効にし、ABI の互換性に不可欠であるコアレジスターの浮動小数点パラメータが渡されるようにします。

    注: r9b よりも前のバージョンの NDK では、これらのフラグを個別に使用しないでください。 すべてのフラグを設定するか、フラグを設定しないでください。そうしないと、予期しない動作が発生したり、クラッシュする可能性があります。

    NEON 命令を使用するには、-mfpu コンパイラー フラグを変更する必要があります。

    CFLAGS= -march=armv7-a -mfloat-abi=softfp -mfpu=neon
    

    この設定は ARM 仕様に従って VFPv3-D32 の使用を強制することに注意してください。

    また、次の 2 つのフラグをリンカーに指定する必要があります。

    LDFLAGS= -march=armv7-a -Wl,--fix-cortex-a8
    

    最初のフラグは、armv7-a 向けに調整された libgcc.alibgcov.a、および crt*.o を選択するようにリンカーに指示します。 2 番目のフラグは、一部の Cortex-A8 実装の CPU バグを回避するために必要です。

    NDK バージョン r9b 以降では、double 値または float 値を取る、または返す Android ネイティブ API すべてが ARM 向けの attribute((pcs("aapcs"))) を備えています。 これにより、-mhard-float(暗黙的に -mfloat-abi=hard になる)でユーザーコードをコンパイルし、softfp ABI に準拠する Android ネイティブ API に引き続きリンクすることが可能になります。 詳細については、$NDK/tests/device/hard-float/jni/Android.mk のコメントをご覧ください。

    x86 で NEON intrinsic を使用すると、ビルドシステムは標準の ARM NEON intrinsic ヘッダーと同じ名前 arm_neon.h を持つ C/C++ 言語の特別なヘッダーを使用して、NEON intrinsic をネイティブ x86 SSE intrinsic に変換します。

    デフォルトでは、x86 ABI は SSSE3 までの SIMD をサポートし、ヘッダーは最大 93%(2,009 個のうち 1,869 個)の NEON 関数をカバーします。

    MIPS ABI をターゲットにする場合は、特定のコンパイラー フラグを使用する必要はありません。

    ABI サポートの詳細については、x86 サポートをご覧ください。

    警告と制限事項

    Windows サポート

    Windows バイナリは Cygwin に依存していません。依存関係がないため、動作が高速になります。ただし、Windows バイナリの場合、C:/foo/bar とは異なる cygdrive/c/foo/bar などの Cygwin パス仕様を認識しないというデメリットがあります。

    NDK ビルドシステムでは、Cygwin からコンパイラーに渡されるすべてのパスが自動的に変換され、その他の複雑な処理も管理されます。 カスタム ビルドシステムを使用している場合は、これらの複雑な処理を自分で解決する必要があります。

    Cygwin/MSys のサポートに関する詳細については、android-ndk のフォーラムをご覧ください。

    wchar_t サポート

    Android 2.3(API レベル 9)よりも前のバージョンでは、Android プラットフォームは wchar_t を実際にサポートしていませんでした。これにより、次のような結果がもたらされます。

    • Android 2.3 以降のプラットフォームをターゲットにしていて、wchar_t のサイズが 4 バイトの場合、C ライブラリでほとんどの wide-char 関数が使用できます(マルチバイトのエンコードおよびデコード関数と wsprintf / wsscanf は例外です)。
    • 低い API レベルをターゲットにしていて、wchar_t のサイズが 1 バイトの場合、wide-char 関数は機能しません。

    wchar_t 型との依存関係を取り除き、より適切な表記に切り替えることをお勧めします。 Android では、既存のコードの移行のみをサポートします。

    例外、RTTI、STL

    ツールチェーン バイナリはデフォルトで C++ 例外 と RTTI をサポートします。ソースをビルドするときに C++ 例外と RTTI を無効にするには(たとえば、より軽量のマシンコードを生成するために)、-fno-exceptions-fno-rtti を使用します。

    これらの機能と GNU libstdc++ を併用するには、libsupc++ に明示的にリンクする必要があります。これを行うには、バイナリをリンクするときに、-lsupc++ を使用します。 次に例を示します。

    arm-linux-androideabi-g++ .... -lsupc++
    

    STLport または libc++ ライブラリを使用しているときは、このように明示的にリンクする必要はありません。

    C++ STL サポート

    スタンドアロン ツールチェーンには、C++ 標準テンプレート ライブラリの実装のコピーが含まれています。この実装は、既に説明した --stl=<name> オプション指定した内容に応じて、GNU libstdc++、STLport、または libc++ 向けのいずれかになります。 STL の実装を使用するには、プロジェクトを適切なライブラリにリンクする必要があります。

    • -lstdc++ を使用して、実装の静的ライブラリ バージョンにリンクします。このようにすると、必要なすべての C++ STL コードが最終バイナリに含まれます。 単一の共有ライブラリまたは実行可能ファイルのみを生成する場合は、この方法が最適です。

      この方法は、推奨される方法です。

    • または、-lgnustl_shared を使用して、GNU libstdc++ の共有ライブラリ バージョンにリンクします。 また、このオプションを使用する場合は、コードを適切に読み込むために、libgnustl_shared.so を端末にコピーする必要があります。 表 6 は、各ツールチェーン タイプでの、このファイルの場所を示しています。
    • 注: GNU libstdc++ は、GPLv3 ライセンスの下に使用が許諾されます(リンク例外が適用されます)。 この要件を順守できない場合は、プロジェクトの共有ライブラリを再配布できません。

    • -lstlport_shared を使用して、STLport の共有ライブラリ バージョンにリンクします。その場合には、コードを適切に読み込むために、libstlport_shared.so を端末にコピーする必要があります。 表 6 は、各ツールチェーン タイプでの、このファイルの場所を示しています。
    • 表 6 ツールチェーンとファイルの場所。

      ツールチェーン [Location]
      arm $TOOLCHAIN/arm-linux-androideabi/lib/
      arm64 $TOOLCHAIN/aarch64-linux-android/lib/
      x86 $TOOLCHAIN/i686-linux-android/lib/
      x86_64 $TOOLCHAIN/x86_64-linux-android/lib/
      mips $TOOLCHAIN/mipsel-linux-android/lib/
      mips64 $TOOLCHAIN/mips64el-linux-android/lib/

      注: プロジェクトに複数の共有ライブラリまたは実行可能ファイルが含まれる場合、共有ライブラリの STL 実装にリンクする必要があります。 そうしないと、ビルドシステムは特定のグローバル要素を一意に定義できません。その結果、予期しないランタイム動作が発生する可能性があり、クラッシュしたり、例外を適切にキャッチできなくなる場合があります。

      ライブラリの共有バージョンを単純に libstdc++.so と呼ぶことができない理由は、この名前がシステムの最小 C++ ランタイムと実行時に競合する可能性があるからです。 そのため、ビルドシステムは、GNU ELF ライブラリの新しい名前を強制的に適用します。 この問題は静的ライブラリにはありません。