Android NDK Standalone Toolchain(中文翻译)

英文原版: Android NDK Standalone Toolchain

独立工具链

你可以在 Android NDK 中单独的使用此工具链,也可以在IDE中以插件的方式使用。如果你已经拥有编译系统,并且只需要一个跨平台的编译器来增加对 Andrid 系统的支持,这种方式是非常方便的。

一种常见的用法是,调用开源项目库的 configure 脚本中时,设置跨平台编译环境变量 CC 为 Toolchain 的路径。

注:本文的主要目的是理解编译,链接,和低层架构。除此之外,一般这些技术是用不到的。多数情况下,我们推荐使用独立工具链的方式代替直接使用NDK编译系统。

选择工具链

在开始之前,你需要知道目标独立工具链要兼容哪一种处理器架构。每一种架构对应一个工具链名字,如下表所示:

表1:不同指令集下的 APP_ABI 设置

架构 工具链名字
ARM-based arm-linux-androideabi-<gcc-version>
x86-based x86-<gcc-version>
MIPS-based mipsel-linux-android-<gcc-version>
ARM64-based aarch64-linux-android-<gcc-version>
X86-64-based x86_64-<gcc-version>
MIPS64-based mips64el-linux-android–<gcc-version>

选择根目录

接下来,需要定义 sysroot 变量(sysroot是包含了目标操作系统头文件和库文件的目录)。在定义 sysroot 之前,你必须确认要支持的目标API版本,本地API是随着Android API级别变化的。

本地API接口位于 $NDK/platforms/ 目录;每一个API接口目录,依次包含了各种CPU和架构的子目录。接下来的示例展示了如何为一个 Android 5.0 (API level 21),ARM架构的系统定义 sysroot 变量:

SYSROOT=$NDK/platforms/android-21/arch-arm

关于 Android API 级别和各自支持的本地API接口的详细信息,参见 Android NDK Native APIs

调用编译器

有两种调用编译器的方法。一种简单的方法,更多的借助于编译系统。另一种复杂的方法,可以提供更大的灵活性。

简单的方法

最简单的编译方法是在命令行下直接调用合适的编译器,使用 –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++标准库(STLport, libc++, GNU libstdc++),也不支持RTTI异常处理。

如果使用 Clang 编译器,需要额外两步:
1.增加合适的 -target 选项,如下表所示:

表2: CPU架构和合适的 -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

2.增加汇编工具和链接支持选项 -gcc-toolchain ,示例如下:

-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 代表 Android 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 的系统拷贝,和32位 ARM 架构的二进制工具链。

注,上面的二进制工具链不依赖于路径和宿主机,这意味着工具链可以安装在任何位置,也可以任意移动。

默认情况下,编译系统使用32位ARM架构的GCC-4.8工具链。你可以使用 –arch= 选项来指定不同的值。如下表所示:

表3: 工具链和合适的 –arch 取值

工具链 取值
mips64 compiler –arch=mips64
mips GCC 4.8 compiler –arch=mips
x86 GCC 4.8 compiler –arch=x86
x86_64 GCC 4.8 compiler –arch=x86_64
arm GCC 4.8 compiler –arch=arm

另外,你可以使用 –toolchain= 选项。如下表所示:

表4: 工具链和合适的 –toolchian 取值

