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

函数lunch的执行逻辑如下所示:

        1. 检查是否带有参数,即位置参数$1是否等于空。如果不等于空的话,就表明带有参数,并且该参数是用来指定要编译的设备型号及其编译类型的。如果等于空的话,那么就调用另外一个函数print_lunch_menu来显示Lunch菜单项,并且通过调用read函数来等待用户输入。无论通过何种方式,最终变量answer的值就保存了用户所指定的备型号及其编译类型。

        2. 对变量answer的值的合法性进行检查。如果等于空的话,就将它设置为默认值“full-eng”。如果不等于空的话,就分为三种情况考虑。第一种情况是值为数字,那么就需要确保该数字的大小不能超过Lunch菜单项的个数。在这种情况下,会将输入的数字索引到数组LUNCH_MENU_CHOICES中去,以便获得一个用来表示设备型号及其编译类型的文本。第二种情况是非数字文本,那么就需要确保该文本符合<product>-<variant>的形式,其中<product>表示设备型号,而<variant>表示编译类型 。第三种情况是除了前面两种情况之外的所有情况,这是非法的。经过合法性检查后,变量selection代表了用户所指定的备型号及其编译类型,如果它的值是非法的,即它的值等于空,那么函数lunch就不往下执行了。

        3. 接下来是解析变量selection的值,也就是通过sed命令将它的<product>和<variant>值提取出来,并且分别保存在变量product和variant中。提取出来的product和variant值有可能是不合法的,因此需要进一步通过调用函数check_product和check_variant来检查。一旦检查失败,也就是函数check_product和check_variant的返回值$?等于非0,那么函数lunch就不往下执行了。

        4. 通过以上合法性检查之后,就将变量product和variant的值保存在环境变量TARGET_PRODUCT和TARGET_BUILD_VARIANT中。此外,另外一个环境变量TARGET_BUILD_TYPE的值会被设置为"release",表示此次编译是一个release版本的编译。另外,前面还有一个环境变量TARGET_BUILD_APPS,它的值被函数lunch设置为空,用来表示此次编译是对整个系统进行编译。如果环境变量TARGET_BUILD_APPS的值不等于空,那么就表示此次编译是只对某些APP模块进行编译,而这些APP模块就是由环境变量TARGET_BUILD_APPS来指定的。

        5. 调用函数set_stuff_for_environment来配置环境,例如设置Java SDK路径和交叉编译工具路径等。

        6. 调用函数printfconfig来显示已经配置好的编译环境参数。

        在上述执行过程中,函数check_product、check_variant和printconfig是比较关键的,因此接下来我们就继续分析它们的实现。

        函数check_product定义在文件build/envsetup.sh中,它的实现如下所示:

[plain]  view plain copy
  1. # check to see if the supplied product is one we can build  
  2. function check_product()  
  3. {  
  4.     T=$(gettop)  
  5.     if [ ! "$T" ]; then  
  6.         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2  
  7.         return  
  8.     fi  
  9.     CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \  
  10.         TARGET_PRODUCT=$1 \  
  11.         TARGET_BUILD_VARIANT= \  
  12.         TARGET_BUILD_TYPE= \  
  13.         TARGET_BUILD_APPS= \  
  14.         get_build_var TARGET_DEVICE > /dev/null  
  15.     # hide successful answers, but allow the errors to show  
  16. }  
        函数gettop用来返回Android源代码工程的根目录。函数check_product需要在Android源代码工程根目录或者子目录下调用。否则的话,函数check_product就出错返回。

        接下来函数check_product设置几个环境变量,其中最重要的是前面三个CALLED_FROM_SETUP、BUILD_SYSTEM和TARGET_PRODUCT。环境变量CALLED_FROM_SETUP的值等于true表示接下来执行的make命令是用来初始化Android编译环境的。环境变量BUILD_SYSTEM用来指定Android编译系统的核心目录,它的值被设置为build/core。环境变量TARGET_PRODUCT用来表示要检查的产品名称(也就是我们前面说的设备型号),它的值被设置为$1,即函数check_product的调用参数。

        最后函数check_product调用函数get_build_var来检查由环境变量TARGET_PRODUCT指定的产品名称是否合法,注意,它的调用参数为TARGET_DEVICE。

        函数get_build_var定义在文件build/envsetup.sh中,它的实现如下所示:

