Andoird编译系统分析(二)
2015-09-18 17:00
465 查看
这篇文章接着前面的<<Android编译系统分析(一)>>继续,
主要分析Android OS以及module的编译行为. 首先先说明一下几个重要的makefile文件:
build/core/main.mk, 这是入口文件, 编译系统将从这里开始
build/core/Makefile, 系统内置目标的规则定义基本都在这个文件里面实现, 由main.mk导入.
build/core/base_rules.mk, 处理module的基本信息, 并提供给编译核心使用, 一般在Android.mk的导入链上的合适地方被导入进来. 其定义了丰富的变量可以供编译核心来处理当前这个module.
build/core/definitions.mk, 实现了一系列基本而重要的函数, 这样能够有效地把处理通用规则的代码抽取出来给各个模块重复使用.
1 主体流程描述
最开始是环境检查,在Android编译系统分析一当中已经分析过, 比较简单, 不再赘述.
TARGET_BUILD_VARIANT
该变量可选值(user,userdebug,eng),其各个值对编译后的系统的影响,可直接查看android官网描述:
意思已经说的比较清楚了,有两点特别说明一下:
ro.secure/ro.debuggable这两个property主要影响adb的行为,也就是adbd是否以root身份运行。
表格里面提到的tag, 就是各个模块Android.mk里面变量LOCAL_MODULE_TAG,其通常的取值是: eng/userdebug或者默认的optional. 如果模块没有定义这个变量,默认的optional表示OS仅装载PRODUCT_PACKAGES列表中声明的模块。这个变量不要定义成user,在5.0及以上系统中不再可用(5.0以下的没考察过,不清楚从哪个版本开始的),base_rules.mk里面会检查并报错。
在编译的时候如何选择某个VARIANT呢?看下图:
![](http://img.blog.csdn.net/20150918170624132)
每个combo的最后一个字段就是VARIANT了,这个在<<Android编译系统分析(一)>>中已经分析过了。这里是aosp的源码,没有user的combo,实际上是有用的,一般是设备厂商最后发布的release版本。
回到make.mk文件,VARIANT最后会确定tags_to_install变量的内容,也就是最后将要被install的模块的tags(也就是模块可以声明成多个tag).
编译SDK
编译目标中含有: sdk win_sdk sdk_addon,则会编译SDK包(is_sdk_build变量被激活),如果HOST系统是windows的话,则只会编译SDK,也就是SDK_ONLY变量会被激活。
SDK_ONLY被激活,则对应的FULL_BUILD为空, 也就意味着product_FILES变量为空,这个变量可以认为就是PRODUCT_PACKAGES,所以也就是说大多数模块在这种模式下都不会被编译的,但是,被打上tags_to_install当中的tag的模块,还是会被编译的。
is_sdk_build会指示从目标编译模块中摘除GNU目标,可能是GPL授权的原因吧,gnu工具编译系统本身可以用用(作为host的目标制作出来),但是肯定是不能在sdk包中发布的。
除了在配置上被以上两个变量控制了以外, sdk的目标规则通过内部目标INTERNAL_SDK_TARGET定义在Makefile文件当中, 具体的这里不再赘述.
关于all_modules目标
这个目标一般是配合mm/mmm等shell函数使用(详见<<Android编译系统分析(一)>>), 它的定义如下
.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
在mm/mmm函数里没有定义BUILD_MODULES_IN_PATHS, 也就是上面的代码走第一个分支all_modules: $(ALL_MODULES), 那么ALL_MODULES在哪里? 这两个命令同时还定义了ONE_SHOT_MAKEFILE变量, 就是当前module的Android.mk, 当该变量非空, main.mk只会单独导入该Android.mk, 否则main.mk会扫描整个工程的Android.mk并都导入进来. 这条导入链最终会导入到base_rules.mk,
该文件里面定义了ALL_MODULES += $(my_register_name), 即ALL_MODULES += $(LOCAL_MODULE), 也即all_modules: $(LOCAL_MODULE). 所以这两个函数会借助all_modules编译当前module当中的所有LOCAL_MODULE.
在mma/mmma函数里少了ONE_SHOT_MAKEFILE, 多了BUILD_MODULES_IN_PATHS. 所以上面的代码走第二个分支. ONE_SHOT_MAKEFILE没有意味着会扫描所有的Android.mk,这个可以理解,毕竟是带着依赖模块一起编译,自然要扫描工程的。至于BUILD_MODULES_IN_PATHS变量,简单点说,mma里面就是PWD. 意义是:mm编译时, ALL_MODULES就是ONE_SHOT_MAKEFILE指向的Android.mk里面定义的LOCAL_MODULE,所以all_modules直接依赖就可以,而mma的ALL_MODULES则代表工程中所有Android.mk,因此不能直接使用,所以只能通过路径与module的对应关系找到需要编译的modules,然后制作成all_modules的依赖链.这个对应关系在base_rulse.mk里面处理,是为$(ALL_MODULES.$(module).PATH)与module的对应.
CUSTOM_MODULES
这个变量记录:
a.没有定义在PRODUCT_PACKAGES列表中的module;
b.没有对应到tags_to_install当中的tag的module.
变量内容分成两部分:
a.合法的LOCAL_MODULE,这类将会转换成ALL_MODULES.$(module).INSTALLED这个实际目标然后被处理;
b.自定义的目标也是可以加入到该列表中来的,一般这类目标及其动作规则是由开发者自己书写在Android.mk当中的.
DEFAULT_GOAL
缺省目标DEFAULT_GOAL为droid,其依赖的droidcore目标是AndroidOS的核心目标,各种系统镜像的make目标都被该目标所依赖,直观点,见下:
droidcore: files \
systemimage \
$(INSTALLED_BOOTIMAGE_TARGET) \
$(INSTALLED_RECOVERYIMAGE_TARGET) \
$(INSTALLED_USERDATAIMAGE_TARGET) \
$(INSTALLED_CACHEIMAGE_TARGET) \
$(INSTALLED_VENDORIMAGE_TARGET) \
$(INSTALLED_FILES_FILE)
我们通常整体编译AndroidOS, 就是编译这个目标.
其它一些常见的内置目标
ramdisk, 将root目录(即rootfs)制作成ramdisk.img.
bootimage, 将kernel(一般就是uImage)和ramdisk.img粘合在一起制作成boot.img.
systemimage, 将system目录制作成system.img.
userdataimage, 将data目录制作成userdata.img.
systemtarball, 把system分区的内容打包成tar压缩包
boottarball, 把kernel和ramdisk.img打包成tar压缩包
userdatatarball, 把userdata分区的内容打包成tar压缩包
nothing, 什么都不做,但是会把工程所有的makefiles都检查一遍,也就是检查编译脚本用的。
dont_bother_goals, 如果目标是这一类的话,不会扫描工程的Android.mk文件。
2 module的编译(Android.mk)
以libcutils和adb为例,因为编译动态库和可执行程序的过程比较相似,所以放在一起分析,以动态库的为主,可执行程序有差异的地方再单独说明。
一般地,module的Android.mk都会定义LOCAL_SRC_FILES, LOCAL_CFLAGS等变量, 然后直接导入这三类规则文件:BUILD_STATIC_LIBRARY或者BUILD_SHARED_LIBRARY或者BUILD_EXECUTABLE.
BUILD_SHARED_LIBRARY或者BUILD_EXECUTABLE
以BUILD_SHARED_LIBRARY为例:
BUILD_SHARED_LIBRARY := build/core/shared_library.mk
在引入64位以后,这个文件自身仅处理32/64模块的的选择问题,这里不讨论。
真正处理动态库的是shared_library_internal.mk, 这个文件导入dynamic_binary.mk并完成linked_module这个目标:
linked_module, 其定义在dynamic_binary.mk:
guessed_intermediates := $(call local-intermediates-dir,,$(LOCAL_2ND_ARCH_VAR_PREFIX))
linked_module := $(guessed_intermediates)/LINKED/$(my_built_module_stem)
展开成:
linked_module = obj/EXECUTABLES/XXX_intermediates/LINKED/XXX, 这是程序的
或者
linked_module = obj/SHARED_LIBRARIES/XXX_intermediates/LINKED/XXX.so, 这是库的
linked_module目标规则定义在本文件:
$(linked_module): $(all_objects) $(all_libraries) \
$(LOCAL_ADDITIONAL_DEPENDENCIES) \
$(my_target_crtbegin_so_o) $(my_target_crtend_so_o)
$(transform-o-to-shared-lib)
依赖链当中,$(all_objects) $(all_libraries)定义在binary.mk, all_objects是module自己的源代码编译成的目标代码(在binary.mk完成编译), all_libraries是模块依赖的所有外部库, 比如libcutils. $(LOCAL_ADDITIONAL_DEPENDENCIES)由开发者自己定义在Android.mk当中, 用于追加一个私有的目标, 可以方便开发者灵活处理自己的module.
$(my_target_crtbegin_so_o) $(my_target_crtend_so_o)是bionic-c库的组成部分,这是生成elf文件的辅助库.
$(transform-o-to-shared-lib)定义在defintions.mk,顾名思义,就不多说了,最后得到我们中间目标obj/SHARED_LIBRARIES/XXX_intermediates/LINKED/XXX.so.
再看导入的dynamic_binary.mk文件,这个文件是针对动态库和可执行程序(包括静态链接的exe)的规则文件, 其主要目的就是生成stripped文件, 但是这里有一个依赖链可以观察一下:
compress_input := $(linked_module)
compress_output := $(compress_input), 还有另一种情形暂时不支持, 不分析了.
symbolic_input := $(compress_output)
symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem), symbols下面的modules在设备下面的相对路径, 比如symbols/system/lib/libcutils.so.
$(symbolic_output) : $(symbolic_input)
$(copy-file-to-target)
这么一段拖沓的动作其实本质就是把刚才链接的linked_module文件拷贝一份至symbols目录下, 然后:
strip_input := $(symbolic_output)
strip_output := $(LOCAL_BUILT_MODULE)
$(strip_output): $(strip_input)
$(transform-to-stripped)
这里的重点是strip_output也就是LOCAL_BUILT_MODULE, 在base_rules.mk里面有:
$(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
以及
my_register_name := LOCAL_MODULE
ALL_MODULES.$(my_register_name).INSTALLED := \
$(strip $(ALL_MODULES.$(my_register_name).INSTALLED) $(LOCAL_INSTALLED_MODULE))
LOCAL_INSTALLED_MODULE是模块最终的安装路径, 比如libcutils的就是system/lib/libcutils.so, 把这个追加到ALL_MODULES.$(LOCAL_MODULE).INSTALLED列表中, 作为system的组成部分, 在生成system.img的时候, 自然通过目标依赖链找到该模块, 然后完成链式编译, 在下文会再结合说明.
同时还有这段话:
$(my_register_name): $(LOCAL_BUILT_MODULE) $(LOCAL_INSTALLED_MODULE)
有了这句话, 就可以用mm/mmm编译该模块了, 请结合上文讲述mm/mmm的内容一起分析.
顺便再提一下LOCAL_BUILT_MODULE, 这个变量只能定义在base_rules.mk当中,否则会报错:
# OVERRIDE_BUILT_MODULE_PATH is only allowed to be used by the
# internal SHARED_LIBRARIES build files.
OVERRIDE_BUILT_MODULE_PATH := $(strip $(OVERRIDE_BUILT_MODULE_PATH))
ifdef OVERRIDE_BUILT_MODULE_PATH
ifneq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)
$(error $(LOCAL_PATH): Illegal use of OVERRIDE_BUILT_MODULE_PATH)
endif
built_module_path := $(OVERRIDE_BUILT_MODULE_PATH)
else
built_module_path := $(intermediates)
endif
LOCAL_BUILT_MODULE := $(built_module_path)/$(my_built_module_stem)
在shared_library_internal.mk导入dynamic_binary.mk前, 定义了:
# Put the built targets of all shared libraries in a common directory
# to simplify the link line.
OVERRIDE_BUILT_MODULE_PATH := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATE_LIBRARIES)
其中intermediates := $(call local-intermediates-dir,,$(LOCAL_2ND_ARCH_VAR_PREFIX)), 和上文的$(guessed_intermediates)是一致的.
简单点说, 如果是动态库, LOCAL_BUILT_MODULE就是obj/lib/XXX.so, 如果是可执行程序, 则是obj/EXECUTABLES/XXX_intermediates/XXX.
BUILD_STATIC_LIBRARY
BUILD_STATIC_LIBRARY := build/core/static_library.mk
同上文, 真正处理静态库的是static_library_internal.mk. 该文件内容很简单:
导入binary.mk以及定义目标规则:
$(LOCAL_BUILT_MODULE) : $(built_whole_libraries)
$(LOCAL_BUILT_MODULE) : $(all_objects)
$(transform-o-to-static-lib)
直接把.o文件转换成.a文件。
out目录下的几个输出目录
obj/STATIC_LIBRARIES, 静态库的路径, 包括源码对应的.o和依赖文件.P以及最后的.a文件, 静态库最终不会发布到镜像当中, 最后链接时会直接从这里取.
obj/SHARED_LIBRARIES, 动态库中间路径, 包括源码对应的.o和依赖文件.P以及LINKED目录内存放一个unstripped格式的.so.
obj/EXECUTABLES, 可执行程序中间路径, 包括源码对应的.o和依赖文件.P以及stripped格式的二进制, 还有LINKED目录内存放一个unstripped格式的二进制.
obj/lib, 动态库的中间路径, 但里面存放这stripped格式的.so.
symbols, 动态库的中间路径, 存放unstripped格式的, 是直接从LINKED下面copy来的, 根据注释内容, 是用于debugging使用的.
system/data等, 最后的发布路径, 也即LOCAL_INSTALLED_MODULE, stripped格式的动态库和可执行程序都会发布至此, 其内部结构, 默认地, 动态库存放在lib下面, 可执行程序存放在bin下面. 但是可以在Android.mk定义LOCAL_MODULE_PATH自行指定.
这里要有一点要注意, obj对应还有一个obj_arm目录, 里面是存放2ND_ARCH编译输出的, 一般就是32bit/64bit双版本结构, 这样是因为目前的Android还需要同时兼容两种位宽的程序. 而symbols和system等以最终安装路径摆放的, 则不需要这样区分, 因为路径中就是lib/lib64两种表述方式了.
从源码到二进制
这个过程由binary.mk规则文件实现, 其作用就是将各类源代码编译成为目标机器码(.o文件)
先看基本信息:
my_src_files := $(LOCAL_SRC_FILES)
my_static_libraries := $(LOCAL_STATIC_LIBRARIES)
my_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES)
my_shared_libraries := $(LOCAL_SHARED_LIBRARIES)
my_cflags := $(LOCAL_CFLAGS), 如果是EXECUTABLES, 追加-fpie, 否则追加-fPIC.
my_cppflags := $(LOCAL_CPPFLAGS)
my_ldflags := $(LOCAL_LDFLAGS)
my_asflags := $(LOCAL_ASFLAGS)
my_cc := $(LOCAL_CC)
my_cxx := $(LOCAL_CXX)
my_c_includes := $(LOCAL_C_INCLUDES)
my_generated_sources := $(LOCAL_GENERATED_SOURCES)
汇集Android.mk当中定义的局部变量, 以上是不区分体系架构和位宽的通用变量.
下面是处理当前选中的体系架构和位宽的变量, 追加在后面:
my_src_files += $(LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SRC_FILES_$(my_32_64_bit_suffix))
my_shared_libraries += $(LOCAL_SHARED_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SHARED_LIBRARIES_$(my_32_64_bit_suffix))
my_cflags += $(LOCAL_CFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CFLAGS_$(my_32_64_bit_suffix))
my_cppflags += $(LOCAL_CPPFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CPPFLAGS_$(my_32_64_bit_suffix))
my_ldflags += $(LOCAL_LDFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_LDFLAGS_$(my_32_64_bit_suffix))
my_asflags += $(LOCAL_ASFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_ASFLAGS_$(my_32_64_bit_suffix))
my_c_includes += $(LOCAL_C_INCLUDES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_C_INCLUDES_$(my_32_64_bit_suffix))
my_generated_sources += $(LOCAL_GENERATED_SOURCES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_GENERATED_SOURCES_$(my_32_64_bit_suffix))
存在这种情形是因为(以libcutils为例):
LOCAL_SRC_FILES_arm += \
arch-arm/memset32.S \
LOCAL_SRC_FILES_arm64 += \
arch-arm64/android_memset.S \
LOCAL_CFLAGS_arm += -DHAVE_MEMSET16 -DHAVE_MEMSET32
LOCAL_CFLAGS_arm64 += -DHAVE_MEMSET16 -DHAVE_MEMSET32
所以需要单独追加进来.
下面是把体系架构相关的静态库放在其他静态库之前, 因为在链接过程在, 静态库的顺序是有前后要求的, 一般体系架构相关的会被通用的依赖, 所以排在前面.
my_static_libraries := $(LOCAL_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_static_libraries)
my_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_WHOLE_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_whole_static_libraries)
关于编译flags, 除了上述定义在module脚本当中的, 还有一类是全局的flags, 这类flags在TARGET-side和HOST-side各不相同, 它们各自定义在build/core/combo下面对应的规则文件里面, 比如以target-arm64为例:
my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_CFLAGS)
TARGET_GLOBAL_CFLAGS则是定义在build/core/combo/TARGET_linux-arm64.mk当中.
接在再处理编译器, 比较典型的c/c++:
my_cc := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)CC)
my_cxx := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)CXX)
my_prefix可选HOST_/TARGET_两个, 比如TARGET_CXX, 这个也是定义在combo目录下对应文件中, 比如TARGET_linux-arm64.mk, 就定义为:TARGET_CXX := $(TARGET_TOOLS_PREFIX)g++$(HOST_EXECUTABLE_SUFFIX)
下面紧跟着, 是源码到二进制的转换规则, 这里支持各种类型的源码文件, 有的是lex/yacc等, 有的是通过私有工具生成的. 它们都会先转成c++文件, 然后c/c++/s/S这些文件会再编译成.o文件, 其目标集合是all_objects:
normal_objects := \
$(asm_objects) \ 常规的.s/.S/.asm文件编译而成, 通过函数$(transform-asm-to-o)
$(cpp_objects) \ 常规的.cpp文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(gen_cpp_objects) \ 通过私有工具生成的cpp文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(gen_asm_objects) \ 通过私有工具生成的.s/.S文件编译而成, 通过函数$(transform-asm-to-o)
$(c_objects) \ 常规的.c文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)c-to-o)
$(gen_c_objects) \ 通过私有工具生成的c文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)c-to-o)
$(objc_objects) \ Objective-C(.m)文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)m-to-o)
$(yacc_objects) \ YACC文件转成的cpp文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(lex_objects) \ LEX文件转成的cpp文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(proto_generated_objects) \ .proto文件转成的cc文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES))
all_objects := $(normal_objects) $(gen_o_objects)
最后是库的处理
all_libraries := \
$(built_shared_libraries) \
$(built_static_libraries) \
$(built_whole_libraries)
这些变量分别来自LOCAL_SHARED_LIBRARIES/LOCAL_SHARED_LIBRARIES/LOCAL_WHOLE_STATIC_LIBRARIES, 但是列表中的名字都是类似libc这样的引用名字, 需要转换成路径文件名, 其实就是对应这个库的LOCAL_BUILT_MODULE.
从上文的描述可以发现, 代码的编译是一组函数实现的, 这些函数都是实现在definitions.mk当中, 并且其中有些函数在definitions.mk里面借了个壳, 类似与API, 其实现在是在编译器的文件当中.
截个图大家感受下:
![](http://img.blog.csdn.net/20150918170519111)
是不是一目了然? 这些函数的参数, 除了目标和依赖是作为输出和输入的以外, 其余的比如编译选项, 头文件, 库等变量, 都是在binary.mk里面定义成PRIVATE_打头的变量再进行引用的:
![](http://img.blog.csdn.net/20150918170546371)
3 modules与OS的串联
这个关联主要实现在Makefile文件里面,main.mk进入Makefile的一段内容:
# 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 :=
modules_to_install变换成ALL_DEFAULT_INSTALLED_MODULES, Makefile会使用该变量.
生成system.img
在Makefile里面, systemimage目标展开如下:
$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
@echo "Install system fs image: $@"
$(copy-file-to-target)
$(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))
systemimage: $(INSTALLED_SYSTEMIMAGE)
其中:
INSTALLED_SYSTEMIMAGE := $(PRODUCT_OUT)/system.img
BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img
所以焦点集中在BUILT_SYSTEMIMAGE上面:
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
$(call build-systemimage-target,$@)
INSTALLED_FILES_FILE就是installed-files.txt,里面记录了所有要安装的文件. 比较重要的是FULL_SYSTEMIMAGE_DEPS:
FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)
INTERNAL_USERIMAGES_DEPS是host要用到的一些工具, 比如文件系统格式化之类的.
INTERNAL_SYSTEMIMAGE_FILES := $(filter $(TARGET_OUT)/%, \
$(ALL_PREBUILT) \
$(ALL_COPIED_HEADERS) \
$(ALL_GENERATED_SOURCES) \
$(ALL_DEFAULT_INSTALLED_MODULES) \
$(PDK_FUSION_SYSIMG_FILES) \
$(RECOVERY_RESOURCE_ZIP))
ALL_DEFAULT_INSTALLED_MODULES即modules_to_install, 在main.mk当中:
modules_to_install := $(sort \
$(ALL_DEFAULT_INSTALLED_MODULES) \
$(product_FILES) \
$(foreach tag,$(tags_to_install),$($(tag)_MODULES)) \
$(CUSTOM_MODULES) \
)
此时的ALL_DEFAULT_INSTALLED_MODULES还是空的.
product_FILES在FULL_BUILD的情形下进行如下变换:
product_MODULES := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES)
product_FILES := $(call module-installed-files, $(product_MODULES))
将PRODUCT_PACKAGES声明的modules转换成前面提到过的ALL_MODULES.$(my_register_name).INSTALLED然后存放在product_FILES.
$(tag)_MODULES也是一样,将匹配$(tag)的module进行转换.
CUSTOM_MODULES前面说过了,也是如此转换.
因此,这个列表基本上就包含了所有的module的ALL_MODULES.$(LOCAL_MODULE).INSTALLED值.换句话说,制作system.img将通过依赖链完成所有相关module的编译.
至此, 源码中Android.mk的LOCAL_MODULE就和OS images等核心目标关联到了一起. 当我们在终端下面敲下make时, android-builder将会完成:
doird -> droidcore -> xxx.img -> ALL_DEFAULT_INSTALLED_MODULES -> ALL_MODULES.$(LOCAL_MODULE).INSTALLED -> LOCAL_INSTALLED_MODULE -> LOCAL_BUILT_MODULE -> linked_module -> all_objects -> sources这个过程.
生成boot.img
目标动作定义在Makefile里面, 目标为INSTALLED_BOOTIMAGE_TARGET:
INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
该目标有三个变体,分别是: ext2格式;带签名验证;通过mkimage工具按照私有格式将uImage和ramdisk.img粘合而成的.
通常就是最后这一种, 展开如下:
$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
$(call pretty,"Target boot image: $@")
$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
$(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))
INTERNAL_BOOTIMAGE_FILES就是kernel(uIamge)和ramdisk.img这两个文件.
INTERNAL_BOOTIMAGE_ARGS是mkimage的参数,通常是四个:
--kernel $(INSTALLED_KERNEL_TARGET) 内核镜像文件
--ramdisk $(INSTALLED_RAMDISK_TARGET) ramdisk.img文件
INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)" 传递给内核的cmdline, 一般定义在BoardConfig.mk里面
INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE) 内核被加载到RAM里面时的基址, 一般定义在BoardConfig.mk里面
在不同场景下还支持其它几个参数,读者有兴趣可以自行阅读build/core/Makefile文件.
BOARD_MKBOOTIMG_ARGS可选, 通常定义在BoardConfig.mk, 内容常见的都是:
--ramdisk_offset 0x02900000 --tags_offset 0x02700000
也就是指出ramdisk和atag的地址.
最后mkbootimge程序根据这些参数生成boot.img.
主要分析Android OS以及module的编译行为. 首先先说明一下几个重要的makefile文件:
build/core/main.mk, 这是入口文件, 编译系统将从这里开始
build/core/Makefile, 系统内置目标的规则定义基本都在这个文件里面实现, 由main.mk导入.
build/core/base_rules.mk, 处理module的基本信息, 并提供给编译核心使用, 一般在Android.mk的导入链上的合适地方被导入进来. 其定义了丰富的变量可以供编译核心来处理当前这个module.
build/core/definitions.mk, 实现了一系列基本而重要的函数, 这样能够有效地把处理通用规则的代码抽取出来给各个模块重复使用.
1 主体流程描述
最开始是环境检查,在Android编译系统分析一当中已经分析过, 比较简单, 不再赘述.
TARGET_BUILD_VARIANT
该变量可选值(user,userdebug,eng),其各个值对编译后的系统的影响,可直接查看android官网描述:
eng | This is the default flavor. Installs modules tagged with: eng and/or debug. Installs modules according to the product definition files, in addition to tagged modules. ro.secure=0 ro.debuggable=1 ro.kernel.android.checkjni=1 adb is enabled by default. |
user | This is the flavor intended to be the final release bits. Installs modules tagged with user. Installs modules according to the product definition files, in addition to tagged modules. ro.secure=1 ro.debuggable=0 adb is disabled by default. |
userdebug | The same as user, except: Also installs modules tagged with debug. ro.debuggable=1 adb is enabled by default. |
ro.secure/ro.debuggable这两个property主要影响adb的行为,也就是adbd是否以root身份运行。
表格里面提到的tag, 就是各个模块Android.mk里面变量LOCAL_MODULE_TAG,其通常的取值是: eng/userdebug或者默认的optional. 如果模块没有定义这个变量,默认的optional表示OS仅装载PRODUCT_PACKAGES列表中声明的模块。这个变量不要定义成user,在5.0及以上系统中不再可用(5.0以下的没考察过,不清楚从哪个版本开始的),base_rules.mk里面会检查并报错。
在编译的时候如何选择某个VARIANT呢?看下图:
每个combo的最后一个字段就是VARIANT了,这个在<<Android编译系统分析(一)>>中已经分析过了。这里是aosp的源码,没有user的combo,实际上是有用的,一般是设备厂商最后发布的release版本。
回到make.mk文件,VARIANT最后会确定tags_to_install变量的内容,也就是最后将要被install的模块的tags(也就是模块可以声明成多个tag).
编译SDK
编译目标中含有: sdk win_sdk sdk_addon,则会编译SDK包(is_sdk_build变量被激活),如果HOST系统是windows的话,则只会编译SDK,也就是SDK_ONLY变量会被激活。
SDK_ONLY被激活,则对应的FULL_BUILD为空, 也就意味着product_FILES变量为空,这个变量可以认为就是PRODUCT_PACKAGES,所以也就是说大多数模块在这种模式下都不会被编译的,但是,被打上tags_to_install当中的tag的模块,还是会被编译的。
is_sdk_build会指示从目标编译模块中摘除GNU目标,可能是GPL授权的原因吧,gnu工具编译系统本身可以用用(作为host的目标制作出来),但是肯定是不能在sdk包中发布的。
除了在配置上被以上两个变量控制了以外, sdk的目标规则通过内部目标INTERNAL_SDK_TARGET定义在Makefile文件当中, 具体的这里不再赘述.
关于all_modules目标
这个目标一般是配合mm/mmm等shell函数使用(详见<<Android编译系统分析(一)>>), 它的定义如下
.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
在mm/mmm函数里没有定义BUILD_MODULES_IN_PATHS, 也就是上面的代码走第一个分支all_modules: $(ALL_MODULES), 那么ALL_MODULES在哪里? 这两个命令同时还定义了ONE_SHOT_MAKEFILE变量, 就是当前module的Android.mk, 当该变量非空, main.mk只会单独导入该Android.mk, 否则main.mk会扫描整个工程的Android.mk并都导入进来. 这条导入链最终会导入到base_rules.mk,
该文件里面定义了ALL_MODULES += $(my_register_name), 即ALL_MODULES += $(LOCAL_MODULE), 也即all_modules: $(LOCAL_MODULE). 所以这两个函数会借助all_modules编译当前module当中的所有LOCAL_MODULE.
在mma/mmma函数里少了ONE_SHOT_MAKEFILE, 多了BUILD_MODULES_IN_PATHS. 所以上面的代码走第二个分支. ONE_SHOT_MAKEFILE没有意味着会扫描所有的Android.mk,这个可以理解,毕竟是带着依赖模块一起编译,自然要扫描工程的。至于BUILD_MODULES_IN_PATHS变量,简单点说,mma里面就是PWD. 意义是:mm编译时, ALL_MODULES就是ONE_SHOT_MAKEFILE指向的Android.mk里面定义的LOCAL_MODULE,所以all_modules直接依赖就可以,而mma的ALL_MODULES则代表工程中所有Android.mk,因此不能直接使用,所以只能通过路径与module的对应关系找到需要编译的modules,然后制作成all_modules的依赖链.这个对应关系在base_rulse.mk里面处理,是为$(ALL_MODULES.$(module).PATH)与module的对应.
CUSTOM_MODULES
这个变量记录:
a.没有定义在PRODUCT_PACKAGES列表中的module;
b.没有对应到tags_to_install当中的tag的module.
变量内容分成两部分:
a.合法的LOCAL_MODULE,这类将会转换成ALL_MODULES.$(module).INSTALLED这个实际目标然后被处理;
b.自定义的目标也是可以加入到该列表中来的,一般这类目标及其动作规则是由开发者自己书写在Android.mk当中的.
DEFAULT_GOAL
缺省目标DEFAULT_GOAL为droid,其依赖的droidcore目标是AndroidOS的核心目标,各种系统镜像的make目标都被该目标所依赖,直观点,见下:
droidcore: files \
systemimage \
$(INSTALLED_BOOTIMAGE_TARGET) \
$(INSTALLED_RECOVERYIMAGE_TARGET) \
$(INSTALLED_USERDATAIMAGE_TARGET) \
$(INSTALLED_CACHEIMAGE_TARGET) \
$(INSTALLED_VENDORIMAGE_TARGET) \
$(INSTALLED_FILES_FILE)
我们通常整体编译AndroidOS, 就是编译这个目标.
其它一些常见的内置目标
ramdisk, 将root目录(即rootfs)制作成ramdisk.img.
bootimage, 将kernel(一般就是uImage)和ramdisk.img粘合在一起制作成boot.img.
systemimage, 将system目录制作成system.img.
userdataimage, 将data目录制作成userdata.img.
systemtarball, 把system分区的内容打包成tar压缩包
boottarball, 把kernel和ramdisk.img打包成tar压缩包
userdatatarball, 把userdata分区的内容打包成tar压缩包
nothing, 什么都不做,但是会把工程所有的makefiles都检查一遍,也就是检查编译脚本用的。
dont_bother_goals, 如果目标是这一类的话,不会扫描工程的Android.mk文件。
2 module的编译(Android.mk)
以libcutils和adb为例,因为编译动态库和可执行程序的过程比较相似,所以放在一起分析,以动态库的为主,可执行程序有差异的地方再单独说明。
一般地,module的Android.mk都会定义LOCAL_SRC_FILES, LOCAL_CFLAGS等变量, 然后直接导入这三类规则文件:BUILD_STATIC_LIBRARY或者BUILD_SHARED_LIBRARY或者BUILD_EXECUTABLE.
BUILD_SHARED_LIBRARY或者BUILD_EXECUTABLE
以BUILD_SHARED_LIBRARY为例:
BUILD_SHARED_LIBRARY := build/core/shared_library.mk
在引入64位以后,这个文件自身仅处理32/64模块的的选择问题,这里不讨论。
真正处理动态库的是shared_library_internal.mk, 这个文件导入dynamic_binary.mk并完成linked_module这个目标:
linked_module, 其定义在dynamic_binary.mk:
guessed_intermediates := $(call local-intermediates-dir,,$(LOCAL_2ND_ARCH_VAR_PREFIX))
linked_module := $(guessed_intermediates)/LINKED/$(my_built_module_stem)
展开成:
linked_module = obj/EXECUTABLES/XXX_intermediates/LINKED/XXX, 这是程序的
或者
linked_module = obj/SHARED_LIBRARIES/XXX_intermediates/LINKED/XXX.so, 这是库的
linked_module目标规则定义在本文件:
$(linked_module): $(all_objects) $(all_libraries) \
$(LOCAL_ADDITIONAL_DEPENDENCIES) \
$(my_target_crtbegin_so_o) $(my_target_crtend_so_o)
$(transform-o-to-shared-lib)
依赖链当中,$(all_objects) $(all_libraries)定义在binary.mk, all_objects是module自己的源代码编译成的目标代码(在binary.mk完成编译), all_libraries是模块依赖的所有外部库, 比如libcutils. $(LOCAL_ADDITIONAL_DEPENDENCIES)由开发者自己定义在Android.mk当中, 用于追加一个私有的目标, 可以方便开发者灵活处理自己的module.
$(my_target_crtbegin_so_o) $(my_target_crtend_so_o)是bionic-c库的组成部分,这是生成elf文件的辅助库.
$(transform-o-to-shared-lib)定义在defintions.mk,顾名思义,就不多说了,最后得到我们中间目标obj/SHARED_LIBRARIES/XXX_intermediates/LINKED/XXX.so.
再看导入的dynamic_binary.mk文件,这个文件是针对动态库和可执行程序(包括静态链接的exe)的规则文件, 其主要目的就是生成stripped文件, 但是这里有一个依赖链可以观察一下:
compress_input := $(linked_module)
compress_output := $(compress_input), 还有另一种情形暂时不支持, 不分析了.
symbolic_input := $(compress_output)
symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem), symbols下面的modules在设备下面的相对路径, 比如symbols/system/lib/libcutils.so.
$(symbolic_output) : $(symbolic_input)
$(copy-file-to-target)
这么一段拖沓的动作其实本质就是把刚才链接的linked_module文件拷贝一份至symbols目录下, 然后:
strip_input := $(symbolic_output)
strip_output := $(LOCAL_BUILT_MODULE)
$(strip_output): $(strip_input)
$(transform-to-stripped)
这里的重点是strip_output也就是LOCAL_BUILT_MODULE, 在base_rules.mk里面有:
$(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
以及
my_register_name := LOCAL_MODULE
ALL_MODULES.$(my_register_name).INSTALLED := \
$(strip $(ALL_MODULES.$(my_register_name).INSTALLED) $(LOCAL_INSTALLED_MODULE))
LOCAL_INSTALLED_MODULE是模块最终的安装路径, 比如libcutils的就是system/lib/libcutils.so, 把这个追加到ALL_MODULES.$(LOCAL_MODULE).INSTALLED列表中, 作为system的组成部分, 在生成system.img的时候, 自然通过目标依赖链找到该模块, 然后完成链式编译, 在下文会再结合说明.
同时还有这段话:
$(my_register_name): $(LOCAL_BUILT_MODULE) $(LOCAL_INSTALLED_MODULE)
有了这句话, 就可以用mm/mmm编译该模块了, 请结合上文讲述mm/mmm的内容一起分析.
顺便再提一下LOCAL_BUILT_MODULE, 这个变量只能定义在base_rules.mk当中,否则会报错:
# OVERRIDE_BUILT_MODULE_PATH is only allowed to be used by the
# internal SHARED_LIBRARIES build files.
OVERRIDE_BUILT_MODULE_PATH := $(strip $(OVERRIDE_BUILT_MODULE_PATH))
ifdef OVERRIDE_BUILT_MODULE_PATH
ifneq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)
$(error $(LOCAL_PATH): Illegal use of OVERRIDE_BUILT_MODULE_PATH)
endif
built_module_path := $(OVERRIDE_BUILT_MODULE_PATH)
else
built_module_path := $(intermediates)
endif
LOCAL_BUILT_MODULE := $(built_module_path)/$(my_built_module_stem)
在shared_library_internal.mk导入dynamic_binary.mk前, 定义了:
# Put the built targets of all shared libraries in a common directory
# to simplify the link line.
OVERRIDE_BUILT_MODULE_PATH := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATE_LIBRARIES)
其中intermediates := $(call local-intermediates-dir,,$(LOCAL_2ND_ARCH_VAR_PREFIX)), 和上文的$(guessed_intermediates)是一致的.
简单点说, 如果是动态库, LOCAL_BUILT_MODULE就是obj/lib/XXX.so, 如果是可执行程序, 则是obj/EXECUTABLES/XXX_intermediates/XXX.
BUILD_STATIC_LIBRARY
BUILD_STATIC_LIBRARY := build/core/static_library.mk
同上文, 真正处理静态库的是static_library_internal.mk. 该文件内容很简单:
导入binary.mk以及定义目标规则:
$(LOCAL_BUILT_MODULE) : $(built_whole_libraries)
$(LOCAL_BUILT_MODULE) : $(all_objects)
$(transform-o-to-static-lib)
直接把.o文件转换成.a文件。
out目录下的几个输出目录
obj/STATIC_LIBRARIES, 静态库的路径, 包括源码对应的.o和依赖文件.P以及最后的.a文件, 静态库最终不会发布到镜像当中, 最后链接时会直接从这里取.
obj/SHARED_LIBRARIES, 动态库中间路径, 包括源码对应的.o和依赖文件.P以及LINKED目录内存放一个unstripped格式的.so.
obj/EXECUTABLES, 可执行程序中间路径, 包括源码对应的.o和依赖文件.P以及stripped格式的二进制, 还有LINKED目录内存放一个unstripped格式的二进制.
obj/lib, 动态库的中间路径, 但里面存放这stripped格式的.so.
symbols, 动态库的中间路径, 存放unstripped格式的, 是直接从LINKED下面copy来的, 根据注释内容, 是用于debugging使用的.
system/data等, 最后的发布路径, 也即LOCAL_INSTALLED_MODULE, stripped格式的动态库和可执行程序都会发布至此, 其内部结构, 默认地, 动态库存放在lib下面, 可执行程序存放在bin下面. 但是可以在Android.mk定义LOCAL_MODULE_PATH自行指定.
这里要有一点要注意, obj对应还有一个obj_arm目录, 里面是存放2ND_ARCH编译输出的, 一般就是32bit/64bit双版本结构, 这样是因为目前的Android还需要同时兼容两种位宽的程序. 而symbols和system等以最终安装路径摆放的, 则不需要这样区分, 因为路径中就是lib/lib64两种表述方式了.
从源码到二进制
这个过程由binary.mk规则文件实现, 其作用就是将各类源代码编译成为目标机器码(.o文件)
先看基本信息:
my_src_files := $(LOCAL_SRC_FILES)
my_static_libraries := $(LOCAL_STATIC_LIBRARIES)
my_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES)
my_shared_libraries := $(LOCAL_SHARED_LIBRARIES)
my_cflags := $(LOCAL_CFLAGS), 如果是EXECUTABLES, 追加-fpie, 否则追加-fPIC.
my_cppflags := $(LOCAL_CPPFLAGS)
my_ldflags := $(LOCAL_LDFLAGS)
my_asflags := $(LOCAL_ASFLAGS)
my_cc := $(LOCAL_CC)
my_cxx := $(LOCAL_CXX)
my_c_includes := $(LOCAL_C_INCLUDES)
my_generated_sources := $(LOCAL_GENERATED_SOURCES)
汇集Android.mk当中定义的局部变量, 以上是不区分体系架构和位宽的通用变量.
下面是处理当前选中的体系架构和位宽的变量, 追加在后面:
my_src_files += $(LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SRC_FILES_$(my_32_64_bit_suffix))
my_shared_libraries += $(LOCAL_SHARED_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SHARED_LIBRARIES_$(my_32_64_bit_suffix))
my_cflags += $(LOCAL_CFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CFLAGS_$(my_32_64_bit_suffix))
my_cppflags += $(LOCAL_CPPFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CPPFLAGS_$(my_32_64_bit_suffix))
my_ldflags += $(LOCAL_LDFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_LDFLAGS_$(my_32_64_bit_suffix))
my_asflags += $(LOCAL_ASFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_ASFLAGS_$(my_32_64_bit_suffix))
my_c_includes += $(LOCAL_C_INCLUDES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_C_INCLUDES_$(my_32_64_bit_suffix))
my_generated_sources += $(LOCAL_GENERATED_SOURCES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_GENERATED_SOURCES_$(my_32_64_bit_suffix))
存在这种情形是因为(以libcutils为例):
LOCAL_SRC_FILES_arm += \
arch-arm/memset32.S \
LOCAL_SRC_FILES_arm64 += \
arch-arm64/android_memset.S \
LOCAL_CFLAGS_arm += -DHAVE_MEMSET16 -DHAVE_MEMSET32
LOCAL_CFLAGS_arm64 += -DHAVE_MEMSET16 -DHAVE_MEMSET32
所以需要单独追加进来.
下面是把体系架构相关的静态库放在其他静态库之前, 因为在链接过程在, 静态库的顺序是有前后要求的, 一般体系架构相关的会被通用的依赖, 所以排在前面.
my_static_libraries := $(LOCAL_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_static_libraries)
my_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_WHOLE_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_whole_static_libraries)
关于编译flags, 除了上述定义在module脚本当中的, 还有一类是全局的flags, 这类flags在TARGET-side和HOST-side各不相同, 它们各自定义在build/core/combo下面对应的规则文件里面, 比如以target-arm64为例:
my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_CFLAGS)
TARGET_GLOBAL_CFLAGS则是定义在build/core/combo/TARGET_linux-arm64.mk当中.
接在再处理编译器, 比较典型的c/c++:
my_cc := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)CC)
my_cxx := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)CXX)
my_prefix可选HOST_/TARGET_两个, 比如TARGET_CXX, 这个也是定义在combo目录下对应文件中, 比如TARGET_linux-arm64.mk, 就定义为:TARGET_CXX := $(TARGET_TOOLS_PREFIX)g++$(HOST_EXECUTABLE_SUFFIX)
下面紧跟着, 是源码到二进制的转换规则, 这里支持各种类型的源码文件, 有的是lex/yacc等, 有的是通过私有工具生成的. 它们都会先转成c++文件, 然后c/c++/s/S这些文件会再编译成.o文件, 其目标集合是all_objects:
normal_objects := \
$(asm_objects) \ 常规的.s/.S/.asm文件编译而成, 通过函数$(transform-asm-to-o)
$(cpp_objects) \ 常规的.cpp文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(gen_cpp_objects) \ 通过私有工具生成的cpp文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(gen_asm_objects) \ 通过私有工具生成的.s/.S文件编译而成, 通过函数$(transform-asm-to-o)
$(c_objects) \ 常规的.c文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)c-to-o)
$(gen_c_objects) \ 通过私有工具生成的c文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)c-to-o)
$(objc_objects) \ Objective-C(.m)文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)m-to-o)
$(yacc_objects) \ YACC文件转成的cpp文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(lex_objects) \ LEX文件转成的cpp文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(proto_generated_objects) \ .proto文件转成的cc文件编译而成, 通过函数$(transform-$(PRIVATE_HOST)cpp-to-o)
$(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES))
all_objects := $(normal_objects) $(gen_o_objects)
最后是库的处理
all_libraries := \
$(built_shared_libraries) \
$(built_static_libraries) \
$(built_whole_libraries)
这些变量分别来自LOCAL_SHARED_LIBRARIES/LOCAL_SHARED_LIBRARIES/LOCAL_WHOLE_STATIC_LIBRARIES, 但是列表中的名字都是类似libc这样的引用名字, 需要转换成路径文件名, 其实就是对应这个库的LOCAL_BUILT_MODULE.
从上文的描述可以发现, 代码的编译是一组函数实现的, 这些函数都是实现在definitions.mk当中, 并且其中有些函数在definitions.mk里面借了个壳, 类似与API, 其实现在是在编译器的文件当中.
截个图大家感受下:
是不是一目了然? 这些函数的参数, 除了目标和依赖是作为输出和输入的以外, 其余的比如编译选项, 头文件, 库等变量, 都是在binary.mk里面定义成PRIVATE_打头的变量再进行引用的:
3 modules与OS的串联
这个关联主要实现在Makefile文件里面,main.mk进入Makefile的一段内容:
# 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 :=
modules_to_install变换成ALL_DEFAULT_INSTALLED_MODULES, Makefile会使用该变量.
生成system.img
在Makefile里面, systemimage目标展开如下:
$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
@echo "Install system fs image: $@"
$(copy-file-to-target)
$(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))
systemimage: $(INSTALLED_SYSTEMIMAGE)
其中:
INSTALLED_SYSTEMIMAGE := $(PRODUCT_OUT)/system.img
BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img
所以焦点集中在BUILT_SYSTEMIMAGE上面:
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
$(call build-systemimage-target,$@)
INSTALLED_FILES_FILE就是installed-files.txt,里面记录了所有要安装的文件. 比较重要的是FULL_SYSTEMIMAGE_DEPS:
FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)
INTERNAL_USERIMAGES_DEPS是host要用到的一些工具, 比如文件系统格式化之类的.
INTERNAL_SYSTEMIMAGE_FILES := $(filter $(TARGET_OUT)/%, \
$(ALL_PREBUILT) \
$(ALL_COPIED_HEADERS) \
$(ALL_GENERATED_SOURCES) \
$(ALL_DEFAULT_INSTALLED_MODULES) \
$(PDK_FUSION_SYSIMG_FILES) \
$(RECOVERY_RESOURCE_ZIP))
ALL_DEFAULT_INSTALLED_MODULES即modules_to_install, 在main.mk当中:
modules_to_install := $(sort \
$(ALL_DEFAULT_INSTALLED_MODULES) \
$(product_FILES) \
$(foreach tag,$(tags_to_install),$($(tag)_MODULES)) \
$(CUSTOM_MODULES) \
)
此时的ALL_DEFAULT_INSTALLED_MODULES还是空的.
product_FILES在FULL_BUILD的情形下进行如下变换:
product_MODULES := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES)
product_FILES := $(call module-installed-files, $(product_MODULES))
将PRODUCT_PACKAGES声明的modules转换成前面提到过的ALL_MODULES.$(my_register_name).INSTALLED然后存放在product_FILES.
$(tag)_MODULES也是一样,将匹配$(tag)的module进行转换.
CUSTOM_MODULES前面说过了,也是如此转换.
因此,这个列表基本上就包含了所有的module的ALL_MODULES.$(LOCAL_MODULE).INSTALLED值.换句话说,制作system.img将通过依赖链完成所有相关module的编译.
至此, 源码中Android.mk的LOCAL_MODULE就和OS images等核心目标关联到了一起. 当我们在终端下面敲下make时, android-builder将会完成:
doird -> droidcore -> xxx.img -> ALL_DEFAULT_INSTALLED_MODULES -> ALL_MODULES.$(LOCAL_MODULE).INSTALLED -> LOCAL_INSTALLED_MODULE -> LOCAL_BUILT_MODULE -> linked_module -> all_objects -> sources这个过程.
生成boot.img
目标动作定义在Makefile里面, 目标为INSTALLED_BOOTIMAGE_TARGET:
INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
该目标有三个变体,分别是: ext2格式;带签名验证;通过mkimage工具按照私有格式将uImage和ramdisk.img粘合而成的.
通常就是最后这一种, 展开如下:
$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
$(call pretty,"Target boot image: $@")
$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
$(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))
INTERNAL_BOOTIMAGE_FILES就是kernel(uIamge)和ramdisk.img这两个文件.
INTERNAL_BOOTIMAGE_ARGS是mkimage的参数,通常是四个:
--kernel $(INSTALLED_KERNEL_TARGET) 内核镜像文件
--ramdisk $(INSTALLED_RAMDISK_TARGET) ramdisk.img文件
INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)" 传递给内核的cmdline, 一般定义在BoardConfig.mk里面
INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE) 内核被加载到RAM里面时的基址, 一般定义在BoardConfig.mk里面
在不同场景下还支持其它几个参数,读者有兴趣可以自行阅读build/core/Makefile文件.
BOARD_MKBOOTIMG_ARGS可选, 通常定义在BoardConfig.mk, 内容常见的都是:
--ramdisk_offset 0x02900000 --tags_offset 0x02700000
也就是指出ramdisk和atag的地址.
最后mkbootimge程序根据这些参数生成boot.img.
相关文章推荐
- Xcode更新之后提示App Transport Security has blocked a cleartext HTTP(http://)resource load since it is ...
- http://ogldev.atspace.co.uk/
- js中数组的字符串表示
- postgreSQL问题一解答
- 那些值得学习的精美邮件模板案例
- 为什么数据库有的大有的小?
- 贪吃的CWT loser题解
- 回溯法
- [noip2013][cdoevs3285]转圈游戏
- 增强for循环用法___ArrayList数组实现使用下标最好,LinkedList使用增强型的(转载)
- mysql 子查询优化
- 切点算法模板(Cut-vertex)
- java即时通信,推送技术详解
- JS实现兼容性好,自动置顶的淘宝悬浮工具栏效果
- Objective-c 块语法
- c++没有实例化的类成员访问
- php实例-注册&登录
- VC下静态链接库与动态链接库
- 1394 - And Then There Was One(约瑟夫问题变形)DP
- Java学习笔记08 泛型