原文网址:http://www.cloudchou.com/android/post-276.html
编译Android的第三步是使用mka命令进行编译,当然我们也可以使用make –j4,但是推荐使用mka命令。因为mka将自动计算-j选项的数字,让我们不用纠结这个数字到底是多少(这个数字其实就是所有cpu的核心数)。在编译时我们可以带上我们需要编译的目标,假设你想生成recovery,那么使用mka recoveryimage,如果想生成ota包,那么需要使用mka otapackage,后续会介绍所有可以使用的目标。另外注意有一些目标只是起到修饰的作用,也就是说需要和其它目标一起使用,共有4个用于修饰的伪目标:
研究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
如果编译目标是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
这一节定义了众多目标,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。
后续文章将列出所有可用的目标
在介绍编译默认目标时的执行流程之前,先介绍一下ALL_系列的变量,否则看代码时很难搞懂这些变量的出处,这些变量在包含所有模块后被建立,每个模块都有对应的用于编译的makefile,这些makefile会包含一个编译类型对应的makefile,比如package.mk,而这些makefile最终都会包含base_rules.mk,在base_rules.mk里会为ALL系列变量添加值。所有这些变量及其来源均可在android.cloudchou.com查看详细解释:
定义默认目标的代码位于main.mk:
1 2 3 |
.PHONY: droid DEFAULT_GOAL := droid $(DEFAULT_GOAL): |
droid目标依赖的目标有:
1 2 3 4 5 6 7 |
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宏添加依赖关系的:
1 2 3 4 5 6 7 8 9 10 11 |
$(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目标依赖的目标有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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.txt ifneq ($(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目标所依赖的目标:
1 2 3 |
files: prebuilt $(modules_to_install) $(INSTALLED_ANDROID_INFO_TXT_TARGET) |
prebuilt目标依赖$(ALL_PREBUILT),android-info.txt的生成规则在target/board/board.mk里定义,而$(modules_to_install)目标是所有要安装的模块的集合,计算比较复杂,现在以在linux下编译默认目标为例,将涉及到的代码组织如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
…… tags_to_install := ifneq (,$(user_variant)) ….. ifeq ($(user_variant),userdebug) tags_to_install += debug else … endif endif ifeq ($(TARGET_BUILD_VARIANT),eng) tags_to_install := debug eng … endif ifeq ($(TARGET_BUILD_VARIANT),tests) tags_to_install := debug eng tests endif ifdef is_sdk_build tags_to_install := debug eng else # !sdk endif …… # ------------------------------------------------------------ # 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) endef ifdef 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) endef endif … #接下来根据配置计算要查找的subdirs目录 ifneq ($(dont_bother),true) … ifeq ($(SDK_ONLY),true) include $(TOPDIR)sdk/build/sdk_only_whitelist.mk include $(TOPDIR)development/build/sdk_only_whitelist.mk # Exclude tools/acp when cross-compiling windows under linux ifeq ($(findstring Linux,$(UNAME)),) subdirs += build/tools/acp endif else # !SDK_ONLY ifeq ($(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/zlib else # !BUILD_TINY_ANDROID subdirs := $(TOP) FULL_BUILD := true endif # !BUILD_TINY_ANDROID endif # 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:=true ifeq ($(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) endif else # 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 modules modules_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)/Makefile modules_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 debugging modules_to_check := $(sort $(modules_to_check)) #$(error modules_to_check $(modules_to_check)) |
原文:http://www.cnblogs.com/wi100sh/p/4338539.html