独立工具链

您可以独立使用 Android NDK 附带的工具链,或将其作为插件与现有 IDE 结合使用。 如果您已有自己的构建系统,且仅需要调用交叉编译器功能以将对构建系统的支持添加到 Android,则这种灵活性非常有用。

选择您的工具链

首先,您需要决定您独立工具链将指向的处理器架构。 您可通过 --arch 标记完成该步骤。

选择您的 Sysroot

接下来,您需要定义 Sysroot。 Sysroot 是包含目标系统标头及库的目录。 如需定义 sysroot,您必须知道原生支持的目标 Android API 级别;可用的原生 API 因 Android API 级别而异。

针对相应 Android API 级别的原生 API 库位于 $NDK/platforms/ 下;每个 API 级别目录又包含针对各种 CPU 和架构的子目录。 标头位于 $NDK/sysroot中。

如需了解有关 Android API 级别及其支持的相应原生 API 的详细信息,请参阅原生 API

创建工具链

NDK 提供 make_standalone_toolchain.py 脚本以允许您从命令行执行自定义工具链安装。

这是替代旧式 make-standalone-toolchain.sh 的新工具。 此工具已在 Python 中重新实现,因此 Windows 用户无需安装 CygwinMSYS 即可运行该工具。

脚本位于 $NDK/build/tools/ 目录中,其中 $NDK 是 NDK 的安装根目录。

下面展示了使用此脚本的示例:

$NDK/build/tools/make_standalone_toolchain.py \
    --arch arm --api 21 --install-dir /tmp/my-android-toolchain

此命令创建一个名为 /tmp/my-android-toolchain/ 的目录,包含一个 android-21/arch-arm sysroot 的副本,以及适用于 32 位 ARM 目标的工具链二进制文件的副本。

请注意,工具链二进制文件不依赖或包含主机特有的路径。 换句话说,您可以将其安装在任意位置,甚至进行移动(如果需要)。

--arch 参数是必填项,但 API 级别将默认设置为给定架构的最低支持级别(目前,级别 16 适于 32 位架构,级别 21 适于 64 位架构)。

自 r18 起,所有独立工具链皆使用 Clang 和 libc++。 除非构建的是静态可执行文件,否则默认情况下,将使用 libc++ 共享库。 若要强制使用静态库,请在创建链接时传递 -static-libstdc++。 此行为与普通主机工具链的行为相符。

C++ 库支持中所述,在链接到 libc++时常常需要传递 -latomic

请注意,如果您省略 --install-dir 选项,此工具将在名为 $TOOLCHAIN_NAME.tar.bz2 的当前目录中创建一个 tarball。 此 tarball 可通过使用 --package-dir 放入不同的目录中。

如需了解更多选项和详细信息,请使用 --help

使用 Clang

Clang 二进制文件将自动添加至独立工具链中。

此外,<install-dir>/bin 下有两个包装器脚本,即 clangclang++。 这些脚本将调用带有正确目标架构标记的 clang 二进制文件。 换句话说,这些脚本无需任何修改即可运行,而且您只需设置指向其的 CCCXX 环境变量,即可在自己的构建中使用这些脚本。

同时,还有名为 gccg++ 的包装器脚本也会调用 Clang。 尽管 NDK 不再包含 GCC,但这些脚本为显式引用 GCC 的构建文件提供一定程度的兼容性。 显然,如果构建文件使用 Clang 不支持的命令行选项,您需要移除或替换这些选项。

Clang 以 ARM 为目标

对 ARM 进行构建时,Clang 基于是否存在 -march=armv7-a 和/或 -mthumb 编译器标记更改目标:

表 1. 可指定的 -march 值及其生成的目标。

-march 生成的目标
-march=armv7-a armv7-none-linux-androideabi
-mthumb thumb-none-linux-androideabi
-march=armv7-a-mthumb thumbv7-none-linux-androideabi

此外,您也可以根据自己的意愿替换为您自己的 -target

