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

时间:2015-03-15 00:31:02   收藏:0   阅读:841

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

1.概述

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

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

编译的核心文件是build/core/main.mkbuild/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 检验编译环境并建立产品配置

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

2.3 根据TARGET_BUILD_VARIANT建立配置

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查看详细解释:

3.1 关键代码

定义默认目标的代码位于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

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!