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

来源:互联网 发布:网络女主播说唱歌曲 编辑:程序博客网 时间:2024/05/24 04:20
这里列出的每一个文件都对应于一个产品。

       我们再来看函数import-products的实现,它定义在文件build/core/product.mk中,如下所示:

[plain] view plaincopy
  1. #  
  2. # $(1): product makefile list  
  3. #  
  4. #TODO: check to make sure that products have all the necessary vars defined  
  5. define import-products  
  6. $(call import-nodes,PRODUCTS,$(1),$(_product_var_list))  
  7. endef  
       它调用另外一个函数import-nodes来加载由参数$1所指定的产品Makefile文件,并且指定了另外两个参数PRODUCTS和$(_product_var_list)。其中,变量_product_var_list也是定义在文件build/core/product.mk中,它的值如下所示:
[plain] view plaincopy
  1. _product_var_list := \  
  2.     PRODUCT_NAME \  
  3.     PRODUCT_MODEL \  
  4.     PRODUCT_LOCALES \  
  5.     PRODUCT_AAPT_CONFIG \  
  6.     PRODUCT_AAPT_PREF_CONFIG \  
  7.     PRODUCT_PACKAGES \  
  8.     PRODUCT_PACKAGES_DEBUG \  
  9.     PRODUCT_PACKAGES_ENG \  
  10.     PRODUCT_PACKAGES_TESTS \  
  11.     PRODUCT_DEVICE \  
  12.     PRODUCT_MANUFACTURER \  
  13.     PRODUCT_BRAND \  
  14.     PRODUCT_PROPERTY_OVERRIDES \  
  15.     PRODUCT_DEFAULT_PROPERTY_OVERRIDES \  
  16.     PRODUCT_CHARACTERISTICS \  
  17.     PRODUCT_COPY_FILES \  
  18.     PRODUCT_OTA_PUBLIC_KEYS \  
  19.     PRODUCT_EXTRA_RECOVERY_KEYS \  
  20.     PRODUCT_PACKAGE_OVERLAYS \  
  21.     DEVICE_PACKAGE_OVERLAYS \  
  22.     PRODUCT_TAGS \  
  23.     PRODUCT_SDK_ADDON_NAME \  
  24.     PRODUCT_SDK_ADDON_COPY_FILES \  
  25.     PRODUCT_SDK_ADDON_COPY_MODULES \  
  26.     PRODUCT_SDK_ADDON_DOC_MODULES \  
  27.     PRODUCT_DEFAULT_WIFI_CHANNELS \  
  28.     PRODUCT_DEFAULT_DEV_CERTIFICATE \  
  29.     PRODUCT_RESTRICT_VENDOR_FILES \  
  30.     PRODUCT_VENDOR_KERNEL_HEADERS \  
  31.     PRODUCT_FACTORY_RAMDISK_MODULES \  
  32.     PRODUCT_FACTORY_BUNDLE_MODULES  
       它描述的是在产品Makefile文件中定义在各种变量。

       函数import-nodes定义在文件build/core/node_fns.mk中,如下所示:

[plain] view plaincopy
  1. #  
  2. # $(1): output list variable name, like "PRODUCTS" or "DEVICES"  
  3. # $(2): list of makefiles representing nodes to import  
  4. # $(3): list of node variable names  
  5. #  
  6. define import-nodes  
  7. $(if \  
  8.   $(foreach _in,$(2), \  
  9.     $(eval _node_import_context := _nic.$(1).[[$(_in)]]) \  
  10.     $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \  
  11.                 should be empty here: $(_include_stack))),) \  
  12.     $(eval _include_stack := ) \  
  13.     $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3)) \  
  14.     $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \  
  15.     $(eval _node_import_context :=) \  
  16.     $(eval $(1) := $($(1)) $(_in)) \  
  17.     $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \  
  18.                 should be empty here: $(_include_stack))),) \  
  19.    ) \  
  20. ,)  
  21. 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 plaincopy
  1. # Overrides  
  2. PRODUCT_NAME := full  
  3. PRODUCT_DEVICE := generic  
  4. PRODUCT_BRAND := Android  
  5. PRODUCT_MODEL := Full Android on Emulator  
       当调用了函数move-var-list对它进行解析后,就会得到以下的新变量:
[plain] view plaincopy
  1. PRODUCTS.build/target/product/full.mk.PRODUCT_NAME := full  
  2. PRODUCTS.build/target/product/full.mk.PRODUCT_DEVICE := generic  
  3. PRODUCTS.build/target/product/full.mk.PRODUCT_BRAND := Android  
  4. 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的值。

       回到build/core/config.mk文件中,接下来我们再看BoardConfig.mk文件的加载过程。前面提到,当前要加载的BoardConfig.mk文件由变量TARGET_DEVICE来确定。例如,假设我们在运行lunch命令时,输入的文本为full-eng,那么build/target/product/full.mk就会被加载,并且我们得到TARGET_DEVICE的值就为generic,接下来加载的BoradConfig.mk文件就会在build/target/board/generic目录中找到。

       BoardConfig.mk文件定义的信息可以参考build/target/board/generic/BoardConfig.mk文件的内容,如下所示:

[plain] view plaincopy
  1. # config.mk  
  2. #  
  3. # Product-specific compile-time definitions.  
  4. #  
  5.   
  6. # The generic product target doesn't have any hardware-specific pieces.  
  7. TARGET_NO_BOOTLOADER := true  
  8. TARGET_NO_KERNEL := true  
  9. TARGET_ARCH := arm  
  10.   
  11. # Note: we build the platform images for ARMv7-A _without_ NEON.  
  12. #  
  13. # Technically, the emulator supports ARMv7-A _and_ NEON instructions, but  
  14. # emulated NEON code paths typically ends up 2x slower than the normal C code  
  15. # it is supposed to replace (unlike on real devices where it is 2x to 3x  
  16. # faster).  
  17. #  
  18. # What this means is that the platform image will not use NEON code paths  
  19. # that are slower to emulate. On the other hand, it is possible to emulate  
  20. # application code generated with the NDK that uses NEON in the emulator.  
  21. #  
  22. TARGET_ARCH_VARIANT := armv7-a  
  23. TARGET_CPU_ABI := armeabi-v7a  
  24. TARGET_CPU_ABI2 := armeabi  
  25. ARCH_ARM_HAVE_TLS_REGISTER := true  
  26.   
  27. HAVE_HTC_AUDIO_DRIVER := true  
  28. BOARD_USES_GENERIC_AUDIO := true  
  29.   
  30. # no hardware camera  
  31. USE_CAMERA_STUB := true  
  32.   
  33. # Enable dex-preoptimization to speed up the first boot sequence  
  34. # of an SDK AVD. Note that this operation only works on Linux for now  
  35. ifeq ($(HOST_OS),linux)  
  36.   ifeq ($(WITH_DEXPREOPT),)  
  37.     WITH_DEXPREOPT := true  
  38.   endif  
  39. endif  
  40.   
  41. # Build OpenGLES emulation guest and host libraries  
  42. BUILD_EMULATOR_OPENGL := true  
  43.   
  44. # Build and enable the OpenGL ES View renderer. When running on the emulator,  
  45. # the GLES renderer disables itself if host GL acceleration isn't available.  
  46. USE_OPENGL_RENDERER := true  
       它描述了产品的Boot Loader、Kernel、CPU体系结构、CPU ABI和Opengl加速等信息。

       再回到build/core/config.mk文件中,它最后加载build/core/dumpvar.mk文件。加载build/core/dumpvar.mk文件是为了生成make目标,以便可以对这些目标进行操作。例如,在我们这个情景中,我们要执行的make目标是dumpvar-TARGET_DEVICE,因此在加载build/core/dumpvar.mk文件的过程中,就会生成dumpvar-TARGET_DEVICE目标。

       文件build/core/dumpvar.mk的内容也比较多,这里我们只关注生成make目标相关的逻辑:

