使用ANDROID作為一個獨立的編譯器工具鏈
======================================================
該文檔解釋了如何做:
1/選擇你的工具鏈:
----------------------------
做任何東西之前,你需要決定是否你的獨立的工具鏈是基於arm的設備,基於x86的,或基於mips的哪一個。
每個架構對應一個不同的工具鏈的名字。例如:
* arm-linux-androideabi-4.6 => targeting ARM-based Android devices
* x86-4.6 => targeting x86-based Android devices
* mipsel-linux-android-4.6 => targeting MIPS-based Android devices
2/選擇目錄切換為sysroot:
--------------------------
第二件事你需要知道的是,你需要知道安卓原生API的級別。每一個都提供了一個不同的各種api,
這是記錄在doc/STABLE-APIS.html和對應 $NDK/platforms的子目錄中
定義'sysroot'包含目標系統頭文件和庫。通常,如下:
SYSROOT=$NDK/platforms/android-<level>/arch-<arch>/
<level>是API 級別 號,<arch>是架構(支持arm,x86,mips)。
例如:如果目標android是2.2,如下:
SYSROOT=$NDK/platforms/android-8/arch-arm
重要:注意X86和MIPS架構只在android-9或更新支持。
3/調用編譯器(最困難一步)
----------------------------------------
使用--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工具鏈,如下:
export CC="$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi-gcc --sysroot=$SYSROOT"
可見,這是很繁瑣,但是有效!
重要事項:
直接使用NDK的工具鏈(非獨立版工具鏈)有嚴重的限制:
你不能使用任何C++ STL(STLport或GNU libstdc++)。也沒有異常和運行時類型檢查。
4/調用編譯器(最簡單一步):
----------------------------------------
NDK允許您創建一個“定制”工具鏈安裝。例如,參考下面的命令:
$NDK/build/tools/make-standalone-toolchain.sh --platform=android-5 --install-dir=/tmp/my-android-toolchain
這將簡歷一個路徑/tmp/my-android-toolchain,包含復制的 android-5/arch-arm sysroot 工具鏈 文件。
注意默認使用ARM GCC 4.6 工具鏈。使用'--arch=x86'設定x86 GCC 4.6,或'--arch=mips'設定MIPS GCC 4.6,或者'--toolchain=<name>'來設。例如
--toolchain=x86-4.4.3 # select x86 GCC 4.4.3 compiler
--toolchain=arm-linux-androideabi-4.7 # select ARM GCC 4.7 compiler
--toolchain=mipsel-linux-android-4.6 # select MIPS GCC 4.6 compiler, same as --arch=mips
'--llvm-version=3.1'可以復制clang/llvm 3.1或使用--toolchain with '-clang3.1'後綴,例如:
--toolchain=arm-linux-androideabi-clang3.1 # same as --arch=arm --llvm-version=3.1
稍後可用類似如下(導入環境變量):
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。
使你可以很容易重新發布文件。
另一個很便利的地方:獨立工具鏈包含GNU libstdc++(支持異常和RTTI機制),只要你鏈接到它。
--help查看更多選項和信息。
重要:工具鏈的二進制文件不依賴或包含特定於主機的路徑,
換句話說,他們可以安裝在任何位置,甚至如果你需要移動。
注意:你仍然可以使用--sysroot選項在新的工具鏈,但它現在只是可選的!
5/關於Clang
---------------------
Clang/clang++使用同樣的編譯器,鏈接器,頭文件,庫文件和GNU libstdc++在獨立的包中。
Clang/clang++是個腳本,創建時使用-target指定到特定架構。
例如:在ARM獨立包,clang是個單行:
`dirname $0`/clang31 -target armv5te-none-linux-androideabi "$@"
clang++是另一行:
`dirname $0`/clang++31 -target armv5te-none-linux-androideabi "$@"
注意arm,使用-march=armv7-a -mthumb clang將改變-target
1/ With "-march=armv7-a", -target becomes armv7-none-linux-androideabi
2/ With "-mthumb", -target becomes thumb-none-linux-androideabi
3/ With both, -target becomes thumbv7-none-linux-androideabi
你可以重寫-target。
做額外的工作替換gcc/g++在Makefile裡。懷疑時,使用下面檢查:
1/ Add option "-v" to dump commands compiler driver issues
打印命令編譯器驅動問題
2/ Add option "-###" to dump command line options, including those
implicitly predefined.
打印選項命令行,包含隱式預定義的
3/ Use "-x c /dev/null -dM -E" to dump predefined preprocessor definitions
打印預定義的預處理器定義
4/ Add option "-save-temps" and compare the preprocessed files *.i or *.ii
比較預處理文件
See http://clang.llvm.org/, especially the GCC compatibility section.
6/應用二進制接口兼容性:
---------------------
ARM工具鏈生成的代碼應該默認兼容官方Android 'armeabi'的應用二進制接口(參照/CPU-ARCH-ABIS.html)
推薦使用-mthumb 編譯器標識來強制生成16位Thumb-1指令(默認是32位ARM的指令)
如果你的目標是'armeabi-v7a'的應用二進制接口,你需要確保使用如下標志:
CFLAGS='-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16'
注意:第一個標志啟用Thumb-2指令,第二個啟用H/W浮點處理器指令功能,
確保用核心寄存器傳遞浮點參數,它對於ABI兼容性來說是關鍵。不要分開使用這些開關!
如果你想使用Neon指令(ARM的單指令多數據技術),你需要再增多一個編譯器開關:
CFLAGS='-march=armv7-a -mfloat-abi=softfp -mfpu=neon'
Note that this forces the use of VFPv3-D32, as per the ARM specification.
注意它強制使用VFPv3-D32(注:VFPv3是vector floating point v3的縮寫,
即向量浮點第三版,D32應該是指32個雙精度浮點寄存器),根據ARM規范。
Also, make sure the following two flags are provided to linker:
還有,確保下鏈接器使用以下兩個標志。
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時,你不需要使用人任何特殊編譯器標志。
7/ 警告和限制:
--------------------------
7.1/Windows支持:
- - - - - - - - - - -
Windows二進制文件不依賴於Cygwin。好消息是它們會跑得更快,
而壞消息是它們不理解Cygwin的目錄格式,像/cygdrive/c/foo/bar(但可以理解C:/foo/bar)
NDK構建系統確保從Cygwin中傳遞的所有路徑被自動翻譯,為你處理其它威脅。
如果你擁有一個定制構建系統,你可能需要自己處理問題。
注意:現在沒計劃支持Cygwin / MSys,但歡迎貢獻。詳細請聯系android-ndk論壇
7.2/wchar_t支持:
- - - - - - - - - - -
正如文檔所寫的,Android平台在Android 2.3之前沒有真正支持wchar_t。
用實際的術語說,這意味著:
- 如果你的目標平台是android-9或更高,wchar_t的大小是4字節,則在C庫中大多數
寬字符函數可用(例外的是多字節的編碼解碼函數和wsprintf/wsscanf)
- 如果你的目標是較早的API級別,wchar_t的大小將是1字節,任何寬字符函數都不可工作。
我們建議所有開發者避免對wchar_t類型的依賴,而是轉向更好的表示。
在Android中提供的支持只出現在幫助你遷移現存代碼的地方。
7.3/ 異常、運行時類型識別和STL(標准模板庫):
- - - - - - - - - - - - - - -
工具鏈二進制文件默認支持C++異常和RTTI(運行時類型識別)。
它們默認是打開的,所以如果你想在構建使用它們的代碼時關閉它們,
請使用-fno-exceptions和-fno-rtti(例如,為了生成更小的機器代碼)。
注意:你將需要顯式地用libsupc++鏈接,如果你使用這些特性。
為了做到這點,當鏈接二進制文件時使用-lsupc++,
像這樣:(注:這裡要小心,是libsupc++不是libstdc++!)
arm-linux-androideabi-g++ .... -lsupc++
7.4/ C++ STL 支持:
- - - - - - - - - - -
工具鏈還帶有一個可用的GNU libstdc++實現,它提供一個工作的C++標准模板庫實現。
你將需要顯式地用-lstdc++鏈接以使用它。
* 使用-lstdc++ 來鏈接靜態庫版本。這確保了所需C++STL代碼包含進你的最終二進制文件。
這是一個生成單獨共享庫或執行文件的辦法。
This is the recommended way to do it.
這是推薦的方式。
* 使用-lgnustl_shared來鏈接靜態庫版本。如果你有幾個相關的需要運行時在同樣的地址空間
的共享庫或執行文件,這是必須的。
(一些全局變量需要唯一的定義,不適用於每一個可執行文件鏈接靜態的libstdc++)
如果你使用此選項,你需要確保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++ 授權在GPLv3,有一個例外,查看如下URL來詳細描述:
http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01s02.html
如果你不能干遵守它的要求,你不能發布這個共享庫,不能使用在你的工程中。
共享版GNU libstdc++不被叫做libstdc++.so的原因是這將會在運行時和最小C++運行時庫
(/system/lib/libstdc++.so)沖突。這強制了一個GNU ELF庫的新名字。對於靜態庫這不是個問題。