现在已经可以把Android NDK提供的工具链以独立编译器的方式使用了!如果你已经有了自己的构建系统,这就很有用了。
一个典型的应用场景是调用一个依赖于CC环境变量开源库的'configure'脚本进行跨平台编译。
本文档将解释如何那样做。
首先 ,你需要确定你的独立工具链所面向的目标CPU加构,是ARM-based设备、x86-based设备,还是MIPS-based设备。每个架构对应不同的工具链名字:
* arm-linux-androideabi-4.6 => 面向ARM-based Android设备
* x86-4.6 => 面向x86-based Android设备
* mipsel-linux-android-4.6 => 面向MIPS-based Android设备
你应该了解的第二项事情是你想面向那一级Android nativeAPI 。每一级都提供了不同的API,它们被文档doc/STABLE-APIS.html所描述,并对应于$NDK/platforms的子文件夹。这使得你可以定义指向你的'sysroot'的路径 ,GCC路径下包含系统头文件和库。通常看起来像这样:
SYSROOT=$NDK/platforms/android-<level>/arch-<arch>/
<level> 是API level 数,<arch> 是体系结构("arm", "x86", 和"mips" 都可以作为值)。例如,如果你面向 Android2.2 (a.k.a. Froyo),你应使用:
SYSROOT=$NDK/platforms/android-8/arch-arm
注意:X86 和MIPS体系仅在android-9才开始支持。
使用--sysroot选项调用编译器,以表明你所面向的平台的系统文件的路径。例如:
export CC="$NDK/toolchains/<name>/prebuilt/<system>/bin/<prefix>gcc--sysroot=$SYSROOT"
$CC -o foo.o -c foo.c
<name> 是工具链的名字,<system> 是宿主系统,<prefix> 是特定工具链的前缀。例如,如果你在Linux上使用NDK r5 工具链,你将使用:
exportCC="$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi-gcc--sysroot=$SYSROOT"
就像你看到的,这很啰嗦,但的确能工作!
注意:
直接使用NDK 工具链具有很多限制:
你不能使用任何C++ STL (无论是STLport 或GNU libstdc++) 。也不能使用异常和RTTI。
NDK 允许你创建一个"自定义的" 工具链并安装以使生活更简单。例如,下面的命令:
$NDK/build/tools/make-standalone-toolchain.sh --platform=android-5--install-dir=/tmp/my-android-toolchain
这将创建一个文件夹,名为 /tmp/my-android-toolchain,包含一个android-5/arch-armsysroot的考贝和工具链的执行文件。
注意,默认下,ARM-based GCC 4.6 工具链将被脚本所选择。使用'--arch=x86' 来指定x86-based 的GCC,使用'--arch=mips' 来指定MIPS-based 的GCC,或使用
'--toolchain=<name>'来指定。例如:
--toolchain=x86-4.4.3 # select x86 GCC 4.4.3 compiler
--toolchain=mipsel-linux-android-4.6 # select MIPS GCC 4.6compiler, same as --arch=mips
如果你想,可以通过添加 --llvm-version=3.1 来把clang/llvm3.1也考贝过去。你可以在之后直接使用它。就像:
exportPATH=/tmp/my-android-toolchain/bin:$PATH
exportCC=arm-linux-androideabi-gcc # or export CC=clang
exportCXX=arm-linux-androideabi-g++ # or export CXX=clang++
注意,不使用 --install-dir 选项,make-standalone-toolchain.sh将创建一个名为/tmp/ndk/<toolchain-name>.tar.bz2的tarball。这使你可以很容易的存储并重新发布二进制工具包。
另一个重要的好处是这个独立的工具链将包含一个GNU libstdc++的考贝,它能支持异常和RTTI (当你链接到libstdc++ 或 libsupc++)。
重点:工具链执行文件不依赖于或包含宿主上的特点路径,换句话说,它们可以被安装于任何位置,或移动到另外位置。
注意:你依然可以对新的工具链使用—sysroot选项,但是现在变简单了!
通过ARM 工具链产生的机器码应该与官方的Android 'armeabi' ABI兼容。推荐使用-mthumb 编译标志来强制产生16位Thumb-1指令 (默认是32-bit 的)。
如果你想面向'armeabi-v7a' ABI,你应该确保下面的标志被使用:
CFLAGS='-march=armv7-a-mfloat-abi=softfp -mfpu=vfpv3-d16'
注:第一个标志启用Thumb-2指令,第二个启用H/W FPU指令同时确保浮点参数被传入核心寄存器,这是ABI兼容的关键。*不要*分开使用这些标志!
如果你想使用Neon指令,你需要改变-mfpu编译标志:
CFLAGS='-march=armv7-a-mfloat-abi=softfp -mfpu=neon'
注意这会强制使用VFPv3-D32。
还要确保下面两个标志也提供给链接器:
LDFLAGS='-march=armv7-a-Wl,--fix-cortex-a8'
注:第一个标志指示链接器为armv7-a选择合适的libgcc.a、libgcov.a和crt*.o。第二个标志用于在某些Cortex-A8装置中绕过一个CPU bug。
如果上面这些还不能满足你,那么你最好不要用独立的工具链了,而是坚守NDK构建系统,它将为你处理所有细节。
当你面向x86 ABI 或MIPS ABI时你不需要使用任何特定的编译器标志。
Windows下的二进制文件*不*依赖于Cygwin。好消息是它们因此会运行快,坏消息是它们不明白Cygwin的路径形式,比如/cygdrive/c/foo/bar (而不是 C:/foo/bar)。
NDK构建系统保证所有从Cygwin传给编译器的路径都被自动转换,并且为你应付那些难搞的事情。如果有一个自定义构建系统,你需要自己应付所有的问题。
注:当前没有支持Cygwin / MSys的计划。
在Android 2.3之前,Android平台并不真的支持wchar_t。这表示:
l 如果你面向android-9 或更高平台,wchar_t的大小是4bytes,并且大多数C库中的宽字节函数都可用(例外是多字节编解码函数和wsprintf/wsscanf函数)。
l 如果你面向任何更低的API level,wchar_t 只有一个字节并且任何宽字节函数都 不能用。
我们建议所有开发者都移除对wchar_t的任何依赖,并且转换到更好的方式。Android所提供的支持仅仅是为了帮助整合已存在的代码。
工具链执行文件默入是支持C++异常和RTTI的。所以当你不需要时,使用-fno-exceptions和-fno-rtti来禁止它们 (产生更少的机器码)。
注:如果你想使用这些特性,你将需要明确的链接到libsupc++。要这样做,在要在链接成二进制文件时使用-lsupc++ :
arm-linux-androideabi-g++ .... -lsupc++
独立工具链也带有一个GNU libstdc++ 库的考贝,它提供了C++标准模版库的一个实现。要使用它,你还需要链接到正确的库:
l 使用-lstdc++ 来链接静态库版。这保证了所有需要的C++ STL代码都包含到了你的最终文件中。这是推荐的方式。
l 使用-lgnustl_shared 来链接动态库版。如果你使用此方式,你需要确保libgnustl_shared.so也被复制到你的设备中。此文件位于:
$TOOLCHAIN/arm-linux-androideabi/lib/ for ARM toolchains.
$TOOLCHAIN/i686-linux-android/lib/ for x86 ones.
$TOOLCHAIN/mipsel-linux-android/lib/ for MIPS toolchains.
动态库版的GNU libstdc++不叫libstdc++.so的原因是这将导致在运行时与系统自己的最小C++运行时(/system/lib/libstdc++.so)冲突。这对静态库就没影响了。