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