SuperIC社区_
标题: Android编译系统环境初始化过程分析(2) [打印本页]
作者: liuwei    时间: 2016-9-28 14:06
标题: Android编译系统环境初始化过程分析(2)
   接下来我们就通过进入到build/core/envsetup.mk文件来分析变量TARGET_DEVICE的值是如何确定的:
[plain] view plain copy
 

- # Read the product specs so we an get TARGET_DEVICE and other  
 - # variables that we need in order to locate the output files.  
 - include $(BUILD_SYSTEM)/product_config.mk  
 
       它通过加载另外一个文件build/core/product_config.mk文件来确定变量TARGET_DEVICE以及其它与目标产品相关的变量的值。
       文件build/core/product_config.mk的内容很多,这里我们只关注变量TARGET_DEVICE设置相关的逻辑,如下所示:
[plain] view plain copy
 

- ......  
 -   
 - 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 definiti** specified by the AndroidProducts.mk  
 - # files in the tree.  
 - all_product_configs := $(get-all-product-makefiles)  
 - endif  
 -   
 - # all_product_configs c**ists 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))  
 -   
 - 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 Cannot 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  
 -   
 - ......  
 -   
 - # Convert a short name like "sooner" into the path to the product  
 - # file defining that product.  
 - #  
 - INTERNAL_PRODUCT := $(call resolve-short-product-name, $(TARGET_PRODUCT))  
 - ifneq ($(current_product_makefile),$(INTERNAL_PRODUCT))  
 - $(error PRODUCT_NAME inc**istent in $(current_product_makefile) and $(INTERNAL_PRODUCT))  
 - endif  
 - current_product_makefile :=  
 - all_product_makefiles :=  
 - all_product_configs :=  
 -   
 - # Find the device that this product maps to.  
 - TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)  
 -   
 - ......  
 
       上述代码的执行逻辑如下所示:
       1. 检查环境变量TARGET_BUILD_APPS的值是否等于空。如果不等于空,那么就说明此次编译不是针对整个系统,因此只要将核心的产品相关的Makefile文件加载进来就行了,否则的话,就要将所有与产品相关的Makefile文件加载进来的。核心产品Makefile文件在$(SRC_TARGET_DIR)/product/AndroidProducts.mk文件中指定,也就是在build/target/product/AndroidProducts.mk文件,通过调用函数get-product-makefiles可以获得。所有与产品相关的Makefile文件可以通过另外一个函数get-all-product-makefiles获得。无论如何,最终获得的产品Makefie文件列表保存在变量all_product_configs中。
       2. 遍历变量all_product_configs所描述的产品Makefile列表,并且在这些Makefile文件中,找到名称与环境变量TARGET_PRODUCT的值相同的文件,保存在另外一个变量current_product_makefile中,作为需要为当前指定的产品所加载的Makefile文件列表。在这个过程当中,上一步找到的所有的产品Makefile文件也会保存在变量all_product_makefiles中。注意,环境变量TARGET_PRODUCT的值是在我们执行lunch命令的时候设置并且传递进来的。
       3.  如果指定的make目标等于product-graph或者dump-products,那么就将所有的产品相关的Makefile文件加载进来,否则的话,只加载与目标产品相关的Makefile文件。从前面的分析可以知道,此时的make目标为dumpvar-TARGET_DEVICE,因此接下来只会加载与目标产品,即$(TARGET_PRODUCT),相关的Makefile文件,这是通过调用另外一个函数import-products实现的。
       4. 调用函数resolve-short-product-name解析环境变量TARGET_PRODUCT的值,将它变成一个Makefile文件路径。并且保存在变量INTERNAL_PRODUCT中。这里要求变量INTERNAL_PRODUCT和current_product_makefile的值相等,否则的话,就说明用户指定了一个非法的产品名称。
       5. 找到一个名称为PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE的变量,并且将它的值保存另外一个变量TARGET_DEVICE中。变量PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE是在加载产品Makefile文件的过程中定义的,用来描述当前指定的产品的名称。
       上述过程主要涉及到了get-all-product-makefiles、import-products和resolve-short-product-name三个关键函数,理解它们的执行过程对理解Android编译系统的初始化过程很有帮助,接下来我们分别分析它们的实现。
        函数get-all-product-makefiles定义在文件build/core/product.mk中,如下所示:
[plain] view plain copy
 

- #  
 - # Returns the sorted concatenation of all PRODUCT_MAKEFILES  
 - # variables set in all AndroidProducts.mk files.  
 - # $(call ) isn't necessary.  
 - #  
 - define get-all-product-makefiles  
 - $(call get-product-makefiles,$(_find-android-products-files))  
 - endef  
 
       它首先是调用函数_find-android-products-files来找到Android源代码目录中定义的所有AndroidProducts.mk文件,然后再调用函数get-product-makefiles获得在这里AndroidProducts.mk文件里面定义的产品Makefile文件。
       函数_find-android-products-files也是定义在文件build/core/product.mk中,如下所示:
[plain] view plain copy
 

- #  
 - # Returns the list of all AndroidProducts.mk files.  
 - # $(call ) isn't necessary.  
 - #  
 - 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  
 
      从这里就可以看出,Android源代码目录中定义的所有AndroidProducts.mk文件位于device、vendor或者build/target/product目录或者相应的子目录(最深是6层)中。
      函数get-product-makefiles也是定义在文件build/core/product.mk中,如下所示:
[plain] view plain copy
 

- #  
 - # Returns the sorted concatenation of PRODUCT_MAKEFILES  
 - # variables set in the given AndroidProducts.mk files.  
 - # $(1): the list of AndroidProducts.mk files.  
 - #  
 - 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  
 
       这个函数实际上就是遍历参数$1所描述的AndroidProucts.mk文件列表,并且将定义在这些AndroidProucts.mk文件中的变量PRODUCT_MAKEFILES的值提取出来,形成一个列表返回给调用者。
       例如,在build/target/product/AndroidProducts.mk文件中,变量PRODUCT_MAKEFILES的值如下所示:
[plain] view plain copy
 

- # Unbundled apps will be built with the most generic product config.  
 - ifneq ($(TARGET_BUILD_APPS),)  
 - PRODUCT_MAKEFILES := \  
 -     $(LOCAL_DIR)/full.mk \  
 -     $(LOCAL_DIR)/full_x86.mk \  
 -     $(LOCAL_DIR)/full_mips.mk  
 - else  
 - PRODUCT_MAKEFILES := \  
 -     $(LOCAL_DIR)/core.mk \  
 -     $(LOCAL_DIR)/generic.mk \  
 -     $(LOCAL_DIR)/generic_x86.mk \  
 -     $(LOCAL_DIR)/generic_mips.mk \  
 -     $(LOCAL_DIR)/full.mk \  
 -     $(LOCAL_DIR)/full_x86.mk \  
 -     $(LOCAL_DIR)/full_mips.mk \  
 -     $(LOCAL_DIR)/vbox_x86.mk \  
 -     $(LOCAL_DIR)/sdk.mk \  
 -     $(LOCAL_DIR)/sdk_x86.mk \  
 -     $(LOCAL_DIR)/sdk_mips.mk \  
 -     $(LOCAL_DIR)/large_emu_hw.mk  
 - endif  
 
       这里列出的每一个文件都对应于一个产品。
       我们再来看函数import-products的实现,它定义在文件build/core/product.mk中,如下所示:
[plain] view plain copy
 

- #  
 - # $(1): product makefile list  
 - #  
 - #TODO: check to make sure that products have all the necessary vars defined  
 - define import-products  
 - $(call import-nodes,PRODUCTS,$(1),$(_product_var_list))  
 - endef  
 
       它调用另外一个函数import-nodes来加载由参数$1所指定的产品Makefile文件,并且指定了另外两个参数PRODUCTS和$(_product_var_list)。其中,变量_product_var_list也是定义在文件build/core/product.mk中,它的值如下所示:
[plain] view plain copy
 

