Android编译系统详解(三)——编译流程详解

来源:互联网 发布:如何打开淘宝口令 编辑:程序博客网 时间:2024/05/14 11:46

原文:http://www.cloudchou.com/android/post-276.html


1.概述

编译Android的第三步是使用mka命令进行编译,当然我们也可以使用make –j4,但是推荐使用mka命令。因为mka将自动计算-j选项的数字,让我们不用纠结这个数字到底是多少(这个数字其实就是所有cpu的核心数)。在编译时我们可以带上我们需要编译的目标,假设你想生成recovery,那么使用mka recoveryimage,如果想生成ota包,那么需要使用mka otapackage,后续会介绍所有可以使用的目标。另外注意有一些目标只是起到修饰的作用,也就是说需要和其它目标一起使用,共有4个用于修饰的伪目标:

  • 1) showcommands 显示编译过程中使用的命令
  • 2) incrementaljavac用于增量编译java代码
  • 3) checkbuild用于检验那些需要检验的模块
  • 4) all如果使用all修饰编译目标,会编译所有模块

研究Android编译系统时最头疼的可能是变量,成百个变量我们无法记住其含义,也不知道这些变量会是什么值,为此我专门做了一个编译变量的参考网站android.cloudchou.com,你可以在该网站查找变量,它能告诉你变量的含义,也会给出你该变量的示例值,另外也详细解释了编译系统里每个Makefile的作用,这样你在看编译系统的代码时不至于一头雾水。

编译的核心文件是build/core/main.mk和build/core/makefile,main.mk主要作用是检查编译环境是否符合要求,确定产品配置,决定产品需要使用的模块,并定义了许多目标供开发者使用,比如droid,sdk等目标,但是生成这些目标的规则主要在Makefile里定义,而内核的编译规则放在build/core/task/kernel.mk

我们将先整体介绍main.mk的执行流程,然后再针对在Linux上编译默认目标时使用的关键代码进行分析。Makefile主要定义了各个目标的生成规则,因此不再详细介绍它的执行流程,若有兴趣看每个目标的生成规则,可查看http://android.cloudchou.com/build/core/Makefile.php

2. main.mk执行流程

2.1 检验编译环境并建立产品配置

  • 1) 设置Shell变量为bash,不能使用其它shell
  • 2) 关闭make的suffix规则,rcs/sccs规则,并设置一个规则: 当某个规则失败了,就删除所有目标
  • 3) 检验make的版本,cygwin可使用任意版本make,但是linux或者mac只能使用3.81版本或者3.82版本
  • 4) 设置PWD,TOP,TOPDIR,BUILD_SYSTEM等变量,定义了默认目标变量,但是暂时并未定义默认目标的生成规则
  • 5) 包含build/core/help.mk,该makefile定义了两个目标help和out, help用于显示帮助,out用于检验编译系统是否正确
  • 6) 包含build/core/config.mk,config.mk作了很多配置,包括产品配置,包含该makefile后,会建立输出目录系列的变量,还会建立PRODUCT系列变量,后续介绍产品配置时,对此会有更多详细介绍
  • 7) 包含build/core/cleanbuild.mk,该makefile会包含所有工程的CleanSpec.mk,写了CleanSpec.mk的工程会定义每次编译前的特殊清理步骤,cleanbuild.mk会执行这些清除步骤
  • 8) 检验编译环境,先检测上次编译结果,如果上次检验的版本和此次检验的版本一致,则不再检测,然后进行检测并将此次编译结果写入

2.2 包含其它makefile及编译目标检测

  • 1) 如果目标里含有incrementaljavac, 那么编译目标时将用incremental javac进行增量编译
  • 2) 设置EMMA_INSTRUMENT变量的值,emma是用于测试代码覆盖率的库
  • 3) 包含build/core/definistions.mk,该makefile定义了许多辅助函数
  • 4) 包含build/core/qcom_utils.mk,该makefile定义了高通板子的一些辅助函数及宏
  • 5) 包含build/core/dex_preopt.mk,该makefile定义了优化dex代码的一些宏
  • 6) 检测编译目标里是否有user,userdebug,eng,如果有则告诉用户放置在buildspec.mk或者使用lunch设置,检测TARGET_BUILD_VARIANT变量,看是否有效
  • 7) 包含build/core/pdk_config.mk, PDK主要是能提高现有设备升级能力,帮助设备制造商能更快的适配新版本的android

