搜索
 找回密码
 立即注册

简单一步 , 微信登陆

Android编译系统环境初始化过程分析(2)

作者:liuwei | 时间:2016-9-28 14:06:06 | 阅读:3466| 只看该作者

   接下来我们就通过进入到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的值。


收藏
收藏0
分享
分享
点赞
点赞0
反对
反对0
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册
手机版