这篇博客的目标是摸清楚默认编译整个android系统时代码的流程。
当我们执行make的时候,会查找当前的Makefie文件或者makefile文件并且执行,在android顶级源码目录下面,确实有个Makefile,它之后一行内容:
- ### DO NOT EDIT THIS FILE ###
- include build/core/main.mk
- ### DO NOT EDIT THIS FILE ###
一.依赖浅析
当我们执行make命令的时候,如果没有传入一个目标,那么就会执行默认的目标。注意,我们在编译android系统的时候,只需要执行make就可以了,那么很显然它会执行默认的目标了,那么默认的目标是什么呢?
在build/core/main.mk中:
- # 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中:
- 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
- define get-all-product-makefiles
- $(call get-product-makefiles,$(_find-android-products-files))
- endef
- 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
- 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都加载进来了,但是我们只需要我们产品的配置信息呀,所以接着做一个查找,找到属于我们产品的AndroidProducts.mk:
- # 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:
- 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
- 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)
4然后获取TARGET_DEVICE的值:
- # Find the device that this product maps to.
- TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)
5获取要拷贝的文件
- # 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中:
- # 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
- 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
- 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
product_MODULES是所有产品配置文件中添加的要打包进系统镜像中的模块,它只是一个名字,比如上篇博客分析过的screencap。
product_FILES获取对应模块的.INSTALLED的值。
- define module-installed-files
- $(foreach module,$(1),$(ALL_MODULES.$(module).INSTALLED))
- endef
$(ALL_MODULES.$(target)).BUILT $(ALL_MODULES.$(target)).INSTALLED
它们在base_rule.mk中生成:
- 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 全部安装模块
- 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就是全部的要安装的模块了。
- 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目标定义的地方来看,没有看到它的依赖,但我们向下搜索,就会发现:
- .PHONY: apps_only
- apps_only: $(unbundled_build_modules)
- droid: apps_only
- # Building a full system-- the default is to build droidcore
- droid: droidcore dist_files
我们会发现它有出现了两个依赖,那它到底依赖哪一个呢?
droid依赖哪一个取决于ifneq ($(TARGET_BUILD_APPS),)是否成立,也就是有没有给TARGET_BUILD_APPS赋值过,源码如下:
- 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的定义:
- # 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的定义:
- # dist_files only for putting your library into the dist directory with a full build.
- .PHONY: dist_files
4.2.files
它的第一个目标是files:
- # All the droid stuff, in directories
- .PHONY: files
- files: prebuilt \
- $(modules_to_install) \
- $(INSTALLED_ANDROID_INFO_TXT_TARGET)、
1.1files又依赖了三个目标,第一个是prebuilt:
- # -------------------------------------------------------------------
- # 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是一些预编译模块:
- 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会为这个目标生成依赖关系链,也就是会给其中的每一个模块生成依赖关系链,然后编译每一个模块,这个过程在上一节中已经说过了。
至此,所有应该编译的模块都已经被编译,剩下的就是打包镜像文件了。这将在下一节讨论。