http://blog.csdn.net/u011913612/article/details/52415948
因为Android的编译系统不同于Linux Kernel的递归式的编译系统,它的编译系统是一种称之为independent的模式,每个模块基本独立(它有可能依赖其他模块),每个模块都可以单独编译,这是Android independent编译系统模式的好处。但这并不意味着它是完美的,普通电脑编译android系统需要8个小时甚至更多(以本人的电脑为例),而编译linux kernel只需要半个小时,代码量是一回事,由independent模式造成的编译时间长应该是可以肯定的。正因为每个模块可以单独编译,所以android系统的编译就是依次编译每个模块,然后把所有编译好的模块和其他一些文件一起打包成镜像文件。因此,只要理解了每个模块的编译,理解android系统的编译就轻松多了。(以上均是个人观点,欢迎拍砖)
在我们source build/envsetup.sh 和 lunch 后,就可以执行mm命令编译单个模块了:
所以,编译的其实位置从mm说起:
- function mm()
- {
- local T=$(gettop)
- local DRV=$(getdriver $T)
- # If we're sitting in the root of the build tree, just do a
- # normal make.
- if [ -f build/core/envsetup.mk -a -f Makefile ]; then
- $DRV make $@
- else
- # Find the closest Android.mk file.
- local M=$(findmakefile)
- local MODULES=
- local GET_INSTALL_PATH=
- local ARGS=
- # Remove the path to top as the makefilepath needs to be relative
- local M=`echo $M|sed 's:'$T'/::'`
- if [ ! "$T" ]; then
- echo "Couldn't locate the top of the tree. Try setting TOP."
- return 1
- elif [ ! "$M" ]; then
- echo "Couldn't locate a makefile from the current directory."
- return 1
- else
- for ARG in $@; do
- case $ARG in
- GET-INSTALL-PATH) GET_INSTALL_PATH=$ARG;;
- esac
- done
- if [ -n "$GET_INSTALL_PATH" ]; then
- MODULES=
- ARGS=GET-INSTALL-PATH
- else
- MODULES=all_modules
- ARGS=$@
- fi
- ONE_SHOT_MAKEFILE=$M $DRV make -C $T -f build/core/main.mk $MODULES $ARGS
- fi
- fi
- }
这个函数做了三件事情:1.找到Android.mk文件,2.设置ONE_SHOT_MAKEFILE=$M,3.执行make all_modules进行编译
1.findmakefile:
- function findmakefile()
- {
- TOPFILE=build/core/envsetup.mk
- local HERE=$PWD
- T=
- while [ !(−f$TOPFILE ) -a ( $PWD != "/" ) ]; do
- T=`PWD= /bin/pwd`
- if [ -f "$T/Android.mk" ]; then
- echo $T/Android.mk
- cd $HERE
- return
- fi
- cd ..
- done
- cd $HERE
- }
2.ONE_SHOT_MAKEFILE=$M
$M = $(findmakefile),所以它就是用来编译的那个模块的Android.mk,一般情况下,如果你在当前目录下执行mm,而且当前目录下如果有个Android.mk的话,那她就是这个Android.mk的路劲+Android.mk了。
3.make -C $T -f build/core/main.mk $MODULES $ARGS
-C $T表明还是在源码顶级目录下执行make的,传入的参数一个是$MODULES=all_modules,$ARGS为空
这个时候,代码机会执行顶级的Makefile:
- ### DO NOT EDIT THIS FILE ###
- include build/core/main.mk
- ### DO NOT EDIT THIS FILE ###
加载main.mk
main.mk往下加载,不久我们就看到了我们在mm函数中设置的ONE_SHOT_MAKEFILE变量了:
- ifneq ($(ONE_SHOT_MAKEFILE),)
- # We've probably been invoked by the "mm" shell function
- # with a subdirectory's makefile.
- include $(ONE_SHOT_MAKEFILE)
- # Change CUSTOM_MODULES to include only modules that were
- # defined by this makefile; this will install all of those
- # modules as a side-effect. Do this after including ONE_SHOT_MAKEFILE
- # so that the modules will be installed in the same place they
- # would have been with a normal make.
- CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS)))
- FULL_BUILD :=
- # Stub out the notice targets, which probably aren't defined
- # when using ONE_SHOT_MAKEFILE.
- NOTICE-HOST-%: ;
- NOTICE-TARGET-%: ;
- # A helper goal printing out install paths
- .PHONY: GET-INSTALL-PATH
- GET-INSTALL-PATH:
- @$(foreach m, $(ALL_MODULES), $(if $(ALL_MODULES.$(m).INSTALLED),
- echo 'INSTALL-PATH: $(m) $(ALL_MODULES.$(m).INSTALLED)';))
- else # ONE_SHOT_MAKEFILE
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES:=
- screencap.cpp
- LOCAL_SHARED_LIBRARIES :=
- libcutils
- libutils
- libbinder
- libskia
- libui
- libgui
- LOCAL_MODULE:= screencap
- LOCAL_MODULE_TAGS := optional
- LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
- include $(BUILD_EXECUTABLE)
- core/config.mk:69:CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
- LOCAL_MODULE:=
- LOCAL_MODULE_PATH:=
- LOCAL_MODULE_RELATIVE_PATH :=
- LOCAL_MODULE_STEM:=
- LOCAL_DONT_CHECK_MODULE:=
- LOCAL_CHECKED_MODULE:=
- LOCAL_BUILT_MODULE:=
- LOCAL_BUILT_MODULE_STEM:=
- 。。。
接着,它有include BUILD_EXTABLE指向的脚本。
- core/config.mk:74:BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
关于阅读Makefile,个人观点就是紧追依赖链。我们执行的make的时候不是传了一个目标叫all_mudules了吗?所以make就会从它开始推导依赖关系,然后从依赖链的最叶子的位置生成目标,一次向上。所以那就看看all_modules:
- # phony target that include any targets in $(ALL_MODULES)
- .PHONY: all_modules
- ifndef BUILD_MODULES_IN_PATHS
- all_modules: $(ALL_MODULES)
- else
- # BUILD_MODULES_IN_PATHS is a list of paths relative to the top of the tree
- module_path_patterns := $(foreach p, $(BUILD_MODULES_IN_PATHS),
- $(if $(filter %/,$(p)),$(p)%,$(p)/%))
- my_all_modules := $(sort $(foreach m, $(ALL_MODULES),$(if $(filter
- $(module_path_patterns), $(addsuffix /,$(ALL_MODULES.$(m).PATH))),$(m))))
- all_modules: $(my_all_modules)
- endif
至此,就需要我们一步步推导依赖关系了,为方便理解,现直接把依赖关系以图的形式列出:
由于一张显示不完,$(linked_module)的依赖如下:
图中的变量未经推导,为了方便对比,推导出变量的值后的图如下:
$(linked_module):
从图中可以看到最终生成的文件有:
out/target/product/xxx/obj/excutable/screepcap__intermediates/screencap
out/target/product/xxx/symbols/system/bin/screencap
out/target/product/xxx/obj/excutable/screepcap__intermediates/PACKED/screencap
out/target/product/xxx/obj/excutable/screepcap__intermediates/LINKED/screencap
out/target/product/xxx/obj/excutable/screepcap__intermediates/screencap.o
out/target/product/xxx/obj/excutable/screepcap__intermediates/export_includes out/target/product/xxx/obj/excutable/screepcap__intermediates/import_includes
至于变量的推导过程,大家顺着文件加载的顺序慢慢推导就是了,这个过程可能比较花时间,但也是没办法的事。
以下是一些重要文件的加载顺序(只有部分比较重要的):
画圈的是我认为非常重要的文件。
在所有依赖生成以后,Android是怎么编译某个模块的呢?
以下是我认为的核心代码,代码在dynamic_binary.mk中:
- $(linked_module): $(my_target_crtbegin_dynamic_o) $(all_objects) $(all_libraries) $(my_target_crtend_o)
- $(transform-o-to-executable)
out/target/product/xxx/obj/excutable/screepcap__intermediates/LINKED/screencap
生成这个文件后,从依赖关系上也可以看出,其他文件在此基础上生成,而这个文件使用transform-o-to-executable函数生成,该函数定义如下:
- define transform-o-to-executable
- @mkdir -p $(dir $@)
- @echo "target Executable: $(PRIVATE_MODULE) ($@)"
- $(transform-o-to-executable-inner)
- endef
- define transform-o-to-executable-inner
- $(hide) $(PRIVATE_CXX) -pie
- -nostdlib -Bdynamic
- -Wl,-dynamic-linker,$($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_LINKER)
- -Wl,--gc-sections
- -Wl,-z,nocopyreloc
- $(PRIVATE_TARGET_GLOBAL_LD_DIRS)
- -Wl,-rpath-link=$(PRIVATE_TARGET_OUT_INTERMEDIATE_LIBRARIES)
- $(if $(filter true,$(PRIVATE_NO_CRT)),,$(PRIVATE_TARGET_CRTBEGIN_DYNAMIC_O))
- $(PRIVATE_ALL_OBJECTS)
- -Wl,--whole-archive
- $(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES))
- -Wl,--no-whole-archive
- $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group)
- $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES))
- $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group)
- $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_LIBGCOV))
- $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_LIBPROFILE_RT))
- $(PRIVATE_TARGET_LIBATOMIC)
- $(PRIVATE_TARGET_LIBGCC)
- $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES))
- -o $@
- $(PRIVATE_TARGET_GLOBAL_LDFLAGS)
- $(PRIVATE_LDFLAGS)
- $(if $(filter true,$(PRIVATE_NO_CRT)),,$(PRIVATE_TARGET_CRTEND_O))
- $(PRIVATE_LDLIBS)
- endef
这个函数使用clang编译器,最终生成了$(linked_module)目标。
而从$(linked_module)生成out/target/product/xxx/obj/excutable/screepcap__intermediates/PACKED/screencap则使用了如下方法:
- $(relocation_packer_output): $(relocation_packer_input) | $(ACP)
- @echo "target Unpacked: $(PRIVATE_MODULE) ($@)"
- $(copy-file-to-target)
- endif
- define copy-file-to-target
- @mkdir -p $(dir $@)
- $(hide) $(ACP) -fp $< $@
- endef
可以看就是一个简单的拷贝,所以这两个文件并没有什么不同。
生成out/target/product/xxx/symbols/system/bin/screencap也是在$(linked_module)的基础上做拷贝:
- $(symbolic_output) : $(symbolic_input) | $(ACP)
- @echo "target Symbolic: $(PRIVATE_MODULE) ($@)"
- $(copy-file-to-target)