- Android核心原理與系統級應用高效開發
- 韓超等
- 12字
- 2018-12-30 05:22:12
第2章 編譯結構和各種構建
2.1 Android的系統構建
↘ 2.1.1 編譯環境要求
Android的編譯需要在Linux主機準備以下幾個方面的支持。
● 目標機編譯工具(交叉編譯工具鏈)。
● 主機編譯工具(x86主機的編譯工具)。
● Java環境(包括Java編譯和運行環境)。
● 腳本運行環境。
Android的源代碼包中自帶了主要的編譯工具,也就是目標機的編譯工具,路徑通常為:prebuilt/linux-x86/toolchain/。
其中Linux-x86指的是Linux環境的x86主機環境(64位的機器對應目錄為linux-x86_64),toolchain目錄中的內容是GCC交叉編譯工具鏈。由此,Android對目標機內容的編譯,使用的就是這些工具,不需要使用主機自己安裝的工具。
Android編譯針對主機環境的要求有Linux主機和JDK兩個方面,如表2-1所示。
表2-1 Android編譯針對主機環境的要求

在Android系統的編譯中,實際上只有Android 2.3及之后對JDK1.6的要求是強制性的,如果不具有JDK1.6環境,將無法成功編譯一些Java代碼。對于其他的限制,去掉編譯中由錯誤引起的中斷,在一定程度上可以繞過編譯的限制。
↘ 2.1.2 構建流程
Android的構建流程從根目錄下直接執行make命令來完成一個基本的編譯。實際上這是從Android開源代碼的根目錄(TOP)中的Makefile文件開始,是Linux中使用make機制的標準流程。
Android系統編譯流程的片斷如下所示:
$ make ============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=2.3.4 TARGET_PRODUCT=generic TARGET_BUILD_VARIANT=eng TARGET_SIMULATOR= TARGET_BUILD_TYPE=release TARGET_BUILD_APPS= TARGET_ARCH=arm HOST_ARCH=x86 HOST_OS=linux HOST_BUILD_TYPE=release BUILD_ID=GRJ22 ============================================ # ……編譯過程 Finding NOTICE files: out/target/product/generic/obj/NOTICE_FILES/hash- timestamp Combining NOTICE files: out/target/product/generic/obj/NOTICE.html Target system fs image: out/target/product/generic/obj/PACKAGING/ systemimage_intermediates/system.img Install system fs image: out/target/product/generic/system.img Installed file list: out/target/product/generic/installed-files.txt
在編譯開始的過程中,TARGET_PRODUCT是編譯的主要環境變量。在開始編譯的時候,TARGET_PRODUCT,TARGET_ARCH等內容是從環境變量得到的,如果需要更改它們的內容,可以直接使用export導出環境變量,但正規的方式是通過Android的編譯系統進行配置。
Android TOP目錄中的Makefile內容如下所示:
### DO NOT EDIT THIS FILE ### include build/core/main.mk ### DO NOT EDIT THIS FILE ###
TOP目錄中的Makefile也就是build/core/root.mk。在repo sync的階段,它被復制成TOP目錄的Makefile。這是因為在Android的代碼倉庫管理文件.repo/manifest.xml中包含了如下的內容:
<project path="build" name="platform/build"> <copyfile src="core/root.mk" dest="Makefile" /> </project>
此時表示的就是將build/core/root.mk復制為TOP目錄的Makefile。
根據這個Makefile的內容,實際上使用的編譯文件是build/core/目錄中的main.mk文件,其中的一個片斷如下所示:
.PHONY: droid DEFAULT_GOAL := droid $(DEFAULT_GOAL):
這是基于Linux標準的Make機制,droid實際上就是默認情況的“Make目標”,直接進行Make的時候,使用的就是droid目標。
在main.mk文件中,還定義另外幾個重要的目標,使用這些目標可以獲得一些額外效果,例如make docs表示生成文檔。
其他的目標如下所示。
● ramdisk:生成內存盤映像。
● bootimage:生成啟動映像。
● systemimage:生成系統映像。
● userdataimage:生成用戶數據映像。
● apps_only:僅生成應用程序。
● docs:生成文檔,生成的文檔和SDK中的文檔類似。
● clean:清除目標,實際上只是刪除out目錄。
除此之外,showcommands也是一個有用的輔助目標,可以列出編譯時詳細執行的各個命令。
● 提示:showcommands和編譯目標結合使用,獲得具體一個編譯過程中執行的命令,然后可以在命令行單獨運行這些命令。
如果在32位的主機上編譯Android 2.3以后的系統,在編譯的開始將不能通過編譯工具的檢查,錯誤如下所示:
Checking build tools versions... build/core/main.mk:76: **************************************************** build/core/main.mk:77: You are attempting to build on a 32-bit system. build/core/main.mk:78: Only 64-bit build environments are supported beyond froyo/2.2. build/core/main.mk:79: **************************************************** build/core/main.mk:80: *** stop. Stop.
此時實際上進行了一個強行的中斷,實際的錯誤沒有嚴重到不能進行編譯的程度。更改build/core/main.mk,注釋掉引發stop的一行,就可以進行編譯了。
除此之外,在源代碼中,有較少的部分和主機的位數(32位或64位)相關。在Android 2.3中,external/clearsilver/工程中的幾個Android.mk有相關的定義,其中的幾個LOCAL_CFLAGS和LOCAL_LDFLAGS被增加了-m64定義。將這幾個定義去除,就可以在32位環境中編譯通過了。
手動在目標文件系統中添加了內容,需要通過手動制作得到文件系統映像。例如,默認情況下制作yaffs2格式的映像文件的方法如下所示:
$./out/host/linux-x86/bin/mkyaffs2image -f out/target/product/generic/system out/target/product/generic/system.img $./out/host/linux-x86/bin/mkyaffs2image -f out/target/product/generic/data out/target/product/generic/userdata.img
↘ 2.1.3 環境設置
默認情況下的Make不一定需要環境設置也可以編譯。但是設置環境可以方便開發和有選擇地進行編譯,方法如下所示:
$ . build/envsetup.sh
環境設置腳本envsetup執行后,可以直接在終端使用一些命令,如下所示。
● croot:切換到Android源代碼根目錄(TOP)。
● m:全系統編譯。
● mm:在一個目錄中編譯這個工程。
● mmm:編譯某個工程,參數為某個工程的路徑。
● cgrep:格式化查找C文件。
● jgrep:格式化查找Java文件。
● resgrep:格式化查找資源(xml)文件。
● godir:到包含某個文件的目錄。
設置完成環境變量后,幾個grep命令可以方便開發過程中的查找,它們實際上都是find和grep命令的結合。例如,格式化查找資源文件的方法如下:
$ resgrep "android:icon"
此時,將會列出包含“android:icon”字串的所有xml文件的相關行,并且用彩色表示行號和“android:icon”字串本身。
cgrep和jgrep的使用方法類似,前者將在擴展名為.c,.cc,.cpp和.h的文件中進行查找;后者在擴展名為.java的文件中進行查找。
↘ 2.1.4 系統構建結果
Android系統構建的結果全部在其根目錄的out目錄中,原始的各個工程的目錄不會改動,也不會生成內容。默認情況下生成的是名稱為generic的產品,表示“通用”的產品,可以用之運行仿真器。
Android編譯的結果包含以下的內容。
● 主機工具和其依賴的內容(out/host/)。
● 目標機程序(out/target/)。
● 目標機映像文件(out/target/product/<產品名稱>)。
out目錄的結構如下所示(帶有[]表示是目錄):
out/ |-- host [主機內容] | |-- common [主機的通用內容] | | `-- obj | `-- linux-x86 [編譯所生成的主機Linux運行的工具] | |-- bin | |-- framework | |-- lib | `-- obj `-- target [目標機內容] |-- common [目標機的通用內容] | |-- R | |-- docs | `-- obj `-- product [目標機的產品目錄] `-- <TARGET_PRODUCT>
其中out/target/product目錄是目標產品的目錄,編譯的產品名稱由TARGET_PRODUCT宏表示。在默認的情況下使用generic作為目標產品的名稱。產品目錄結構如下所示(帶有[]表示是目錄):
out/target/product/generic/ |-- android-info.txt |-- clean_steps.mk |-- data [數據目錄] |-- installed-files.txt |-- obj [中間目標文件目錄] | |-- APPS [apk應用程序包] | |-- ETC [運行時配置文件] | |-- EXECUTABLES [可執行程序] | |-- KEYCHARS | |-- NOTICE.html | |-- NOTICE.html.gz | |-- NOTICE_FILES | |-- PACKAGING | |-- SHARED_LIBRARIES [動態庫(共享庫)] | |-- STATIC_LIBRARIES [靜態庫(歸檔文件)] | |-- include | `-- lib |-- previous_build_config.mk |-- ramdisk.img 根文件系統映像 |-- root [根文件系統目錄] |-- symbols [符號的目錄] |-- system [主文件系統目錄] |-- system.img 主文件系統映像 |-- userdata-qemu.img QEMU的數據文件系統映像 `-- userdata.img 數據文件系統映像
系統構建的第一個步驟是“編譯”,將生成目錄obj中的內容,是目標機的各個目標。其中,EXECUTABLES為可執行程序目錄,SHARED_LIBRARIES為動態庫(*.so)目錄,STATIC_LIBRARIES為靜態庫(*.a)目錄,APPS為Android中的應用程序包(*.apk)目錄。
系統構建的第二個步驟是“安裝”,將生成root、system、data這3個目錄,分別是目標根文件系統、主文件系統和數據文件系統的目錄。
系統構建的第三個步驟是“生成映像”,將根據3 個文件系統目錄生成ramdisk.img、system.img和userdata.img。“生成映像”的步驟是一個簡單而機械的動作,只是根據3 個目錄生成3個映像,這樣目錄中的內容確定,映像文件的內容不會有變化。