clangclang++ 应能够轻松替换 makefile 中的 gccg++。 如有疑问,在调用编译器时使用以下选项来验证其是否正常工作:

  • -v 用于转储与编译器驱动程序问题有关的命令
  • -### 用于转储命令行选项,包括以隐式方式预定义的选项。
  • -x c < /dev/null -dM -E 用于转储预定义的预处理器定义
  • -save-temps 用于比较 *.i*.ii 预处理文件。

ABI 兼容性

默认情况下,ARM Clang 独立工具链将以 armeabi-v7a ABI 为目标。 通过传递恰当的 -march-target 选项,可完成目标的替换。

我们建议使用 -mthumb 编译器标记来强制生成 16 位 Thumb-2 指令。 如果已省略此指令,工具链将发出 32 位 ARM 指令。

若要使用 NEON 指令,您必须使用 -mfpu 编译器标记:-mfpu=neon

请注意,按照 ARM 规范,此设置强制使用 VFPv3-D32

另外,确保向链接器提供以下两个标记: -march=armv7-a -Wl,--fix-cortex-a8

第一个标记指示链接器选取为 armv7-a 定制的工具链库。 在某些 Cortex-A8 实现中,需要第二个标记作为 CPU 错误的解决方法。

如果以其他 ABI 为目标,您不必使用任何特定的编译器标记。

如需有关 ABI 支持的详细信息,请参阅 ABI

警告和限制

Windows 支持

Windows 二进制文件不依赖于 Cygwin。 这种独立性让其能更快地运行。 不过,代价是它们不理解 Cygwin 路径规范,如与 C:/foo/bar 相反的 cygdrive/c/foo/bar

异常、RTTI 和 STL

默认情况下,工具链二进制文件支持 C++ 异常和 RTTI。 在构建源时,若要停用 C++ 异常和 RTTI(例如,为了生成更轻量的机器代码),请使用 -fno-exceptions-fno-rtti

C++ STL 支持

独立工具链包含一个 C++ 标准模板库 (STL) 实现。

  • 使用 -static-libstdc++ 获取静态库版本的 libc++。 这样做可确保将所有必需的 C++ STL 代码添加到您最终的二进制文件。 如果您仅生成我们建议的共享库或可执行文件,则此方法为理想之选。

  • 默认情况下,应使用共享库版本的 libc++。 要链接到共享库,无需额外的标记。 您必须打包应用中的 libc++_shared.so,否则代码将无法加载。

    表 2 显示每个架构中此文件的位置。

    表 2. 可指定的 -march 值及其生成的目标。

    工具链 位置
    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/

使用独立工具链构建开源项目

请参考以下示例工具链:

# Create an arm64 API 26 libc++ toolchain.
$NDK/build/tools/make_standalone_toolchain.py \
  --arch arm64 \
  --api 26 \
  --install-dir=my-toolchain

以下是您设置环境并将其用于构建传统开源项目的方法:

# Add the standalone toolchain to the search path.
export PATH=$PATH:`pwd`/my-toolchain/bin

# Tell configure what tools to use.
target_host=aarch64-linux-android
export AR=$target_host-ar
export AS=$target_host-clang
export CC=$target_host-clang
export CXX=$target_host-clang++
export LD=$target_host-ld
export STRIP=$target_host-strip

# Tell configure what flags Android requires.
export CFLAGS="-fPIE -fPIC"
export LDFLAGS="-pie"

带自定义构建系统的项目

以下示例展示执行上述步骤后如何构建 Toybox:

git clone https://github.com/landley/toybox.git
cd toybox
make defconfig && make

使用 autoconf 的项目

或者,如果是基于 autoconf 的项目,代码将更像下面这样:

tar zxvf make-4.2.tar.gz
cd make-4.2
./configure --host=$target_host && make

请注意,基于 autoconf 的项目在支持交叉编译方面的差异很大。 另外,请注意,如果您 git clone 基于 autoconf 的项目,则不可能产生已签入的 configure 脚本,因此您必须遵循项目的文档进行引导。