zoukankan      html  css  js  c++  java
  • android编译系统分析(三)make


    http://blog.csdn.net/u011913612/article/details/52434411


    这篇博客的目标是摸清楚默认编译整个Android系统时代码的流程。

    当我们执行make的时候,会查找当前的Makefie文件或者makefile文件并且执行,在android顶级源码目录下面,确实有个Makefile,它之后一行内容:

    1. ### DO NOT EDIT THIS FILE ###  
    2. include build/core/main.mk  
    3. ### DO NOT EDIT THIS FILE ###  
    因此,正真执行的是build/core/main.mk

    一.依赖浅析

    当我们执行make命令的时候,如果没有传入一个目标,那么就会执行默认的目标。注意,我们在编译android系统的时候,只需要执行make就可以了,那么很显然它会执行默认的目标了,那么默认的目标是什么呢?

    在build/core/main.mk中:

    1. # This is the default target.  It must be the first declared target.  
    2. .PHONY: droid  
    3. DEFAULT_GOAL := droid  
    4. $(DEFAULT_GOAL):  

    在main.mk开始不久,就出现了一个伪目标,即便你看不懂Makefile也没有关系,注释上说的很清楚了,他就是默认的目标了。而且这个默认的目标是一个伪目标。make工具遇到伪目标以后,会检查解析伪目标的依赖,如果伪目标存在依赖,就会检查这些依赖,如果这些依赖是伪目标,继续检查这个伪目标的依赖,如果不是伪目标,就会生成这个目标。

    阅读一个Makefile,理清目标的依赖关系很重,下图列出了部分重要的以来关系:


    在对依赖关系有个了解之后,我们开始顺着make的加载流程,看看它到底做了什么。

    首先,我觉得很重要的就是加载特定产品的配置信息。

    二.配置产品信息

    首先,大致的流程如下图所示:


    在product_config.mk中:

    1. ifneq ($(strip $(TARGET_BUILD_APPS)),)  
    2. # An unbundled app build needs only the core product makefiles.  
    3. all_product_configs := $(call get-product-makefiles,  
    4.     $(SRC_TARGET_DIR)/product/AndroidProducts.mk)  
    5. else  
    6. # Read in all of the product definitions specified by the AndroidProducts.mk  
    7. # files in the tree.  
    8. all_product_configs := $(get-all-product-makefiles)  
    9. endif  

    1.AndoridProducts.mk

    使用get-all-product-makefiles获取所有的AndoridProducts.mk文件:
    1. define get-all-product-makefiles  
    2. $(call get-product-makefiles,$(_find-android-products-files))  
    3. endef  
    调用_find-android-products-files获取所有的AndroidProducts.mk,然后交由get-product-makefiles函数处理。
    1. define _find-android-products-files  
    2. $(shell test -d device && find device -maxdepth 6 -name AndroidProducts.mk)   
    3.   $(shell test -d vendor && find vendor -maxdepth 6 -name AndroidProducts.mk)   
    4.   $(SRC_TARGET_DIR)/product/AndroidProducts.mk  
    5. endef  
    1. define get-product-makefiles  
    2. $(sort   
    3.   $(foreach f,$(1),   
    4.     $(eval PRODUCT_MAKEFILES :=)   
    5.     $(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f))))   
    6.     $(eval include $(f))   
    7.     $(PRODUCT_MAKEFILES)   
    8.    )   
    9.   $(eval PRODUCT_MAKEFILES :=)   
    10.   $(eval LOCAL_DIR :=)   
    11.  )  
    12. endef  
    可以看到最终处理的结果是加载了AndroidProducts.mk, 返回了一个排好顺序的PRODUCT_MAKEFILES。

    这里把所有的AndroidProducts.mk都加载进来了,但是我们只需要我们产品的配置信息呀,所以接着做一个查找,找到属于我们产品的AndroidProducts.mk:

    1. # Find the product config makefile for the current product.  
    2. # all_product_configs consists items like:  
    3. # <product_name>:<path_to_the_product_makefile>  
    4. # or just <path_to_the_product_makefile> in case the product name is the  
    5. # same as the base filename of the product config makefile.  
    6. current_product_makefile :=  
    7. all_product_makefiles :=  
    8. $(foreach f, $(all_product_configs),  
    9.     $(eval _cpm_words := $(subst :,$(space),$(f)))  
    10.     $(eval _cpm_word1 := $(word 1,$(_cpm_words)))  
    11.     $(eval _cpm_word2 := $(word 2,$(_cpm_words)))  
    12.     $(if $(_cpm_word2),  
    13.         $(eval all_product_makefiles += $(_cpm_word2))  
    14.         $(if $(filter $(TARGET_PRODUCT),$(_cpm_word1)),  
    15.             $(eval current_product_makefile += $(_cpm_word2)),),  
    16.         $(eval all_product_makefiles += $(f))  
    17.         $(if $(filter $(TARGET_PRODUCT),$(basename $(notdir $(f)))),  
    18.             $(eval current_product_makefile += $(f)),)))  
    19. _cpm_words :=  
    20. _cpm_word1 :=  
    21. _cpm_word2 :=  
    22. current_product_makefile := $(strip $(current_product_makefile))  
    23. all_product_makefiles := $(strip $(all_product_makefiles))  

    2.current_product_makefile

    最终找到的结果存储在current_product_makefile中。关于它的值,这里举例说明:

    加入我们在lunch的时候选择了 5:

    1. 1. aosp_arm-eng  
    2. 2. aosp_arm64-eng  
    3. 3. aosp_mips-eng  
    4. 4. aosp_mips64-eng  
    5. 5. aosp_x86-eng  
    6. 6. aosp_x86_64-eng  
    那么经过以上查找current_product_makefile就等于device/generic/x86/mini_x86.mk

    3.加载产品配置文件

    1. ifneq (,$(filter product-graph dump-products, $(MAKECMDGOALS)))  
    2. # Import all product makefiles.  
    3. $(call import-products, $(all_product_makefiles))  
    4. else  
    5. # Import just the current product.  
    6. ifndef current_product_makefile  
    7. $(error Can not locate config makefile for product "$(TARGET_PRODUCT)")  
    8. endif  
    9. ifneq (1,$(words $(current_product_makefile)))  
    10. $(error Product "$(TARGET_PRODUCT)" ambiguous: matches $(current_product_makefile))  
    11. endif  
    12. $(call import-products, $(current_product_makefile))  
    13. endif  # Import all or just the current product makefile  
    14.   
    15. # Sanity check  
    16. $(check-all-products)  
    在import-products中导入产品的配置信息,这里就是device/generic/x86/mini_x86.mk。

    4然后获取TARGET_DEVICE的值:

    1. # Find the device that this product maps to.  
    2. TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)  
    此时,TARGET_DEVICE = mini_x86.mk;

    5获取要拷贝的文件

    1. # A list of words like <source path>:<destination path>[:<owner>].  
    2. # The file at the source path should be copied to the destination path  
    3. # when building  this product.  <destination path> is relative to  
    4. # $(PRODUCT_OUT), so it should look like, e.g., "system/etc/file.xml".  
    5. # The rules for these copy steps are defined in build/core/Makefile.  
    6. # The optional :<owner> is used to indicate the owner of a vendor file.  
    7. PRODUCT_COPY_FILES :=   
    8.     $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_COPY_FILES))  

    这个变量也很重要,它存储了需要拷贝的文件。格式为 <source path>:<destination path>,在build/core/Makefile一开始就会先拷贝这个变量指定的文件。

    6.加载BoardConfig.mk

    又回到envsetup.mk中:

    1. # Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)  
    2. # or under vendor/*/$(TARGET_DEVICE).  Search in both places, but  
    3. # make sure only one exists.  
    4. # Real boards should always be associated with an OEM vendor.  
    5. board_config_mk :=   
    6.     $(strip $(wildcard   
    7.         $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk   
    8.         $(shell test -d device && find device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk')   
    9.         $(shell test -d vendor && find vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk')   
    10.     ))  
    11. ifeq ($(board_config_mk),)  
    12.   $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))  
    13. endif  
    14. ifneq ($(words $(board_config_mk)),1)  
    15.   $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))  
    16. endif  
    17. include $(board_config_mk)  
    18. ifeq ($(TARGET_ARCH),)  
    19.   $(error TARGET_ARCH not defined by board config: $(board_config_mk))  
    20. endif  

    BoardConfig.mk中配置了重要的板级信息,比如cpu架构等。

    至此,配置一个产品所需的AndroidProducts.mk,具体产品的配置文件,比如这里的mini_x86.mk以及BoardConfig.mk都加载进来了。


    三.加载所有模块

    加载完单板信息,make又回到main.mk中,不就就发现了ONE_SHOT_MAKEFILE变量的判断:

    1ONE_SHOT_MAKEFILE

    1. ifneq ($(ONE_SHOT_MAKEFILE),)  
    2. # We've probably been invoked by the "mm" shell function  
    3. # with a subdirectory's makefile.  
    4. include $(ONE_SHOT_MAKEFILE)  
    5. # Change CUSTOM_MODULES to include only modules that were  
    6. # defined by this makefile; this will install all of those  
    7. # modules as a side-effect.  Do this after including ONE_SHOT_MAKEFILE  
    8. # so that the modules will be installed in the same place they  
    9. # would have been with a normal make.  
    10. CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS)))  
    11. FULL_BUILD :=  
    12. # Stub out the notice targets, which probably aren't defined  
    13. # when using ONE_SHOT_MAKEFILE.  
    14. NOTICE-HOST-%: ;  
    15. NOTICE-TARGET-%: ;  
    16.   
    17. # A helper goal printing out install paths  
    18. .PHONY: GET-INSTALL-PATH  
    19. GET-INSTALL-PATH:  
    20.     @$(foreach m, $(ALL_MODULES), $(if $(ALL_MODULES.$(m).INSTALLED),   
    21.         echo 'INSTALL-PATH: $(m) $(ALL_MODULES.$(m).INSTALLED)';))  
    22.   
    23. else # ONE_SHOT_MAKEFILE  
    24.   
    25. ifneq ($(dont_bother),true)  
    26. #  
    27. # Include all of the makefiles in the system  
    28. #  
    29.   
    30. # Can't use first-makefiles-under here because  
    31. # --mindepth=2 makes the prunes not work.  
    32. subdir_makefiles :=   
    33.     $(shell build/tools/findleaves.py --prune=$(OUT_DIR) --prune=.repo --prune=.git $(subdirs) Android.mk)  
    34.   
    35. $(foreach mk, $(subdir_makefiles), $(info including $(mk) ...)$(eval include $(mk)))  
    36.   
    37. endif # dont_bother  
    38.   
    39. endif # ONE_SHOT_MAKEFILE  
    如果这个变量定义了,那么,就是编译一个模块,在上一篇博客中已将分析过了,如果没有定义,就说明是编译整个系统。
    MAKECMDGOALS是make的一个环境变量,当我们执行make的时候并没有设置它,因此它为空。所以dont_bother不等于true,因此,就会加载所有的Android.mk.这里使用
    一个python脚本查找系统中所有的Android.mk,然后Include进来。
    四 收集所有要安装的模块
    在main.mk中继续往下看:

    3.1FULL_BUILD

    1. ifdef FULL_BUILD  
    2.   # The base list of modules to build for this product is specified  
    3.   # by the appropriate product definition file, which was included  
    4.   # by product_config.mk.  
    5.   product_MODULES := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES)  
    6.   # Filter out the overridden packages before doing expansion  
    7.   product_MODULES := $(filter-out $(foreach p, $(product_MODULES),   
    8.       $(PACKAGES.$(p).OVERRIDES)), $(product_MODULES))  
    9.   
    10.   # Resolve the :32 :64 module name  
    11.   modules_32 := $(patsubst %:32,%,$(filter %:32, $(product_MODULES)))  
    12.   modules_64 := $(patsubst %:64,%,$(filter %:64, $(product_MODULES)))  
    13.   modules_rest := $(filter-out %:32 %:64,$(product_MODULES))  
    14.   # Note for 32-bit product, $(modules_32) and $(modules_64) will be  
    15.   # added as their original module names.  
    16.   product_MODULES := $(call get-32-bit-modules-if-we-can, $(modules_32))  
    17.   product_MODULES += $(modules_64)  
    18.   # For the rest we add both  
    19.   product_MODULES += $(call get-32-bit-modules, $(modules_rest))  
    20.   product_MODULES += $(modules_rest)  
    21.   
    22.   $(call expand-required-modules,product_MODULES,$(product_MODULES))  
    23.   
    24.   product_FILES := $(call module-installed-files, $(product_MODULES))  
    25.   ifeq (0,1)  
    26.     $(info product_FILES for $(TARGET_DEVICE) ($(INTERNAL_PRODUCT)):)  
    27.     $(foreach p,$(product_FILES),$(info :   $(p)))  
    28.     $(error done)  
    29.   endif  
    30. else  
    31.   # We're not doing a full build, and are probably only including  
    32.   # a subset of the module makefiles.  Don't try to build any modules  
    33.   # requested by the product, because we probably won't have rules  
    34.   # to build them.  
    35.   product_FILES :=  
    36. endif  
    在执行make的时候,FULL_BUILD:=true
    product_MODULES是所有产品配置文件中添加的要打包进系统镜像中的模块,它只是一个名字,比如上篇博客分析过的screencap。
    product_FILES获取对应模块的.INSTALLED的值。
    1. define module-installed-files  
    2. $(foreach module,$(1),$(ALL_MODULES.$(module).INSTALLED))  
    3. endef  
    在加载单个模块的时候,会给每一个模块生成另外两个值:
    $(ALL_MODULES.$(target)).BUILT
    $(ALL_MODULES.$(target)).INSTALLED
    它们在base_rule.mk中生成:
    1. ALL_MODULES.$(my_register_name).BUILT :=   
    2.     $(ALL_MODULES.$(my_register_name).BUILT) $(LOCAL_BUILT_MODULE)  
    3. ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))  
    4. ALL_MODULES.$(my_register_name).INSTALLED :=   
    5.     $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) $(LOCAL_INSTALLED_MODULE))  
    6. ALL_MODULES.$(my_register_name).BUILT_INSTALLED :=   
    7.     $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) $(LOCAL_BUILT_MODULE):$(LOCAL_INSTALLED_MODULE))  
    8. endif  
    $(ALL_MODULES.$(target)).BUILT代表的一般是out/target/product/xxx/obj下编译生成的模块。
    $(ALL_MODULES.$(target)).INSTALLED代表的是out/target/product/xxx/system下生成的模块。

    3.2 全部安装模块

    1. modules_to_install := $(sort   
    2.     $(ALL_DEFAULT_INSTALLED_MODULES)   
    3.     $(product_FILES)   
    4.     $(foreach tag,$(tags_to_install),$($(tag)_MODULES))   
    5.     $(CUSTOM_MODULES)   
    6.   )  
    ALL_DEFAULT_INSTALLED_MODULES是系统默认要安装的模块,product_FILES是特定产品附加的要安装的模块,foreach找到的是特定
    TAG的模块,以及加上CUSTOM_MODULES,这样modules_to_install就是全部的要安装的模块了。
    
    1. ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)  
    2. include $(BUILD_SYSTEM)/Makefile  
    然后把modules_to_install的值全部赋给ALL_DEFAULT_INSTALLED_MODULES,接着加载build/core/Makefile。这个Makefile会使用
    ALL_DEFAULT_INSTALLED_MODULES变量最终生成所有的镜像文件。生成镜像文件的过程放在下一节讨论。


    四.编译所有模块

    依赖关系我们在一开始就做了简单的梳理,现在开始分析编译所有模块的依赖关系。

    从droid目标定义的地方来看,没有看到它的依赖,但我们向下搜索,就会发现:

    1. .PHONY: apps_only  
    2. apps_only: $(unbundled_build_modules)  
    3.   
    4. droid: apps_only  
    1. # Building a full system-- the default is to build droidcore  
    2. droid: droidcore dist_files  

    我们会发现它有出现了两个依赖,那它到底依赖哪一个呢?

    droid依赖哪一个取决于ifneq ($(TARGET_BUILD_APPS),)是否成立,也就是有没有给TARGET_BUILD_APPS赋值过,源码如下:

    1. ifneq ($(TARGET_BUILD_APPS),)  
    2.   # If this build is just for apps, only build apps and not the full system by default.  
    3.   
    4.   unbundled_build_modules :=  
    5.   ifneq ($(filter all,$(TARGET_BUILD_APPS)),)  
    6.     # If they used the magic goal "all" then build all apps in the source tree.  
    7.     unbundled_build_modules := $(foreach m,$(sort $(ALL_MODULES)),$(if $(filter APPS,$(ALL_MODULES.$(m).CLASS)),$(m)))  
    8.   else  
    9.     unbundled_build_modules := $(TARGET_BUILD_APPS)  
    10.   endif  
    11.   
    12. ...  
    13.   
    14. .PHONY: apps_only  
    15. apps_only: $(unbundled_build_modules)  
    16.   
    17. droid: apps_only  
    18.   
    19. else # TARGET_BUILD_APPS  
    20.   $(call dist-for-goals, droidcore,   
    21.     $(INTERNAL_UPDATE_PACKAGE_TARGET)   
    22.     $(INTERNAL_OTA_PACKAGE_TARGET)   
    23.     $(BUILT_OTATOOLS_PACKAGE)   
    24.     $(SYMBOLS_ZIP)   
    25.     $(INSTALLED_FILES_FILE)   
    26.     $(INSTALLED_BUILD_PROP_TARGET)   
    27.     $(BUILT_TARGET_FILES_PACKAGE)   
    28.     $(INSTALLED_ANDROID_INFO_TXT_TARGET)   
    29.     $(INSTALLED_RAMDISK_TARGET)   
    30.    )  
    31. # Building a full system-- the default is to build droidcore  
    32. droid: droidcore dist_files  
    33.   
    34. endif # TARGET_BUILD_APPS  

    我们期望的是整个系统的编译,所以,droid依赖的是droidcore 和 dist_files

    4.1droidcore的定义:

    1. # Build files and then package it into the rom formats  
    2. .PHONY: droidcore  
    3. droidcore: files   
    4.     systemimage   
    5.     $(INSTALLED_BOOTIMAGE_TARGET)   
    6.     $(INSTALLED_RECOVERYIMAGE_TARGET)   
    7.     $(INSTALLED_USERDATAIMAGE_TARGET)   
    8.     $(INSTALLED_CACHEIMAGE_TARGET)   
    9.     $(INSTALLED_VENDORIMAGE_TARGET)   
    10.     $(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的定义:

    1. # dist_files only for putting your library into the dist directory with a full build.  
    2. .PHONY: dist_files  
    从定义来看,dist_files也是个伪目标,并且它没有任何依赖,作用是完整编译系统的时候拷贝库文件。


    4.2.files

    它的第一个目标是files:

    1. # All the droid stuff, in directories  
    2. .PHONY: files  
    3. files: prebuilt   
    4.         $(modules_to_install)   
    5.         $(INSTALLED_ANDROID_INFO_TXT_TARGET)、  

    1.1files又依赖了三个目标,第一个是prebuilt:

    1. # -------------------------------------------------------------------  
    2. # This is used to to get the ordering right, you can also use these,  
    3. # but they're considered undocumented, so don't complain if their  
    4. # behavior changes.  
    5. .PHONY: prebuilt  
    6. prebuilt: $(ALL_PREBUILT)  

    prebuilt又是一个伪目标,它又依赖于ALL_PREBUILT变量指向的目标,ALL_PREBUILT是一些预编译模块:

    1. Android.mk (makefileframeworksasecmdsmgr):ALL_PREBUILT += $(TARGET_OUT)/bin/bmgr  
    2. Android.mk (makefileframeworksasecmdsime):ALL_PREBUILT += $(TARGET_OUT)/bin/ime  
    3. Android.mk (makefileframeworksasecmdsinput):ALL_PREBUILT += $(TARGET_OUT)/bin/input  
    4. Android.mk (makefileframeworksasecmdspm):ALL_PREBUILT += $(TARGET_OUT)/bin/pm  
    5. Android.mk (makefileframeworksasecmdssvc):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会为这个目标生成依赖关系链,也就是会给其中的每一个模块生成依赖关系链,然后编译每一个模块,这个过程在上一节中已经说过了。
    至此,所有应该编译的模块都已经被编译,剩下的就是打包镜像文件了。这将在下一节讨论。



  • 相关阅读:
    case 条件语句
    shell 函数
    if条件语句
    IP地址分类和分段
    shell脚本的条件测试与比较
    bc命令
    [LeetCode]Remove Duplicates from Sorted Array II
    [LeetCode]Merge Two Sorted Lists
    [LeetCode]Climbing Stairs
    [LeetCode]Merge Intervals
  • 原文地址:https://www.cnblogs.com/ztguang/p/12645309.html
Copyright © 2011-2022 走看看