[plain] view plaincopy
  1. ......  
  2.   
  3. # The "dumpvar" stuff lets you say something like  
  4. #  
  5. #     CALLED_FROM_SETUP=true \  
  6. #       make -f config/envsetup.make dumpvar-TARGET_OUT  
  7. # or  
  8. #     CALLED_FROM_SETUP=true \  
  9. #       make -f config/envsetup.make dumpvar-abs-HOST_OUT_EXECUTABLES  
  10. #  
  11. # The plain (non-abs) version just dumps the value of the named variable.  
  12. # The "abs" version will treat the variable as a path, and dumps an  
  13. # absolute path to it.  
  14. #  
  15. dumpvar_goals := \  
  16.     $(strip $(patsubst dumpvar-%,%,$(filter dumpvar-%,$(MAKECMDGOALS))))  
  17. ifdef dumpvar_goals  
  18.   
  19.   ifneq ($(words $(dumpvar_goals)),1)  
  20.     $(error Only one "dumpvar-" goal allowed. Saw "$(MAKECMDGOALS)")  
  21.   endif  
  22.   
  23.   # If the goal is of the form "dumpvar-abs-VARNAME", then  
  24.   # treat VARNAME as a path and return the absolute path to it.  
  25.   absolute_dumpvar := $(strip $(filter abs-%,$(dumpvar_goals)))  
  26.   ifdef absolute_dumpvar  
  27.     dumpvar_goals := $(patsubst abs-%,%,$(dumpvar_goals))  
  28.     ifneq ($(filter /%,$($(dumpvar_goals))),)  
  29.       DUMPVAR_VALUE := $($(dumpvar_goals))  
  30.     else  
  31.       DUMPVAR_VALUE := $(PWD)/$($(dumpvar_goals))  
  32.     endif  
  33.     dumpvar_target := dumpvar-abs-$(dumpvar_goals)  
  34.   else  
  35.     DUMPVAR_VALUE := $($(dumpvar_goals))  
  36.     dumpvar_target := dumpvar-$(dumpvar_goals)  
  37.   endif  
  38.   
  39. .PHONY: $(dumpvar_target)  
  40. $(dumpvar_target):  
  41.     @echo $(DUMPVAR_VALUE)  
  42.   
  43. endif # dumpvar_goals  
  44.   
  45. ......  
      我们在执行make命令时,指定的目示会经由MAKECMDGOALS变量传递到Makefile中,因此通过变量MAKECMDGOALS可以获得make目标。

      上述代码的逻辑很简单,例如,在我们这个情景中,指定的make目标为dumpvar-TARGET_DEVICE,那么就会得到变量DUMPVAR_VALUE的值为$(TARGET_DEVICE)。TARGET_DEVICE的值在前面已经被设置为“generic”,因此变量DUMPVAR_VALUE的值就等于“generic”。此外,变量dumpvar_target的被设置为“dumpvar-TARGET_DEVICE”。最后我们就可以得到以下的make规则:

[plain] view plaincopy
  1. .PHONY dumpvar-TARGET_DEVICE  
  2. dumpvar-TARGET_DEVICE:  
  3.     @echo generic  
       至此,在build/envsetup.sh文件中定义的函数check_product就分析完成了。看完了之后,小伙伴们可能会问,前面不是说这个函数是用来检查用户输入的产品名称是否合法的吗?但是这里没看出哪一段代码给出了true或者false的答案啊。实际上,在前面分析的build/core/config.mk和build/core/product_config.mk等文件的加载过程中,如果发现输入的产品名称是非法的,也就是找不到相应的产品Makefile文件,那么就会通过调用error函数来产生一个错误,这时候函数check_product的返回值$?就会等于非0值。

       接下来我们还要继续分析在build/envsetup.sh文件中定义的函数check_variant的实现,如下所示:

