本文基于AOSP的android-6.0.1_r9
分支木羹,介紹Android平臺編譯系統(tǒng)中的Makefile绷跑。
(本文轉(zhuǎn)載自本人的個人網(wǎng)站宾毒,謝絕二次轉(zhuǎn)載鸣峭。原文鏈接:http://note.qidong.name/2017/08/android-6.0-makefile/)
簡介
之所以選android-6.0.1_r9
這個分支前普,是因為這是最后一個純Makefile的大版本肚邢。后面隨著時間的發(fā)展,Android項目變得越來越龐大拭卿,純Makefile編譯系統(tǒng)已經(jīng)越來越不堪使用骡湖。使用Makefile,不僅擴(kuò)展不便峻厚,而且執(zhí)行效率也不太高响蕴。從7.0版本開始,Android已經(jīng)開始用Ninja來替代Makefile惠桃。
6.0版本浦夷,是Makefile最后的輝煌懊渡。Android平臺的編譯系統(tǒng),其實就是用Makefile寫出來的一個獨(dú)立項目军拟。這個項目剃执,不僅把分散在數(shù)百個Git庫中的代碼整合起來、統(tǒng)一編譯懈息,而且還把產(chǎn)物分門別類地輸出到一個目錄肾档,打包成手機(jī)ROM,更能產(chǎn)生應(yīng)用開發(fā)時所使用的SDK辫继、NDK怒见、網(wǎng)頁文檔等。以前姑宽,從來沒有這么大規(guī)模的一個手寫Makefile項目遣耍,以后應(yīng)該也不會再有了。
在大炮车、中型項目都普遍使用高級工具來生成Makefile的時候舵变,Android竟然還是手寫Makefile。現(xiàn)在看來瘦穆,這也是一件咄咄怪事纪隙。
主要文件
在Android項目根目錄,都有一個Makefile文件扛或,其核心內(nèi)容只有一行绵咱。
### DO NOT EDIT THIS FILE ###
include build/core/main.mk
### DO NOT EDIT THIS FILE ###
所以,build/core/main.mk
就是Android真正的Makefile入口熙兔。
在Android 6.0以前悲伶,主要的Makefile都在build/core
目錄下。在Android 7.0以后住涉,全部調(diào)整到了build/make/core
中麸锉,因為build目錄下,又新增了kati秆吵、soong淮椰、blueprint等項目的目錄。
Android的編譯系統(tǒng)的Makefile文件纳寂,主要分成三部分:
-
核心組件,全部在
build/core/
目錄下泻拦。
這里的幾十個mk文件毙芜,是編譯系統(tǒng)的核心內(nèi)容,定義了編譯流程的框架争拐。 -
產(chǎn)品組件腋粥,出現(xiàn)在
build/target/
晦雨、device/
或vendor/
目錄中。
這部分的主要入口是AndroidProducts.mk文件隘冲,功能是指定一些產(chǎn)品獨(dú)特的內(nèi)容闹瞧。這些額外的組件,會隨著編譯前lunch PRODUCT
的PRODUCT參數(shù)而改變展辞。 - 模塊組件奥邮,入口為各個Git庫的Android.mk。
這是普通平臺開發(fā)者最熟悉的文件罗珍,其中定義了一個模塊的必要參數(shù)洽腺,使模塊跟隨平臺編譯。
本文只分析核心組件中的結(jié)構(gòu)覆旱,以及build/core/
下的一些重要文件蘸朋。
核心組件
結(jié)構(gòu)
build/core/main.mk
├── help.mk
├── config.mk
│ ├── pathmap.mk
│ ├── envsetup.mk
│ │ ├── version_defaults.mk
│ │ └── product_config.mk
│ │ ├── node_fns.mk
│ │ ├── product.mk
│ │ └── device.mk
│ ├── combo/select.mk
│ ├── ccache.mk
│ ├── combo/javac.mk
│ ├── clang/config.mk
│ │ ├── clang/HOST_$(HOST_2ND_ARCH).mk
│ │ ├── clang/HOST_$(HOST_ARCH).mk
│ │ ├── clang/TARGET_$(TARGET_2ND_ARCH).mk
│ │ └── clang/TARGET_$(TARGET_ARCH).mk
│ └── dumpvar.mk
├── cleanbuild.mk
│ └── cleanspec.mk
├── definitions.mk
│ └── distdir.mk
├── dex_preopt.mk
│ └── dex_preopt_libart.mk
│ └── dex_preopt_libart_boot.mk
├── pdk_config.mk
├── post_clean.mk
├── legacy_prebuilts.mk
└── Makefile
├── sdk_font.mk
└── tasks/*.mk
以上結(jié)構(gòu)代表main.mk
中對其它Makefile的include關(guān)系。排列順序扣唱,大致依照文件中include的順序藕坯,但要注意,Makefile的排列順序不一定代表執(zhí)行順序噪沙。
這個圖中僅列出了build/core
目錄下的文件堕担,不包括其它目錄下被包含的mk文件。并且曲聂,build/core
目錄下總計近百個mk文件霹购,這里也未列出沒被包含到main.mk
中的那些。
部分文件說明
文件 | 作用 |
---|---|
main.mk | make命令入口朋腋,是整個Android編譯系統(tǒng)最核心的文件齐疙。 |
help.mk | 在執(zhí)行make help 時打印幾個主要的Target信息。 |
config.mk | 定義了編譯過程中的環(huán)境變量旭咽,包括BUILD_* 贞奋、TARGET_* 等。它include的其它mk文件穷绵,也是類似作用轿塔。 |
cleanbuild.mk | 定義了完整編譯前,清理產(chǎn)物的步驟和內(nèi)容仲墨。 |
definitions.mk | 定義了大量編譯過程中會用到的函數(shù)勾缭,如my-dir 、all-subdir-makefile 等目养。 |
dex_preopt.mk | 利用dexopt (Dalvik)或dex2oat (ART)對dex進(jìn)行優(yōu)化将谊。 |
pdk_config.mk | 編譯PDK(Platform Developement Kit)的產(chǎn)物platform.zip 岖食。 |
post_clean.mk | 針對應(yīng)用層模塊的產(chǎn)物清理谎仲。 |
legacy_prebuilts.mk | 定義了GRANDFATHERED_ALL_PREBUILT 變量,指定一些預(yù)編譯Target兜畸。 |
Makefile | 這是一個功能復(fù)雜的文件,可以看做main.mk的延伸碘梢。 |
在上述Makefile文件中咬摇,還include了tasks/*.mk
,其中內(nèi)容如下:
- apicheck.mk
- boot_jars_package_check.mk
- build_custom_images.mk
- collect_gpl_sources.mk
- cts.mk
- deps_licenses.mk
- ide.mk
- oem_image.mk
- product-graph.mk
- sdk-addon.mk
- vendor_module_check.mk
其作用可以通過文件名來推測煞躬,這里不再贅述肛鹏。
產(chǎn)品組件的相關(guān)文件
產(chǎn)品組件的主要入口是AndroidProducts.mk文件,而product_config.mk就是提供相關(guān)支持的文件汰翠。文件頭中龄坪,有一段注釋:
# Generic functions
# TODO: Move these to definitions.make once we're able to include
# definitions.make before config.make.
可見,這個文件原本只是一個臨時的獨(dú)立文件复唤,但因為歷史原因健田,一直沿用至今。這也體現(xiàn)了這個Makefile編譯系統(tǒng)的一些固有缺陷佛纫。
其中的核心邏輯如下:
ifneq ($(strip $(TARGET_BUILD_APPS)),)
# An unbundled app build needs only the core product makefiles.
all_product_configs := $(call get-product-makefiles,\
$(SRC_TARGET_DIR)/product/AndroidProducts.mk)
else
# Read in all of the product definitions specified by the AndroidProducts.mk
# files in the tree.
all_product_configs := $(get-all-product-makefiles)
endif
$(SRC_TARGET_DIR)/product/AndroidProducts.mk
妓局,就是build/target/product/AndroidProducts.mk
,是系統(tǒng)默認(rèn)自帶的文件呈宇,內(nèi)含aosp-arm
好爬、aosp-arm64
等PRODUCT。而get-all-product-makefiles
則是在項目的device/
甥啄、vendor/
兩個目錄下存炮,查找6層以內(nèi)的所有AndroidProducts.mk文件。
它include的三個文件蜈漓,與definitions.mk類似穆桂,分別定義一些函數(shù)。比如融虽,product.mk中定義了get-product-makefiles
享完、check-all-products
等函數(shù),提供了查找AndroidProducts.mk文件有额、列出所有可以被lunch
的PRODUCT參數(shù)等功能般又。
所以,如果要新增一個PRODUCT巍佑,只需在device/
或vendor/
下合適的位置茴迁,新增一個AndroidProducts.mk,指定PRODUCT_*
的各項參數(shù)即可句狼。
Android.mk的相關(guān)文件
在build/core/main.mk
中笋熬,有以下代碼:
subdir_makefiles := \
$(shell build/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) $(subdirs) Android.mk)
$(foreach mk, $(subdir_makefiles), $(info including $(mk) ...)$(eval include $(mk)))
這就是查找整個Android項目中所有的Android.mk文件,并且include進(jìn)來腻菇。Android.mk的組合方式胳螟,本質(zhì)上和AndroidProducts.mk并無不同。只是AndroidProducts.mk的范圍小一些筹吐,而Android.mk則是在整個項目中搜索糖耸。
除此之外,build/core/
下還有很多mk文件丘薛,雖然沒有直接被main.mk所include嘉竟,卻會被各種Android.mk所include。比如洋侨,clear_vars.mk舍扰、package.mk等。
新增一個LOCAL_MODULE希坚,只需要在任意位置新增一個Android.mk边苹,指定LOCAL_*
參數(shù)即可。篇幅所限裁僧,不對此做詳細(xì)介紹个束。
其它文件
除了Makefile文件以外,在build/
下還包含了其它的工具聊疲,主要集中在build/tools/
目錄下茬底。很多用Makefile做起來不方便的工作,都由它們?nèi)プ觥?/p>
這些工具获洲,以Python阱表、Bash腳本為主,也包含部分C語言寫的微型項目贡珊,比如build/tools/acp/
最爬。
總結(jié)
我在深入研究Android的編譯系統(tǒng)前,從未想過Makefile會有這樣壯麗的風(fēng)景飞崖。利用Makefile的依賴管理烂叔,構(gòu)建一個龐大而復(fù)雜的編譯系統(tǒng),這無疑是一個令人驚嘆的構(gòu)思固歪。
不過蒜鸡,無論如何,它也快走到了盡頭牢裳。
如果說Bash的語法逢防,令人難以把握,那么Makefile則更是令人每學(xué)每忘蒲讯。假如早知道要以Makefile為主忘朝,Python、Bash判帮、乃至C語言為輔局嘁,構(gòu)建這樣一個大型項目溉箕,說不定2003年組建之初的Android團(tuán)隊,就不會這樣選擇悦昵。若是以Python作為膠水肴茄,粘合各個模塊的Makefile,也許能經(jīng)久不衰但指。
Google從7.0開始寡痰,逐步用各種工具替換Makefile,利弊參半棋凳。在編譯前用kati來把Makefile轉(zhuǎn)換為Ninja拦坠,在實際編譯各模塊時用Ninja替代Makefile。歷時12年剩岳,Android中的Makefile終于在2016年贞滨,開始退場。