工具链 取值
arm –toolchain=arm-linux-androideabi-4.8
arm –toolchain=arm-linux-androideabi-4.9
arm –toolchain=arm-linux-android-clang3.5
arm –toolchain=arm-linux-android-clang3.6
x86 –toolchain=x86-linux-android-4.8
x86 –toolchain=x86-linux-android-4.9
x86 –toolchain=x86-linux-android-clang3.5
x86 –toolchain=x86-linux-android-clang3.6
mips –toolchain=mips-linux-android-4.8
mips –toolchain=mips-linux-android-4.9
mips –toolchain=mips-linux-android-clang3.5
mips –toolchain=mips-linux-android-clang3.6
arm64 –toolchain=aarch64-linux-android-4.9
arm64 –toolchain=aarch64-linux-android-clang3.5
arm64 –toolchain=aarch64-linux-android-clang3.6
x86_64 –toolchain=x86_64-linux-android-4.9
x86_64 –toolchain=x86_64-linux-android-clang3.5
x86_64 –toolchain=x86_64-linux-android-clang3.6
mips64 –toolchain=mips64el-linux-android-4.9
mips64 –toolchain=mips64el-linux-android-clang3.5
mips64 –toolchain=mips64el-linux-android-clang3.6

注:表4并不包含所有的可用选项。还有其他未确认的选项。

你也可以使用下面两个方法拷贝 Clang/LLVM 3.6:在 –toolchain 选项后添加 -clang3.6 值,示例如下:

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

也可以使用 -llvm-version=3.6 作为命令行中单独的附加选项。

注:你可以使用 来代替具体的版本号,此值默认取 Clang 的最高版本。

默认情况下,编译系统使用32位的主机工具链。你可以指定使用64位的主机工具链。下表显示了 –system 选项的不同取值。

表5: 主机工具链和合适的 –system 取值

工具链 取值
64-bit Linux –system=linux-x86_64
64-bit MacOSX –system=darwin-x86_64
64-bit Windows –system=windows-x86_64

有关32位或64位结构主机工具链的更多信息,请参照 64-Bit and 32-Bit Toolchains

你可以使用 –stl=stlport 选项来拷贝 libstlport 代替默认的 libgnustl。同时,必须使用 -lstlport_shared 选项来指定动态链接库。这种用法与使用 -lgnustl_shared 来指定 GNU libstdc++ 道理是一样的。

当然,你可以使用 –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/.tar.bz2 压缩包。方便架构和分发此工具。

这种独立工具链提供了额外的优势,它包含了 C++ STL 库的拷贝,用来支持异常处理和RTTI操作。

关于工具链的更多选项和信息,可以使用 –help 选项查看。

使用 Clang

你可以使用 –llvm-version= 选项来生成带有Clang编译器的工具链。 是LLVM/Clang的版本号,如3.5,3.6。示例代码如下:

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

注: Clang工具链包含了GCC工具,因此他们使用同样的汇编器、链接器、头文件、库文件、以及C++ STL实现。

上面的命令在 /bin/@ 目录安装了 clang, clang++ 两个脚本。这些脚本可以调用系统 clang 工具并且附带默认的目标架构标签。换句话说,你可以在自己已有的编译系统中简单的替换 CC或CXX 路径到这两个脚本就可以使用了。

调用Clang

在Unix系统平台下,使用命令行的方式调用Clang生成ARM工具链。示例如下:

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

clang++使用同样的方式调用clang++31。

Clang

当生成ARM架构的工具链时,Clang使用 -march=armv7-a 和/或 -mthumb 选项来生成不同的工具链:

表6: -march取值和目标结果

-march取值 目标结果
-march=armv7-a armv7-none-linux-androideabi
-mthumb thumb-none-linux-androideabi
-march=armv7-a -mthumb thumbv7-none-linux-androideabi

你可以使用其他 -target 来生成想要的结果。

在Clang版本的独立工具链里面已经包含了as和ld工具,因此-gcc-toolchain选项可以省略。

在Makefile脚本里,clang和clang++应该很容易的更换为gcc和g++。如果有疑问,可以使用下面的选项来验证:

  • -v 输出命令与编译器相关的驱动程序问题
  • -### 输出命令行选项,包括预定义的
  • -x c < /dev/null -dM -E 输出预定义的预处理器定义
  • -save-temps 对比.i或.ii预处理文件

关于Clang的更多消息,请查看 http://clang.llvm.org/ 网站的GCC兼容性章节。

ABI兼容性

未完待续。。。