2.3 根据TARGET_BUILD_VARIANT建立配置

  • 1) 如果编译目标里有sdk,win_sdk或者sdk_addon,那么设置is_sdk_build为true
  • 2) 如果定义了HAVE_SELINUX,那么编译时为build prop添加属性ro.build.selinux=1
  • 3) 如果TARGET_BUILD_VARIANT是user或者userdebug,那么tags_to_install += debug 如果用户未定义DISABLE_DEXPREOPT为true,并且是user模式,那么将设置WITH_DEXPREOPT := true,该选项将开启apk的预优化,即将apk分成odex代码文件和apk资源文件
  • 4) 判断enable_target_debugging变量,默认是true,当build_variant是user时,则它是false。如果该变量值为true,则设置Rom的编译属性ro.debuggable为1,否则设置ro.debuggable为0
  • 5) 如果TARGET_BUILD_VARIANT是eng,那么tags_to_install为debug,eng, 并设置Rom的编译属性ro.setupwizard.mode为OPTIONAL,因为eng模式并不要安装向导
  • 6) 如果TARGET_BUILD_VARIANT是tests,那么tags_to_install := debug eng tests
  • 7) 设置sdk相关变量
  • 8) 添加一些额外的编译属性
  • 9) 定义should-install-to-system宏函数
  • 10) 若除了修饰目标,没定义任何目标,那么将使用默认目标编译

2.4 包含所有要编译的模块的Makefile

如果编译目标是clean clobber installclean dataclean,那么设置dont_bother为true,若dont_bother为false,则将所有要编译的模块包含进来

    1) 如果主机操作系统及体系结构为darwin-ppc(Mac电脑),那么提示不支持编译Sdk,并将SDK_ONLY设置为true

    2) 如果主机操作系统是windows,那么设置SDK_ONLY为true

    3) 根据SDK_ONLY是否为true,编译主机操作系统类型,BUILD_TINY_ANDROID的值,设置sudbidrs变量

    4) 将所有PRODUCT_*相关变量存储至stash_product_vars变量,稍后将验证它是否被修改

    5) 根据ONE_SHOT_MAKEFILE的值是否为空,包含不同的makefile

    6) 执行post_clean步骤,并确保产品相关变量没有变化

    7) 检测是否有文件加入ALL_PREBUILT

    8) 包含其它必须在所有Android.mk包含之后需要包含的makefile

    9) 将known_custom_modules转化成安装路径得到变量CUSTOM_MODULES

    10) 定义模块之间的依赖关系,$(ALL_MODULES.$(m).REQUIRED))变量指明了模块之间的依赖关系

    11)计算下述变量的值:product_MODULES,debug_MODULES,eng_MODULES,tests_MODULES,modules_to_install,overridden_packages,target_gnu_MODULES,ALL_DEFAULT_INSTALLED_MODULES

    12) 包含build/core/Makefile

    13) 定义变量modules_to_check

2.5 定义多个目标

这一节定义了众多目标,prebuilt,all_copied_headers,files,checkbuild,ramdisk,factory_ramdisk,factory_bundle,systemtarball,boottarball,userdataimage,userdatatarball,cacheimage,bootimage,droidcore,dist_files,apps_only,all_modules,docs,sdk,lintall,samplecode,findbugs,clean,modules,showcommands,nothing。

后续文章将列出所有可用的目标

3 编译默认目标时的执行流程

