這篇博客的目標是摸清楚默認編譯整個android系統時代碼的流程。
當我們執行make的時候,會查找當前的Makefie文件或者makefile文件并且執行,在android頂級源碼目錄下面,確實有個Makefile,它之后一行內容:
[plain]?view plaincopy
###?DO?NOT?EDIT?THIS?FILE?###??include?build/core/main.mk??###?DO?NOT?EDIT?THIS?FILE?###??
因此,正真執行的是build/core/main.mk
一.依賴淺析
當我們執行make命令的時候,如果沒有傳入一個目標,那么就會執行默認的目標。注意,我們在編譯android系統的時候,只需要執行make就可以了,那么很顯然它會執行默認的目標了,那么默認的目標是什么呢?
在build/core/main.mk中:
[plain]?view plaincopy
#?This?is?the?default?target.??It?must?be?the?first?declared?target.??.PHONY:?droid??DEFAULT_GOAL?:=?droid??$(DEFAULT_GOAL):??
在main.mk開始不久,就出現了一個偽目標,即便你看不懂Makefile也沒有關系,注釋上說的很清楚了,他就是默認的目標了。而且這個默認的目標是一個偽目標。make工具遇到偽目標以后,會檢查解析偽目標的依賴,如果偽目標存在依賴,就會檢查這些依賴,如果這些依賴是偽目標,繼續檢查這個偽目標的依賴,如果不是偽目標,就會生成這個目標。
閱讀一個Makefile,理清目標的依賴關系很重,下圖列出了部分重要的以來關系:
在對依賴關系有個了解之后,我們開始順著make的加載流程,看看它到底做了什么。
首先,我覺得很重要的就是加載特定產品的配置信息。
二.配置產品信息
首先,大致的流程如下圖所示:
在product_config.mk中:
[java]?view plaincopy
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??
1.AndoridProducts.mk 使用get-all-product-makefiles獲取所有的AndoridProducts.mk文件:
[java]?view plaincopy
define?get-all-product-makefiles??$(call?get-product-makefiles,$(_find-android-products-files))??endef??
調用_find-android-products-files獲取所有的AndroidProducts.mk,然后交由get-product-makefiles函數處理。
[java]?view plaincopy
define?_find-android-products-files??$(shell?test?-d?device?&&?find?device?-maxdepth?6?-name?AndroidProducts.mk)?\????$(shell?test?-d?vendor?&&?find?vendor?-maxdepth?6?-name?AndroidProducts.mk)?\????$(SRC_TARGET_DIR)/product/AndroidProducts.mk??endef??
[java]?view plaincopy
define?get-product-makefiles??$(sort?\????$(foreach?f,$(1),?\??????$(eval?PRODUCT_MAKEFILES?:=)?\??????$(eval?LOCAL_DIR?:=?$(patsubst?%/,%,$(dir?$(f))))?\??????$(eval?include?$(f))?\??????$(PRODUCT_MAKEFILES)?\?????)?\????$(eval?PRODUCT_MAKEFILES?:=)?\????$(eval?LOCAL_DIR?:=)?\???)??endef??
可以看到最終處理的結果是加載了AndroidProducts.mk, 返回了一個排好順序的PRODUCT_MAKEFILES。
這里把所有的AndroidProducts.mk都加載進來了,但是我們只需要我們產品的配置信息呀,所以接著做一個查找,找到屬于我們產品的AndroidProducts.mk:
[plain]?view plaincopy
#?Find?the?product?config?makefile?for?the?current?product.??#?all_product_configs?consists?items?like:??#?<product_name>:<path_to_the_product_makefile>??#?or?just?<path_to_the_product_makefile>?in?case?the?product?name?is?the??#?same?as?the?base?filename?of?the?product?config?makefile.??current_product_makefile?:=??all_product_makefiles?:=??$(foreach?f,?$(all_product_configs),\??????$(eval?_cpm_words?:=?$(subst?:,$(space),$(f)))\??????$(eval?_cpm_word1?:=?$(word?1,$(_cpm_words)))\??????$(eval?_cpm_word2?:=?$(word?2,$(_cpm_words)))\??????$(if?$(_cpm_word2),\??????????$(eval?all_product_makefiles?+=?$(_cpm_word2))\??????????$(if?$(filter?$(TARGET_PRODUCT),$(_cpm_word1)),\??????????????$(eval?current_product_makefile?+=?$(_cpm_word2)),),\??????????$(eval?all_product_makefiles?+=?$(f))\??????????$(if?$(filter?$(TARGET_PRODUCT),$(basename?$(notdir?$(f)))),\??????????????$(eval?current_product_makefile?+=?$(f)),)))??_cpm_words?:=??_cpm_word1?:=??_cpm_word2?:=??current_product_makefile?:=?$(strip?$(current_product_makefile))??all_product_makefiles?:=?$(strip?$(all_product_makefiles))??
2.current_product_makefile
最終找到的結果存儲在current_product_makefile中。關于它的值,這里舉例說明:
加入我們在lunch的時候選擇了 5:
[plain]?view plaincopy
1.?aosp_arm-eng??2.?aosp_arm64-eng??3.?aosp_mips-eng??4.?aosp_mips64-eng??5.?aosp_x86-eng??6.?aosp_x86_64-eng??
那么經過以上查找current_product_makefile就等于device/generic/x86/mini_x86.mk
3.加載產品配置文件
[plain]?view plaincopy
ifneq?(,$(filter?product-graph?dump-products,?$(MAKECMDGOALS)))??#?Import?all?product?makefiles.??$(call?import-products,?$(all_product_makefiles))??else??#?Import?just?the?current?product.??ifndef?current_product_makefile??$(error?Can?not?locate?config?makefile?for?product?"$(TARGET_PRODUCT)")??endif??ifneq?(1,$(words?$(current_product_makefile)))??$(error?Product?"$(TARGET_PRODUCT)"?ambiguous:?matches?$(current_product_makefile))??endif??$(call?import-products,?$(current_product_makefile))??endif??#?Import?all?or?just?the?current?product?makefile????#?Sanity?check??$(check-all-products)??
在import-products中導入產品的配置信息,這里就是device/generic/x86/mini_x86.mk。
4然后獲取TARGET_DEVICE的值:
[plain]?view plaincopy
#?Find?the?device?that?this?product?maps?to.??TARGET_DEVICE?:=?$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)??
此時,TARGET_DEVICE =?mini_x86.mk;
5獲取要拷貝的文件
[plain]?view plaincopy
#?A?list?of?words?like?<source?path>:<destination?path>[:<owner>].??#?The?file?at?the?source?path?should?be?copied?to?the?destination?path??#?when?building??this?product.??<destination?path>?is?relative?to??#?$(PRODUCT_OUT),?so?it?should?look?like,?e.g.,?"system/etc/file.xml".??#?The?rules?for?these?copy?steps?are?defined?in?build/core/Makefile.??#?The?optional?:<owner>?is?used?to?indicate?the?owner?of?a?vendor?file.??PRODUCT_COPY_FILES?:=?\??????$(strip?$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_COPY_FILES))??
這個變量也很重要,它存儲了需要拷貝的文件。格式為?<source path>:<destination path>,在build/core/Makefile一開始就會先拷貝這個變量指定的文件。
6.加載BoardConfig.mk
又回到envsetup.mk中:
[plain]?view plaincopy
#?Boards?may?be?defined?under?$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)??#?or?under?vendor/*/$(TARGET_DEVICE).??Search?in?both?places,?but??#?make?sure?only?one?exists.??#?Real?boards?should?always?be?associated?with?an?OEM?vendor.??board_config_mk?:=?\??????$(strip?$(wildcard?\??????????$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk?\??????????$(shell?test?-d?device?&&?find?device?-maxdepth?4?-path?'*/$(TARGET_DEVICE)/BoardConfig.mk')?\??????????$(shell?test?-d?vendor?&&?find?vendor?-maxdepth?4?-path?'*/$(TARGET_DEVICE)/BoardConfig.mk')?\??????))??ifeq?($(board_config_mk),)????$(error?No?config?file?found?for?TARGET_DEVICE?$(TARGET_DEVICE))??endif??ifneq?($(words?$(board_config_mk)),1)????$(error?Multiple?board?config?files?for?TARGET_DEVICE?$(TARGET_DEVICE):?$(board_config_mk))??endif??include?$(board_config_mk)??ifeq?($(TARGET_ARCH),)????$(error?TARGET_ARCH?not?defined?by?board?config:?$(board_config_mk))??endif??
BoardConfig.mk中配置了重要的板級信息,比如cpu架構等。
至此,配置一個產品所需的AndroidProducts.mk,具體產品的配置文件,比如這里的mini_x86.mk以及BoardConfig.mk都加載進來了。
三.加載所有模塊
加載完單板信息,make又回到main.mk中,不就就發現了ONE_SHOT_MAKEFILE變量的判斷:
1ONE_SHOT_MAKEFILE
[plain]?view plaincopy
ifneq?($(ONE_SHOT_MAKEFILE),)??#?We've?probably?been?invoked?by?the?"mm"?shell?function??#?with?a?subdirectory's?makefile.??include?$(ONE_SHOT_MAKEFILE)??#?Change?CUSTOM_MODULES?to?include?only?modules?that?were??#?defined?by?this?makefile;?this?will?install?all?of?those??#?modules?as?a?side-effect.??Do?this?after?including?ONE_SHOT_MAKEFILE??#?so?that?the?modules?will?be?installed?in?the?same?place?they??#?would?have?been?with?a?normal?make.??CUSTOM_MODULES?:=?$(sort?$(call?get-tagged-modules,$(ALL_MODULE_TAGS)))??FULL_BUILD?:=??#?Stub?out?the?notice?targets,?which?probably?aren't?defined??#?when?using?ONE_SHOT_MAKEFILE.??NOTICE-HOST-%:?;??NOTICE-TARGET-%:?;????#?A?helper?goal?printing?out?install?paths??.PHONY:?GET-INSTALL-PATH??GET-INSTALL-PATH:??????@$(foreach?m,?$(ALL_MODULES),?$(if?$(ALL_MODULES.$(m).INSTALLED),?\??????????echo?'INSTALL-PATH:?$(m)?$(ALL_MODULES.$(m).INSTALLED)';))????else?#?ONE_SHOT_MAKEFILE????ifneq?($(dont_bother),true)??#??#?Include?all?of?the?makefiles?in?the?system??#????#?Can't?use?first-makefiles-under?here?because??#?--mindepth=2?makes?the?prunes?not?work.??subdir_makefiles?:=?\??????$(shell?build/tools/findleaves.py?--prune=$(OUT_DIR)?--prune=.repo?--prune=.git?$(subdirs)?Android.mk)????$(foreach?mk,?$(subdir_makefiles),?$(info?including?$(mk)?...)$(eval?include?$(mk)))????endif?#?dont_bother????endif?#?ONE_SHOT_MAKEFILE??
如果這個變量定義了,那么,就是編譯一個模塊,在上一篇博客中已將分析過了,如果沒有定義,就說明是編譯整個系統。
MAKECMDGOALS是make的一個環境變量,當我們執行make的時候并沒有設置它,因此它為空。所以dont_bother不等于true,因此,就會加載所有的Android.mk.這里使用
一個python腳本查找系統中所有的Android.mk,然后Include進來。
四 收集所有要安裝的模塊
在main.mk中繼續往下看:
3.1FULL_BUILD
[plain]?view plaincopy
ifdef?FULL_BUILD????#?The?base?list?of?modules?to?build?for?this?product?is?specified????#?by?the?appropriate?product?definition?file,?which?was?included????#?by?product_config.mk.????product_MODULES?:=?$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES)????#?Filter?out?the?overridden?packages?before?doing?expansion????product_MODULES?:=?$(filter-out?$(foreach?p,?$(product_MODULES),?\????????$(PACKAGES.$(p).OVERRIDES)),?$(product_MODULES))??????#?Resolve?the?:32?:64?module?name????modules_32?:=?$(patsubst?%:32,%,$(filter?%:32,?$(product_MODULES)))????modules_64?:=?$(patsubst?%:64,%,$(filter?%:64,?$(product_MODULES)))????modules_rest?:=?$(filter-out?%:32?%:64,$(product_MODULES))????#?Note?for?32-bit?product,?$(modules_32)?and?$(modules_64)?will?be????#?added?as?their?original?module?names.????product_MODULES?:=?$(call?get-32-bit-modules-if-we-can,?$(modules_32))????product_MODULES?+=?$(modules_64)????#?For?the?rest?we?add?both????product_MODULES?+=?$(call?get-32-bit-modules,?$(modules_rest))????product_MODULES?+=?$(modules_rest)??????$(call?expand-required-modules,product_MODULES,$(product_MODULES))??????product_FILES?:=?$(call?module-installed-files,?$(product_MODULES))????ifeq?(0,1)??????$(info?product_FILES?for?$(TARGET_DEVICE)?($(INTERNAL_PRODUCT)):)??????$(foreach?p,$(product_FILES),$(info?:???$(p)))??????$(error?done)????endif??else????#?We're?not?doing?a?full?build,?and?are?probably?only?including????#?a?subset?of?the?module?makefiles.??Don't?try?to?build?any?modules????#?requested?by?the?product,?because?we?probably?won't?have?rules????#?to?build?them.????product_FILES?:=??endif??
在執行make的時候,FULL_BUILD:=true
product_MODULES是所有產品配置文件中添加的要打包進系統鏡像中的模塊,它只是一個名字,比如上篇博客分析過的screencap。
product_FILES獲取對應模塊的.INSTALLED的值。
[plain]?view plaincopy
define?module-installed-files??$(foreach?module,$(1),$(ALL_MODULES.$(module).INSTALLED))??endef??
在加載單個模塊的時候,會給每一個模塊生成另外兩個值:
$(ALL_MODULES.$(target)).BUILT
$(ALL_MODULES.$(target)).INSTALLED
它們在base_rule.mk中生成:
[plain]?view plaincopy
ALL_MODULES.$(my_register_name).BUILT?:=?\??????$(ALL_MODULES.$(my_register_name).BUILT)?$(LOCAL_BUILT_MODULE)??ifneq?(true,$(LOCAL_UNINSTALLABLE_MODULE))??ALL_MODULES.$(my_register_name).INSTALLED?:=?\??????$(strip?$(ALL_MODULES.$(my_register_name).INSTALLED)?$(LOCAL_INSTALLED_MODULE))??ALL_MODULES.$(my_register_name).BUILT_INSTALLED?:=?\??????$(strip?$(ALL_MODULES.$(my_register_name).BUILT_INSTALLED)?$(LOCAL_BUILT_MODULE):$(LOCAL_INSTALLED_MODULE))??endif??
$(ALL_MODULES.$(target)).BUILT代表的一般是out/target/product/xxx/obj下編譯生成的模塊。
$(ALL_MODULES.$(target)).INSTALLED代表的是out/target/product/xxx/system下生成的模塊。
3.2 全部安裝模塊
[plain]?view plaincopy
modules_to_install?:=?$(sort?\??????$(ALL_DEFAULT_INSTALLED_MODULES)?\??????$(product_FILES)?\??????$(foreach?tag,$(tags_to_install),$($(tag)_MODULES))?\??????$(CUSTOM_MODULES)?\????)??
ALL_DEFAULT_INSTALLED_MODULES是系統默認要安裝的模塊,product_FILES是特定產品附加的要安裝的模塊,foreach找到的是特定
TAG的模塊,以及加上CUSTOM_MODULES,這樣modules_to_install就是全部的要安裝的模塊了。
[plain]?view plaincopy
ALL_DEFAULT_INSTALLED_MODULES?:=?$(modules_to_install)??include?$(BUILD_SYSTEM)/Makefile??
然后把
modules_to_install的值全部賦給ALL_DEFAULT_INSTALLED_MODULES,接著加載build/core/Makefile。這個Makefile會使用
ALL_DEFAULT_INSTALLED_MODULES變量最終生成所有的鏡像文件。生成鏡像文件的過程放在下一節討論。
四.編譯所有模塊
依賴關系我們在一開始就做了簡單的梳理,現在開始分析編譯所有模塊的依賴關系。
從droid目標定義的地方來看,沒有看到它的依賴,但我們向下搜索,就會發現:
[plain]?view plaincopy
.PHONY:?apps_only??apps_only:?$(unbundled_build_modules)????droid:?apps_only??
[plain]?view plaincopy
#?Building?a?full?system--?the?default?is?to?build?droidcore??droid:?droidcore?dist_files??
我們會發現它有出現了兩個依賴,那它到底依賴哪一個呢?
droid依賴哪一個取決于ifneq ($(TARGET_BUILD_APPS),)是否成立,也就是有沒有給TARGET_BUILD_APPS賦值過,源碼如下:
[plain]?view plaincopy
ifneq?($(TARGET_BUILD_APPS),)????#?If?this?build?is?just?for?apps,?only?build?apps?and?not?the?full?system?by?default.??????unbundled_build_modules?:=????ifneq?($(filter?all,$(TARGET_BUILD_APPS)),)??????#?If?they?used?the?magic?goal?"all"?then?build?all?apps?in?the?source?tree.??????unbundled_build_modules?:=?$(foreach?m,$(sort?$(ALL_MODULES)),$(if?$(filter?APPS,$(ALL_MODULES.$(m).CLASS)),$(m)))????else??????unbundled_build_modules?:=?$(TARGET_BUILD_APPS)????endif????...????.PHONY:?apps_only??apps_only:?$(unbundled_build_modules)????droid:?apps_only????else?#?TARGET_BUILD_APPS????$(call?dist-for-goals,?droidcore,?\??????$(INTERNAL_UPDATE_PACKAGE_TARGET)?\??????$(INTERNAL_OTA_PACKAGE_TARGET)?\??????$(BUILT_OTATOOLS_PACKAGE)?\??????$(SYMBOLS_ZIP)?\??????$(INSTALLED_FILES_FILE)?\??????$(INSTALLED_BUILD_PROP_TARGET)?\??????$(BUILT_TARGET_FILES_PACKAGE)?\??????$(INSTALLED_ANDROID_INFO_TXT_TARGET)?\??????$(INSTALLED_RAMDISK_TARGET)?\?????)??#?Building?a?full?system--?the?default?is?to?build?droidcore??droid:?droidcore?dist_files????endif?#?TARGET_BUILD_APPS??
我們期望的是整個系統的編譯,所以,droid依賴的是droidcore 和 dist_files
4.1droidcore的定義:
[plain]?view plaincopy
#?Build?files?and?then?package?it?into?the?rom?formats??.PHONY:?droidcore??droidcore:?files?\??????systemimage?\??????$(INSTALLED_BOOTIMAGE_TARGET)?\??????$(INSTALLED_RECOVERYIMAGE_TARGET)?\??????$(INSTALLED_USERDATAIMAGE_TARGET)?\??????$(INSTALLED_CACHEIMAGE_TARGET)?\??????$(INSTALLED_VENDORIMAGE_TARGET)?\??????$(INSTALLED_FILES_FILE)??
可以droidcore又是一個偽目標,它又依賴于files 等一系列目標,從名字來看,這些目標應該是systemimage,userdataimage,recoryimage等,也就是說,droidcore的最終目的就是生成system.img,userdata.img等系統鏡像文件。
看到變量的定義就明白了:
1.boot.img:
INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
2.recovery.img:
INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
3.userdata.img:
INSTALLED_USERDATAIMAGE_TARGET := $(BUILT_USERDATAIMAGE_TARGET)
? --->BUILT_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img
4.cache.img
INSTALLED_CACHEIMAGE_TARGET := $(BUILT_CACHEIMAGE_TARGET)
?--->BUILT_CACHEIMAGE_TARGET := $(PRODUCT_OUT)/cache.img
5.vendor.img
INSTALLED_VENDORIMAGE_TARGET := $(BUILT_VENDORIMAGE_TARGET)
BUILT_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img
因此,droidcore的最終目的就是生成這些.Img文件。
dist_files的定義:
[plain]?view plaincopy
#?dist_files?only?for?putting?your?library?into?the?dist?directory?with?a?full?build.??.PHONY:?dist_files??
從定義來看,dist_files也是個偽目標,并且它沒有任何依賴,作用是完整編譯系統的時候拷貝庫文件。
4.2.files
它的第一個目標是files:
[plain]?view plaincopy
#?All?the?droid?stuff,?in?directories??.PHONY:?files??files:?prebuilt?\??????????$(modules_to_install)?\??????????$(INSTALLED_ANDROID_INFO_TXT_TARGET)、??
1.1files又依賴了三個目標,第一個是prebuilt:
[plain]?view plaincopy
#?-------------------------------------------------------------------??#?This?is?used?to?to?get?the?ordering?right,?you?can?also?use?these,??#?but?they're?considered?undocumented,?so?don't?complain?if?their??#?behavior?changes.??.PHONY:?prebuilt??prebuilt:?$(ALL_PREBUILT)??
prebuilt又是一個偽目標,它又依賴于ALL_PREBUILT變量指向的目標,ALL_PREBUILT是一些預編譯模塊:
[plain]?view plaincopy
Android.mk?(makefile\frameworks\base\cmds\bmgr):ALL_PREBUILT?+=?$(TARGET_OUT)/bin/bmgr??Android.mk?(makefile\frameworks\base\cmds\ime):ALL_PREBUILT?+=?$(TARGET_OUT)/bin/ime??Android.mk?(makefile\frameworks\base\cmds\input):ALL_PREBUILT?+=?$(TARGET_OUT)/bin/input??Android.mk?(makefile\frameworks\base\cmds\pm):ALL_PREBUILT?+=?$(TARGET_OUT)/bin/pm??Android.mk?(makefile\frameworks\base\cmds\svc):ALL_PREBUILT?+=?$(TARGET_OUT)/bin/svc??
4.3modules_to_install?
modules_to_install := $(sort \
? ? $(ALL_DEFAULT_INSTALLED_MODULES) \
? ? $(product_FILES) \
? ? $(foreach tag,$(tags_to_install),$($(tag)_MODULES)) \
? ? $(CUSTOM_MODULES) \
? )
這個變量之前已經分析過,它包含所有的要安裝的模塊,make會為這個目標生成依賴關系鏈,也就是會給其中的每一個模塊生成依賴關系鏈,然后編譯每一個模塊,這個過程在上一節中已經說過了。
至此,所有應該編譯的模塊都已經被編譯,剩下的就是打包鏡像文件了。這將在下一節討論。
總結
以上是生活随笔為你收集整理的Android编译系统分析三:make完整编译android系统的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。