[plain]  view plain copy
  1. # Get the exact value of a build variable.  
  2. function get_build_var()  
  3. {  
  4.     T=$(gettop)  
  5.     if [ ! "$T" ]; then  
  6.         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2  
  7.         return  
  8.     fi  
  9.     CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \  
  10.       make --no-print-directory -C "$T" -f build/core/config.mk dumpvar-$1  
  11. }  
        这里就可以看到,函数get_build_var实际上就是通过make命令在Android源代码工程根目录中执行build/core/config.mk文件,并且将make目标设置为dumpvar-$1,也就是dumpvar-TARGET_DEVICE。

        文件build/core/config.mk的内容比较多,这里我们只关注与产品名称合法性检查相关的逻辑,这些逻辑也基本上涵盖了Android编译系统初始化的逻辑,如下所示:

[plain]  view plain copy
  1. ......  
  2.   
  3. # ---------------------------------------------------------------  
  4. # Define most of the global variables.  These are the ones that  
  5. # are specific to the user's build configuration.  
  6. include $(BUILD_SYSTEM)/envsetup.mk //定义下文的TARGET_DEVICE
  7.   
  8. # Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)  
  9. # or under vendor/*/$(TARGET_DEVICE).  Search in both places, but  
  10. # make sure only one exists.  
  11. # Real boards should always be associated with an OEM vendor.  
  12. board_config_mk := \  
  13.     $(strip $(wildcard \  
  14.         $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \  //加载TARGET_DEVICE目录下的BoardConfig.mk
  15.         device/*/$(TARGET_DEVICE)/BoardConfig.mk \  
  16.         vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \  
  17.     ))  
  18. ifeq ($(board_config_mk),)  
  19.   $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))  
  20. endif  
  21. ifneq ($(words $(board_config_mk)),1)  
  22.   $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))  
  23. endif  
  24. include $(board_config_mk)  
  25.   
  26. ......  
  27.   
  28. include $(BUILD_SYSTEM)/dumpvar.mk  
       上述代码主要就是将envsetup.mk、BoardConfig,mk和dumpvar.mk三个Makefile片段文件加载进来。其中,envsetup.mk文件位于$(BUILD_SYSTEM)目录中,也就是build/core目录中,BoardConfig.mk文件的位置主要就是由环境变量TARGET_DEVICE来确定,它是用来描述目标产品的硬件模块信息的,例如CPU体系结构。环境变量TARGET_DEVICE用来描述目标设备,它的值是在envsetup.mk文件加载的过程中确定的。一旦目标设备确定后,就可以在$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)、device/*/$(TARGET_DEVICE)和vendor/*/$(TARGET_DEVICE)目录中找到对应的BoradConfig.mk文件。注意,变量SRC_TARGET_DIR的值等于build/target。最后,dumpvar.mk文件也是位于build/core目录中,它用来打印已经配置好的编译环境信息。

        接下来我们就通过进入到build/core/envsetup.mk文件来分析变量TARGET_DEVICE的值是如何确定的:

[plain]  view plain copy
  1. # Read the product specs so we an get TARGET_DEVICE and other  
  2. # variables that we need in order to locate the output files.  
  3. include $(BUILD_SYSTEM)/product_config.mk  
      它通过加载另外一个文件build/core/product_config.mk文件来确定变量TARGET_DEVICE以及其它与目标产品相关的变量的值。

       文件build/core/product_config.mk的内容很多,这里我们只关注变量TARGET_DEVICE设置相关的逻辑,如下所示:

[plain]  view plain copy
  1. ......  
  2.   
  3. ifneq ($(strip $(TARGET_BUILD_APPS)),)  
  4. # An unbundled app build needs only the core product makefiles.  
  5. all_product_configs := $(call get-product-makefiles,\  
  6.     $(SRC_TARGET_DIR)/product/AndroidProducts.mk)//仅仅是加载(SRC_TARGET_DIR)/product下的AndroidProduct.mk文件
  7. else  
  8. # Read in all of the product definitions specified by the AndroidProducts.mk  
  9. # files in the tree.  
  10. all_product_configs := $(get-all-product-makefiles)  //加载所有AndroidProducts.mk,分析见下面
  11. endif  
  12.   
  13. # all_product_configs consists items like:  
  14. # <product_name>:<path_to_the_product_makefile>  
  15. # or just <path_to_the_product_makefile> in case the product name is the  
  16. # same as the base filename of the product config makefile.  
  17. current_product_makefile :=  
  18. all_product_makefiles :=  
  19. $(foreach f, $(all_product_configs),\  
  20.     $(eval _cpm_words := $(subst :,$(space),$(f)))\  
  21.     $(eval _cpm_word1 := $(word 1,$(_cpm_words)))\  
  22.     $(eval _cpm_word2 := $(word 2,$(_cpm_words)))\  
  23.     $(if $(_cpm_word2),\  
  24.         $(eval all_product_makefiles += $(_cpm_word2))\  
  25.         $(if $(filter $(TARGET_PRODUCT),$(_cpm_word1)),\  
  26.             $(eval current_product_makefile += $(_cpm_word2)),),\  
  27.         $(eval all_product_makefiles += $(f))\  
  28.         $(if $(filter $(TARGET_PRODUCT),$(basename $(notdir $(f)))),\  
  29.             $(eval current_product_makefile += $(f)),)))  
  30. _cpm_words :=  
  31. _cpm_word1 :=  
  32. _cpm_word2 :=  
  33. current_product_makefile := $(strip $(current_product_makefile)) //rk3288_box.mk 
  34. all_product_makefiles := $(strip $(all_product_makefiles))  
  35.   
  36. ifneq (,$(filter product-graph dump-products, $(MAKECMDGOALS)))  
  37. # Import all product makefiles.  
  38. $(call import-products, $(all_product_makefiles)) //例如提取rk3288_box.mk
  39. else  
  40. # Import just the current product.  
  41. ifndef current_product_makefile  
  42. $(error Cannot locate config makefile for product "$(TARGET_PRODUCT)")  
  43. endif  
  44. ifneq (1,$(words $(current_product_makefile)))  
  45. $(error Product "$(TARGET_PRODUCT)" ambiguous: matches $(current_product_makefile))  
  46. endif  
  47. $(call import-products, $(current_product_makefile))  
  48. endif  # Import all or just the current product makefile  
  49.   
  50. ......  
  51.   
  52. # Convert a short name like "sooner" into the path to the product  
  53. # file defining that product.  
  54. #  
  55. INTERNAL_PRODUCT := $(call resolve-short-product-name, $(TARGET_PRODUCT))//是一个mk文件
  56. ifneq ($(current_product_makefile),$(INTERNAL_PRODUCT))  
  57. $(error PRODUCT_NAME inconsistent in $(current_product_makefile) and $(INTERNAL_PRODUCT))  
  58. endif  
  59. current_product_makefile :=  
  60. all_product_makefiles :=  
  61. all_product_configs :=  
  62.   
  63. # Find the device that this product maps to.  
  64. TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE) 
  65. //如下所示:
  66. //PRODUCTS.build/target/product/full.mk.PRODUCT_NAME := full  
  67. //PRODUCTS.build/target/product/full.mk.PRODUCT_DEVICE := generic  
  68. //PRODUCTS.build/target/product/full.mk.PRODUCT_BRAND := Android  
  69. //PRODUCTS.build/target/product/full.mk.PRODUCT_MODEL := Full Android on Emulator 
     
  70. ......  
       上述代码的执行逻辑如下所示:

       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
  1. #  
  2. # Returns the sorted concatenation of all PRODUCT_MAKEFILES  
  3. # variables set in all AndroidProducts.mk files.  
  4. # $(call ) isn't necessary.  
  5. #  
  6. define get-all-product-makefiles  
  7. $(call get-product-makefiles,$(_find-android-products-files))  
  8. 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
  1. #  
  2. # Returns the list of all AndroidProducts.mk files.  
  3. # $(call ) isn't necessary.  
  4. #  
  5. define _find-android-products-files  
  6. $(shell test -d device && find device -maxdepth 6 -name AndroidProducts.mk) \  
  7.   $(shell test -d vendor && find vendor -maxdepth 6 -name AndroidProducts.mk) \  
  8.   $(SRC_TARGET_DIR)/product/AndroidProducts.mk  
  9. endef  
      从这里就可以看出,Android源代码目录中定义的所有AndroidProducts.mk文件位于device、vendor或者build/target/product目录或者相应的子目录(最深是6层)中。

      函数get-product-makefiles也是定义在文件build/core/product.mk中,如下所示:(与get-all-product-makefiles区别是加载的范围不一样)

[plain]  view plain copy
  1. #  
  2. # Returns the sorted concatenation of PRODUCT_MAKEFILES  
  3. # variables set in the given AndroidProducts.mk files.  
  4. # $(1): the list of AndroidProducts.mk files.  
  5. #  
  6. define get-product-makefiles  
  7. $(sort \  
  8.   $(foreach f,$(1), \  
  9.     $(eval PRODUCT_MAKEFILES :=) \  
  10.     $(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f)))) \  
  11.     $(eval include $(f)) \  
  12.     $(PRODUCT_MAKEFILES) \  
  13.    ) \  
  14.   $(eval PRODUCT_MAKEFILES :=) \  
  15.   $(eval LOCAL_DIR :=) \  
  16.  )  
  17. endef  
       这个函数实际上就是遍历参数$1所描述的AndroidProucts.mk文件列表,并且将定义在这些AndroidProucts.mk文件中的变量PRODUCT_MAKEFILES的值提取出来,形成一个列表返回给调用者。

       例如,在build/target/product/AndroidProducts.mk文件中,变量PRODUCT_MAKEFILES的值如下所示:

[plain]  view plain copy
  1. # Unbundled apps will be built with the most generic product config.  
  2. ifneq ($(TARGET_BUILD_APPS),)  
  3. PRODUCT_MAKEFILES := \  
  4.     $(LOCAL_DIR)/full.mk \  
  5.     $(LOCAL_DIR)/full_x86.mk \  
  6.     $(LOCAL_DIR)/full_mips.mk  
  7. else  
  8. PRODUCT_MAKEFILES := \  
  9.     $(LOCAL_DIR)/core.mk \  
  10.     $(LOCAL_DIR)/generic.mk \  
  11.     $(LOCAL_DIR)/generic_x86.mk \  
  12.     $(LOCAL_DIR)/generic_mips.mk \  
  13.     $(LOCAL_DIR)/full.mk \  
  14.     $(LOCAL_DIR)/full_x86.mk \  
  15.     $(LOCAL_DIR)/full_mips.mk \  
  16.     $(LOCAL_DIR)/vbox_x86.mk \  
  17.     $(LOCAL_DIR)/sdk.mk \  
  18.     $(LOCAL_DIR)/sdk_x86.mk \  
  19.     $(LOCAL_DIR)/sdk_mips.mk \  
  20.     $(LOCAL_DIR)/large_emu_hw.mk  
  21. endif 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值