前言
簡(jiǎn)單地說访忿,交叉編譯就是程序的編譯的環(huán)境和它的運(yùn)行的環(huán)境不一樣撵幽,即在一個(gè)平臺(tái)上生成另一個(gè)平臺(tái)上的可執(zhí)行代碼是牢。需要使用交叉編譯的主要原因是由于目標(biāo)環(huán)境中各種資源都相對(duì)有限僵井,所以很難直接進(jìn)行本地編譯。
一驳棱、交叉編譯工具鏈
1.1批什、什么是交叉編譯工具鏈
交叉工具鏈?zhǔn)侵缚梢陨赡繕?biāo)環(huán)境可運(yùn)行代碼的工具集。在 Linux 系統(tǒng)中的編譯工具鏈包括以下一些工具社搅,編譯器 gcc驻债,鏈接器 ld,歸檔工具 ar 等形葬,如果在 Linux 環(huán)境中交叉編譯 Android 系統(tǒng)環(huán)境可運(yùn)行的代碼合呐,是不能直接使用 Linux 下的編譯工具的,而應(yīng)該使用 Android NDK 里的工具鏈笙以,它們對(duì)應(yīng)的名子為 arm-linux-android-gcc合砂,arm-linux-android-ld 等。
1.2源织、交叉編譯工具的命名規(guī)則
交叉編譯工具的命名形式如下所示:
arch-[vendor]-kernel-system-toolname
它由五部分組成翩伪,下面分別解釋一下每部分的含義:
- arch,指的是 CPU 架構(gòu)谈息,一般包括如下幾種架構(gòu):
- arm
- mips
- powerpc
- x86
- x86_64
- verdor缘屹,一般指的是生產(chǎn)廠商,如果沒有生產(chǎn)廠商可以為空侠仇。
- kernel轻姿,指的目標(biāo)環(huán)境使用的 kernel,以 android 為例逻炊,它使用的是 linux 內(nèi)核互亮,所以在這部分會(huì)填寫為 linux。
- system余素,指的是哪個(gè)系統(tǒng)豹休,如:
- androideabi
- android
- toolname: 指的是 gcc,ld桨吊,ar 等威根。
所以可以看到 android 的編譯工具的名子會(huì)寫成 arm-linux-andirod-gcc凤巨。
1.3、交叉編譯常用變量說明
在做交叉編譯時(shí)洛搀,常會(huì)在腳本中定義一些環(huán)境變量以方便我們命名用敢茁,常用的環(huán)境變量如下:
- PREFIX: 指明交叉編譯后輸出的目錄;
- ARCH: 指明交叉編譯后輸出的 CPU 架構(gòu)留美;
- CROSS-PREFIX:指明交叉編譯前輟 arch-vender-kernel-system彰檬;
- SYSROOT: 指明交叉編譯目標(biāo)機(jī)器的頭文件和庫(kù)文件目錄;
- TOOLCHAIN: 指明交叉編譯工具鏈的位置谎砾;
- PLATFROM:指明交叉編譯時(shí)使用的是哪個(gè)版本的的頭文件和庫(kù)文件僧叉,它是 SYSROOT 的一部分;
- ANDROID_NDK: 指明 Android NDK 所在目錄棺榔。
二瓶堕、如何進(jìn)行交叉編譯
通過設(shè)置 configure 參數(shù)來生成交叉編譯的 Makefile 文件:
- arch 指定架構(gòu);
- cross-prefix 交叉編譯工具鏈前輟症歇;
- sys root 交叉編譯樹的根郎笆。
例子:
以 ffmpeg 的交叉編譯為例,來看一下如何生成交叉編譯的 Makefile:
./configure --target-os=linux --arch=arm --enable-cross-compile --cross-prefix=arm-linux-androideabi-
--sysroot=~Library/Android/sdk/ndk-bundle/platforms/android-9/arch-arm
執(zhí)行后的結(jié)果如下:
install prefix /usr/local
source path .
C compiler arm-linux-androideabi-gcc
C library bionic
host C compiler gcc
host C library
ARCH arm (armv5te)
三忘晤、使用 ndk-build 編譯 NDK 程序
Google 提供了一系列腳本工具宛蚓,以方便大家做 Android NDK 方面的開發(fā),其中最重要的是 ndk-build 腳本设塔。有了這些腳本凄吏,開發(fā)者就不必再定義各種環(huán)境變量,只需要提供兩個(gè) Makefile 片段指明要編譯哪些 C/C++文件闰蛔,生成哪個(gè)目標(biāo)環(huán)境的程序就好了痕钢。ndk-build 腳本工具會(huì)自動(dòng)檢測(cè)各種環(huán)境變量、目標(biāo)環(huán)境編譯器等序六,最終完成交叉編譯任连。
3.1 NDK-BUILD 的工作
先來看一下 ndk-build,通過查看 ndk-build 源碼可以看到例诀,它執(zhí)行的是類似于
$GNUMAKE -f <ndk>/build/core/build-local.mk <parameters>
這樣的命令随抠,實(shí)際就是自動(dòng)檢測(cè)并設(shè)置環(huán)境變量。執(zhí)行 ndk-build NDK_LOG=1 命令繁涂,會(huì)看到下面的信息:
Android NDK: NDK installation path auto-detected:
Android NDK: GNU Make version 3.81 detected
Android NDK: Host OS was auto-detected: darwin
Android NDK: Host operating system detected: darwin
Android NDK: Host CPU was auto-detected: x86
Android NDK: HOST_TAG set to darwin-x86
Android NDK: Host tools prebuilt directory:
#這里檢測(cè)到編譯器地址
~/Library/Android/sdk/ndk-bundle/prebuilt/darwin-x86_64/bin
Android NDK: Found platform root directory:
#這里檢測(cè)到 platforms 目錄地址
~/Library/Android/sdk/ndk-bundle/platforms
#下面列出了所有支持的 platform
Android NDK: Found supported platforms: android-12 android-13 android-14 android-15 android-16 android-17
android-18 android-19 android-21 android-22 android-23 android-24 android-26 android-9
#列出支持的目標(biāo)環(huán)境
Android NDK: PLATFORM android-12 supports: arm mips x86
......
#下面是每個(gè)目標(biāo)環(huán)境的的環(huán)境樹
Android NDK: ABI arm sysroot is: /Users/lichao/Library/Android/sdk/ndk-bundle/platforms/android-12/arch-arm
Android NDK: ABI mips sysroot is: /Users/lichao/Library/Android/sdk/ndk-bundle/platforms/android-12/arch-mips
Android NDK: ABI x86 sysroot is: /Users/lichao/Library/Android/sdk/ndk-bundle/platforms/android-12/arch-x86
通過上面的分析就應(yīng)該清楚 ndk-build 主要作什么事情了拱她。既然 Google 已經(jīng)給我們提供了這么方便的交叉編譯工具,那作為開發(fā)者還需要做下面兩件事兒:
編寫 C/C++ 代碼扔罪。
編寫 Android.mk 和 Application.mk 兩個(gè) Makefile 片段秉沼。
3.2、Android.mk 與 Application.mk
下面就來看一下兩個(gè) Makefile 片段 Android.mk 和 Application.mk 是做什么用的。
- Android.mk:目的是用于向構(gòu)建系統(tǒng)描述源文件和共享庫(kù)氧猬,它位于 $PROJECT/jni/ 目錄中。
- Application.mk:目的是描述在你的應(yīng)用程序中所需要的模塊(即靜態(tài)庫(kù)或動(dòng)態(tài)庫(kù))坏瘩,它也位于 $PROJECT/jni/ 目錄中盅抚。
3.2.1、Android.mk
先來看一下 Android.mk 的例子:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello_jni
LOCAL_SRC_FILES := hello_jni.c
include $(BUILD_SHARED_LIBRARY)
每行語(yǔ)句的作用如下:
-
LOCAL_PATH
在 Android.mk 中必須首先定義 LOCAL_PATH 變量倔矾,此變量表示源文件在開發(fā)樹中的位置妄均。構(gòu)建系統(tǒng)提供的宏函數(shù) my-dir 將返回當(dāng)前目錄(包含 Android.mk 文件本身的目錄)的路徑。
-
CLEAR_VARS
CLEAR_VARS 變量指向特殊 GNU Makefile哪自,可清除許多 LOCAL_XXX 變量丰包,例如 LOCAL_MODULE、LOCAL_SRC_FILES 和 LOCAL_STATIC_LIBRARIES壤巷。
請(qǐng)注意邑彪,它不會(huì)清除 LOCAL_PATH。此變量必須保留其值胧华,因?yàn)橄到y(tǒng)在單一 GNU Make 執(zhí)行環(huán)境(其中所有變量都是全局的)中解析所有構(gòu)建控制文件寄症。在描述每個(gè)模塊之前,必須聲明(重新聲明)此變量矩动。
-
LOCAL_MODULE
變量將存儲(chǔ)要構(gòu)建的模塊的名稱有巧。
-
LOCAL_SRC_FILES
LOCAL_SRC_FILES 枚舉源文件,以空格分隔多個(gè)文件悲没。LOCAL_SRC_FILES 變量必須包含要構(gòu)建到模塊中的 C 和/或 C++ 源文件列表篮迎。
-
BUILD_SHARED_LIBRARY
這一行幫助系統(tǒng)將所有內(nèi)容連接到一起。
3.2.2示姿、Application.mk
再來看一下 Application.mk 的例子:
APP_PLATFORM := android-9
APP_STL := gnustl_static
APP_CFLAGS := -Wno-error=format-security
APP_ABI := armeabi armeabi-v7a arm64-v8a
APP_OPTION := release
每行的作用如下:
- APP_PLATFORM
此變量包含目標(biāo) Android 平臺(tái)的名稱甜橱。NDK API 級(jí)別與 Android 版本對(duì)照表如下:
NDK 支持的 API 級(jí)別 | Android 版本 |
---|---|
9 | 2.3 到 3.0.x |
12 | 3.1.x |
13 | 3.2 |
14 | 4.0 到 4.0.2 |
15 | 4.0.3 和 4.0.4 |
16 | 4.1 和 4.1.1 |
17 | 4.2 和 4.2.2 |
18 | 4.3 |
19 | 4.4 |
21 | 4.4W 和 5.0 |
-
APP_STL
默認(rèn)情況下,NDK 構(gòu)建系統(tǒng)為 Android 系統(tǒng)提供的最小 C++ 運(yùn)行時(shí)庫(kù)(system/lib/libstdc++.so) 栈戳。該指令可以讓您在自己的應(yīng)用中使用或鏈接的替代 C++ 實(shí)現(xiàn)渗鬼。
-
APP_CFLAGS
構(gòu)建系統(tǒng)在僅構(gòu)建 C++ 源文件時(shí)傳遞到編譯器的一組 C++ 編譯器標(biāo)志。
-
APP_ABI
您可以使用 APP_ABI 選擇編譯出不同的 ABI 目標(biāo)環(huán)境代碼荧琼。
-
APP_OPTION
可定義變量為 release 或 debug譬胎。在構(gòu)建應(yīng)用的模塊時(shí)可使用它來更改優(yōu)化級(jí)別。
通過上面的分析命锄,我們可以知道 Google 為了開發(fā)者更容易的開發(fā) NDK 程序堰乔,給我們提供了一套交叉編譯的工具,大大降低了開發(fā) NDK 程序的難度脐恩。