- _product_var_list := \  
 -     PRODUCT_NAME \  
 -     PRODUCT_MODEL \  
 -     PRODUCT_LOCALES \  
 -     PRODUCT_AAPT_CONFIG \  
 -     PRODUCT_AAPT_PREF_CONFIG \  
 -     PRODUCT_PACKAGES \  
 -     PRODUCT_PACKAGES_DEBUG \  
 -     PRODUCT_PACKAGES_ENG \  
 -     PRODUCT_PACKAGES_TESTS \  
 -     PRODUCT_DEVICE \  
 -     PRODUCT_MANUFACTURER \  
 -     PRODUCT_BRAND \  
 -     PRODUCT_PROPERTY_OVERRIDES \  
 -     PRODUCT_DEFAULT_PROPERTY_OVERRIDES \  
 -     PRODUCT_CHARACTERISTICS \  
 -     PRODUCT_COPY_FILES \  
 -     PRODUCT_OTA_PUBLIC_KEYS \  
 -     PRODUCT_EXTRA_RECOVERY_KEYS \  
 -     PRODUCT_PACKAGE_OVERLAYS \  
 -     DEVICE_PACKAGE_OVERLAYS \  
 -     PRODUCT_TAGS \  
 -     PRODUCT_SDK_ADDON_NAME \  
 -     PRODUCT_SDK_ADDON_COPY_FILES \  
 -     PRODUCT_SDK_ADDON_COPY_MODULES \  
 -     PRODUCT_SDK_ADDON_DOC_MODULES \  
 -     PRODUCT_DEFAULT_WIFI_CHANNELS \  
 -     PRODUCT_DEFAULT_DEV_CERTIFICATE \  
 -     PRODUCT_RESTRICT_VENDOR_FILES \  
 -     PRODUCT_VENDOR_KERNEL_HEADERS \  
 -     PRODUCT_FACTORY_RAMDISK_MODULES \  
 -     PRODUCT_FACTORY_BUNDLE_MODULES  
 
       它描述的是在产品Makefile文件中定义在各种变量。
       函数import-nodes定义在文件build/core/node_fns.mk中,如下所示:
[plain] view plain copy
 

- #  
 - # $(1): output list variable name, like "PRODUCTS" or "DEVICES"  
 - # $(2): list of makefiles representing nodes to import  
 - # $(3): list of node variable names  
 - #  
 - define import-nodes  
 - $(if \  
 -   $(foreach _in,$(2), \  
 -     $(eval _node_import_context := _nic.$(1).[[$(_in)]]) \  
 -     $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \  
 -                 should be empty here: $(_include_stack))),) \  
 -     $(eval _include_stack := ) \  
 -     $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3)) \  
 -     $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \  
 -     $(eval _node_import_context :=) \  
 -     $(eval $(1) := $($(1)) $(_in)) \  
 -     $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \  
 -                 should be empty here: $(_include_stack))),) \  
 -    ) \  
 - ,)  
 - endef  
 
       这个函数主要是做了三件事情:
       1. 调用函数_import-nodes-inner将参数$2描述的每一个产品Makefile文件加载进来。
       2. 调用函数move-var-list将定义在前面所加载的产品Makefile文件里面的由参数$3指定的变量的值分别拷贝到另外一组独立的变量中。
       3. 将参数$2描述的每一个产品Makefile文件路径以空格分隔保存在参数$1所描述的变量中,也就是保存在变量PRODUCTS中。
       上述第二件事情需要进一步解释一下。由于当前加载的每一个文件都会定义相同的变量,为了区分这些变量,我们需要在这些变量前面加一些前缀。例如,假设加载了build/target/product/full.mk这个产品Makefile文件,它里面定义了以下几个变量:
[plain] view plain copy
 

- # Overrides  
 - PRODUCT_NAME := full  
 - PRODUCT_DEVICE := generic  
 - PRODUCT_BRAND := Android  
 - PRODUCT_MODEL := Full Android on Emulator  
 
       当调用了函数move-var-list对它进行解析后,就会得到以下的新变量:
[plain] view plain copy
 

- PRODUCTS.build/target/product/full.mk.PRODUCT_NAME := full  
 - PRODUCTS.build/target/product/full.mk.PRODUCT_DEVICE := generic  
 - PRODUCTS.build/target/product/full.mk.PRODUCT_BRAND := Android  
 - PRODUCTS.build/target/product/full.mk.PRODUCT_MODEL := Full Android on Emulator  
 
       正是由于调用了函数move-var-list,我们在build/core/product_config.mk文件中可以通过PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE来设置变量TARGET_DEVICE的值。
| 欢迎光临 SuperIC社区_ (/) | 
Powered by Discuz! X3.3 |