在介绍编译默认目标时的执行流程之前,先介绍一下ALL_系列的变量,否则看代码时很难搞懂这些变量的出处,这些变量在包含所有模块后被建立,每个模块都有对应的用于编译的makefile,这些makefile会包含一个编译类型对应的makefile,比如package.mk,而这些makefile最终都会包含base_rules.mk,在base_rules.mk里会为ALL系列变量添加值。所有这些变量及其来源均可在android.cloudchou.com查看详细解释:

  • 1) ALL_DOCS所有文档的全路径,ALL_DOCS的赋值在droiddoc.mk里, ALL_DOCS += $(full_target)
  • 2)ALL_MODULES系统的所有模块的简单名字集合,编译系统还为每一个模块还定义了其它两个变量,ALL_MODULES.$(LOCAL_MODULE).BUILT 所有模块的生成路径ALL_MODULES.$(LOCAL_MODULE).INSTALLED 所有模块的各自安装路径,详情请见http://android.cloudchou.com/build/core/definitions.php#ALL_MODULES
  • 3) ALL_DEFAULT_INSTALLED_MODULES 所有默认要安装的模块,在build/core/main.mk和build/core/makfile里设置
  • 4) ALL_MODULE_TAGS 使用LOCAL_MODULE_TAGS定义的所有tag集合,每一个tag对应一个ALL_MODULE_TAGS.变量,详情请见http://android.cloudchou.com/build/core/definitions.php#ALL_MODULE_TAGS
  • 5) ALL_MODULE_NAME_TAGS类似于ALL_MODULE_TAGS,但是它的值是 某个tag的所有模块的名称 详情请见http://android.cloudchou.com/build/core/definitions.php#ALL_MODULE_NAME_TAGS
  • 6) ALL_HOST_INSTALLED_FILES 安装在pc上的程序集合
  • 7) ALL_PREBUILT 将会被拷贝的预编译文件的安装全路径的集合
  • 8) ALL_GENERATED_SOURCES 某些工具生成的源代码文件的集合,比如aidl会生成java源代码文件
  • 9) ALL_C_CPP_ETC_OBJECTS 所有asm,c,c++,以及lex和yacc生成的c代码文件的全路径
  • 10) ALL_ORIGINAL_DYNAMIC_BINARIES 没有被优化,也没有被压缩的动态链接库
  • 11) ALL_SDK_FILES 将会放在sdk的文件
  • 12) ALL_FINDBUGS_FILES 所有findbugs程序用的xml文件
  • 13) ALL_GPL_MODULE_LICENSE_FILES GPL 模块的 许可文件
  • 14) ANDROID_RESOURCE_GENERATED_CLASSES Android 资源文件生成的java代码编译后的类的类型

3.1 关键代码

定义默认目标的代码位于main.mk:

123
.PHONY: droidDEFAULT_GOAL := droid$(DEFAULT_GOAL):

droid目标依赖的目标有:

1234567
ifneq ($(TARGET_BUILD_APPS),)……droid: apps_only #如果编译app,那么droid依赖apps_only目标else……droid: droidcore dist_files #默认依赖droidcore目标和dist_files目标endif

dist_files目标依赖的目标主要是一些用于打包的工具,它们都是用dist-for-goals宏添加依赖关系的:

1234567891011
$(call dist-for-goals, dist_files, $(EMMA_META_ZIP))system/core/mkbootimg/Android.mk$(call dist-for-goals, dist_files, $( LOCAL_BUILT_MODULE ))system/core/cpio/Android.mk:13:$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE))system/core/adb/Android.mk:88:$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))system/core/fastboot/Android.mk:68:$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))external/guava/Android.mk:26:$(call dist-for-goals, dist_files, $(LOCAL_BUILT_MODULE):guava.jar)external/yaffs2/Android.mk:28:$(call dist-for-goals, dist_files, $(LOCAL_BUILT_MODULE))external/mp4parser/Android.mk:26:$(call dist-for-goals, dist_files, $(LOCAL_BUILT_MODULE):mp4parser.jar)external/jsr305/Android.mk:25:$(call dist-for-goals, dist_files, $(LOCAL_BUILT_MODULE):jsr305.jar)frameworks/support/renderscript/v8/Android.mk:29:#$(call dist-for-goals, dist_files, $(LOCAL_BUILT_MODULE):volley.jar)frameworks/support/volley/Android.mk:29:#$(call dist-for-goals, dist_files, $(LOCAL_BUILT_MODULE):volley.jar)

我们再看droidcore目标依赖的目标有:

1234567891011121314151617181920212223
droidcore: files \systemimage \ #system.img$(INSTALLED_BOOTIMAGE_TARGET) \ #boot.img$(INSTALLED_RECOVERYIMAGE_TARGET) \#recovery.img$(INSTALLED_USERDATAIMAGE_TARGET) \#data.img$(INSTALLED_CACHEIMAGE_TARGET) \#cache.img$(INSTALLED_FILES_FILE)# installed-files.txtifneq ($(TARGET_BUILD_APPS),).else$(call dist-for-goals, droidcore, \    $(INTERNAL_UPDATE_PACKAGE_TARGET) #cm_find5-img-eng.cloud.zip    $(INTERNAL_OTA_PACKAGE_TARGET) \ # cm_find5-ota-eng.cloud.zip    $(SYMBOLS_ZIP) \ # cm_find5-symbols-eng.cloud.zip    $(INSTALLED_FILES_FILE) \# installed-files.txt    $(INSTALLED_BUILD_PROP_TARGET) \# system/build.prop    $(BUILT_TARGET_FILES_PACKAGE) \# cm_find5-target_files-eng.cloud.zip    $(INSTALLED_ANDROID_INFO_TXT_TARGET) \# android-info.txt    $(INSTALLED_RAMDISK_TARGET) \# ramdisk.img    $(INSTALLED_FACTORY_RAMDISK_TARGET) \# factory_ramdisk.gz    $(INSTALLED_FACTORY_BUNDLE_TARGET) \# cm_find5-factory_bundle- eng.cloud.zip   )endif