[plain] view plaincopy
  1. VARIANT_CHOICES=(user userdebug eng)  
  2.   
  3. # check to see if the supplied variant is valid  
  4. function check_variant()  
  5. {  
  6.     for v in ${VARIANT_CHOICES[@]}  
  7.     do  
  8.         if [ "$v" = "$1" ]  
  9.         then  
  10.             return 0  
  11.         fi  
  12.     done  
  13.     return 1  
  14. }  
       这个函数的实现就简单多了。合法的编译类型定义在数组VARIANT_CHOICES中,并且它只有三个值user、userdebug和eng。其中,user表示发布版本,userdebug表示带调试信息的发布版本,而eng表标工程机版本。

       最后,我们再来分析在build/envsetup.sh文件中定义的函数printconfig的实现,如下所示:

[plain] view plaincopy
  1. function printconfig()  
  2. {  
  3.     T=$(gettop)  
  4.     if [ ! "$T" ]; then  
  5.         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2  
  6.         return  
  7.     fi  
  8.     get_build_var report_config  
  9. }  
       对比我们前面对函数check_product的分析,就会发现函数printconfig的实现与这很相似,都是通过调用get_build_var来获得相关的信息,但是这里传递给函数get_build_var的参数为report_config。

       我们跳过前面build/core/config.mk和build/core/envsetup.mk等文件对目标产品Makefile文件的加载,直接跳到build/core/dumpvar.mk文件来查看与report_config这个make目标相关的逻辑:

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ......  
  2.   
  3. dumpvar_goals := \  
  4.     $(strip $(patsubst dumpvar-%,%,$(filter dumpvar-%,$(MAKECMDGOALS))))  
  5. .....  
  6.   
  7. ifneq ($(dumpvar_goals),report_config)  
  8. PRINT_BUILD_CONFIG:=  
  9. endif  
  10.   
  11. ......  
  12.   
  13. ifneq ($(PRINT_BUILD_CONFIG),)  
  14. HOST_OS_EXTRA:=$(shell python -c "import platform; print(platform.platform())")  
  15. $(info ============================================)  
  16. $(info   PLATFORM_VERSION_CODENAME=$(PLATFORM_VERSION_CODENAME))  
  17. $(info   PLATFORM_VERSION=$(PLATFORM_VERSION))  
  18. $(info   TARGET_PRODUCT=$(TARGET_PRODUCT))  
  19. $(info   TARGET_BUILD_VARIANT=$(TARGET_BUILD_VARIANT))  
  20. $(info   TARGET_BUILD_TYPE=$(TARGET_BUILD_TYPE))  
  21. $(info   TARGET_BUILD_APPS=$(TARGET_BUILD_APPS))  
  22. $(info   TARGET_ARCH=$(TARGET_ARCH))  
  23. $(info   TARGET_ARCH_VARIANT=$(TARGET_ARCH_VARIANT))  
  24. $(info   HOST_ARCH=$(HOST_ARCH))  
  25. $(info   HOST_OS=$(HOST_OS))  
  26. $(info   HOST_OS_EXTRA=$(HOST_OS_EXTRA))  
  27. $(info   HOST_BUILD_TYPE=$(HOST_BUILD_TYPE))  
  28. $(info   BUILD_ID=$(BUILD_ID))  
  29. $(info   OUT_DIR=$(OUT_DIR))  
  30. $(info ============================================)  
  31. endif  
       变量PRINT_BUILD_CONFIG定义在文件build/core/envsetup.mk中,默认值设置为true。当make目标为report-config的时候,变量PRINT_BUILD_CONFIG的值就会被设置为空。因此,接下来就会打印一系列用来描述编译环境配置的变量的值,也就是我们执行lunch命令后看到的输出。注意,这些环境配置相关的变量量都是在加载build/core/config.mk和build/core/envsetup.mk文件的过程中设置的,就类似于前面我们分析的TARGET_DEVICE变量的值的设置过程。

       至此,我们就分析完成Android编译系统环境的初始化过程了。从分析的过程可以知道,Android编译系统环境是由build/core/config.mk、build/core/envsetup.mk、build/core/product_config.mk、AndroidProducts.mk和BoardConfig.mk等文件来完成的。这些mk文件涉及到非常多的细节,而我们这里只提供了一个大体的骨架和脉络,希望能够起到抛砖引玉的作用。

0 0
原创粉丝点击