system.img, boot.img, recovery.img, data.img,cache.img,installed_files.txt的生成规则在Makefile里定义, 在http://android.cloudchou.com/build/core/Makefile.php里可以看到详细的生成规则再看一下files目标所依赖的目标:

123
files: prebuilt \        $(modules_to_install) \        $(INSTALLED_ANDROID_INFO_TXT_TARGET)

prebuilt目标依赖$(ALL_PREBUILT),android-info.txt的生成规则在target/board/board.mk里定义,而$(modules_to_install)目标是所有要安装的模块的集合,计算比较复杂,现在以在linux下编译默认目标为例,将涉及到的代码组织如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
……tags_to_install :=ifneq (,$(user_variant))..  ifeq ($(user_variant),userdebug)  tags_to_install += debug  elseendifendififeq ($(TARGET_BUILD_VARIANT),eng)tags_to_install := debug eng…endififeq ($(TARGET_BUILD_VARIANT),tests)tags_to_install := debug eng testsendififdef is_sdk_buildtags_to_install := debug engelse # !sdkendif……# ------------------------------------------------------------# Define a function that, given a list of module tags, returns# non-empty if that module should be installed in /system. # For most goals, anything not tagged with the "tests" tag should# be installed in /system.define should-install-to-system$(if $(filter tests,$(1)),,true)endefifdef is_sdk_build# For the sdk goal, anything with the "samples" tag should be# installed in /data even if that module also has "eng"/"debug"/"user".define should-install-to-system$(if $(filter samples tests,$(1)),,true)endefendif#接下来根据配置计算要查找的subdirs目录ifneq ($(dont_bother),true)ifeq ($(SDK_ONLY),true)include $(TOPDIR)sdk/build/sdk_only_whitelist.mkinclude $(TOPDIR)development/build/sdk_only_whitelist.mk# Exclude tools/acp when cross-compiling windows under linuxifeq ($(findstring Linux,$(UNAME)),)subdirs += build/tools/acpendif else# !SDK_ONLYifeq ($(BUILD_TINY_ANDROID), true)subdirs := \bionic \system/core \system/extras/ext4_utils \system/extras/su \build/libs \build/target \build/tools/acp \external/gcc-demangle \external/mksh \external/openssl \external/yaffs2 \external/zlibelse# !BUILD_TINY_ANDROIDsubdirs := $(TOP) FULL_BUILD := trueendif# !BUILD_TINY_ANDROIDendif# Before we go and include all of the module makefiles, stash away# the PRODUCT_* values so that later we can verify they are not modified.stash_product_vars:=trueifeq ($(stash_product_vars),true)  $(call stash-product-vars, __STASHED)endif.ifneq ($(ONE_SHOT_MAKEFILE),)include $(ONE_SHOT_MAKEFILE)CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS)))FULL_BUILD :=else # ONE_SHOT_MAKEFILE## Include all of the makefiles in the system# # Can't use first-makefiles-under here because# --mindepth=2 makes the prunes not work.subdir_makefiles := \$(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk) include $(subdir_makefiles) endif # ONE_SHOT_MAKEFILE……ifeq ($(stash_product_vars),true)  $(call assert-product-vars, __STASHED)endif# -------------------------------------------------------------------# Fix up CUSTOM_MODULES to refer to installed files rather than# just bare module names.  Leave unknown modules alone in case# they're actually full paths to a particular file.known_custom_modules := $(filter $(ALL_MODULES),$(CUSTOM_MODULES))unknown_custom_modules := $(filter-out $(ALL_MODULES),$(CUSTOM_MODULES))CUSTOM_MODULES := \$(call module-installed-files,$(known_custom_modules)) \$(unknown_custom_modules# -------------------------------------------------------------------# Figure out our module sets.## Of the modules defined by the component makefiles,# determine what we actually want to build. ifdef FULL_BUILD  # The base list of modules to build for this product is specified  # by the appropriate product definition file, which was included  # by product_config.make.  product_MODULES := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES)  # Filter out the overridden packages before doing expansion  product_MODULES := $(filter-out $(foreach p, $(product_MODULES), \      $(PACKAGES.$(p).OVERRIDES)), $(product_MODULES))  $(call expand-required-modules,product_MODULES,$(product_MODULES))  product_FILES := $(call module-installed-files, $(product_MODULES))  ifeq (0,1)    $(info product_FILES for $(TARGET_DEVICE) ($(INTERNAL_PRODUCT)):)    $(foreach p,$(product_FILES),$(info :   $(p)))    $(error done)  endifelse  # We're not doing a full build, and are probably only including  # a subset of the module makefiles.  Don't try to build any modules  # requested by the product, because we probably won't have rules  # to build them.  product_FILES :=endif# When modules are tagged with debug eng or tests, they are installed# for those variants regardless of what the product spec says.debug_MODULES := $(sort \        $(call get-tagged-modules,debug) \        $(call module-installed-files, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES_DEBUG)) \    )eng_MODULES := $(sort \        $(call get-tagged-modules,eng) \        $(call module-installed-files, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES_ENG)) \    )tests_MODULES := $(sort \        $(call get-tagged-modules,tests) \        $(call module-installed-files, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES_TESTS)) \)# TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES# and get rid of it from this list.# TODO: The shell is chosen by magic.  Do we still need this?modules_to_install := $(sort \    $(ALL_DEFAULT_INSTALLED_MODULES) \    $(product_FILES) \    $(foreach tag,$(tags_to_install),$($(tag)_MODULES)) \    $(call get-tagged-modules, shell_$(TARGET_SHELL)) \    $(CUSTOM_MODULES) \  )# Some packages may override others using LOCAL_OVERRIDES_PACKAGES.# Filter out (do not install) any overridden packages.overridden_packages := $(call get-package-overrides,$(modules_to_install))ifdef overridden_packages#  old_modules_to_install := $(modules_to_install)  modules_to_install := \      $(filter-out $(foreach p,$(overridden_packages),$(p) %/$(p).apk), \          $(modules_to_install))endif#$(error filtered out#           $(filter-out $(modules_to_install),$(old_modules_to_install))) # Don't include any GNU targets in the SDK.  It's ok (and necessary)# to build the host tools, but nothing that's going to be installed# on the target (including static libraries).ifdef is_sdk_build  target_gnu_MODULES := \              $(filter \                      $(TARGET_OUT_INTERMEDIATES)/% \                      $(TARGET_OUT)/% \                      $(TARGET_OUT_DATA)/%, \                              $(sort $(call get-tagged-modules,gnu)))  $(info Removing from sdk:)$(foreach d,$(target_gnu_MODULES),$(info : $(d)))  modules_to_install := \              $(filter-out $(target_gnu_MODULES),$(modules_to_install))   # Ensure every module listed in PRODUCT_PACKAGES* gets something installed  # TODO: Should we do this for all builds and not just the sdk?  $(foreach m, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES), \    $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\      $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES has nothing to install!)))  $(foreach m, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES_DEBUG), \    $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\      $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_DEBUG has nothing to install!)))  $(foreach m, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES_ENG), \    $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\      $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_ENG has nothing to install!)))  $(foreach m, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES_TESTS), \    $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\      $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_TESTS has nothing to install!)))endif # Install all of the host modulesmodules_to_install += $(sort $(modules_to_install) $(ALL_HOST_INSTALLED_FILES)) # build/core/Makefile contains extra stuff that we don't want to pollute this# top-level makefile with.  It expects that ALL_DEFAULT_INSTALLED_MODULES# contains everything that's built during the current make, but it also further# extends ALL_DEFAULT_INSTALLED_MODULES.ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)include $(BUILD_SYSTEM)/Makefilemodules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))ALL_DEFAULT_INSTALLED_MODULES := endif # dont_bother# These are additional goals that we build, in order to make sure that there# is as little code as possible in the tree that doesn't build.modules_to_check := $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).CHECKED)) # If you would like to build all goals, and not skip any intermediate# steps, you can pass the "all" modifier goal on the commandline.ifneq ($(filter all,$(MAKECMDGOALS)),)modules_to_check += $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).BUILT))endif # for easier debuggingmodules_to_check := $(sort $(modules_to_check))#$(error modules_to_check $(modules_to_check))

0 0
原创粉丝点击