Android编译系统学习总结
Android编译系统
- Android编译系统
- 1. makefile入门
- 2. Android编译系统
- 3. Java编译链(java android compiler kit)
- 4. SDK的编译过程
- 5. Android GDB调试过程
1. makefile入门
- makefile本质是创建的一种“规则”,根据“规则”按指定顺序一步步执行,可以用它来编译系统、生成文档、打印log等
- makefile也是一种脚本,和shell、python等类似,由make程序来解析
- make解析程序种类有很多,android中采用的是GNU make程序来编译系统
- 不同的make解析程序对应的makefile语法也有差异,但是这些makefile语法都是根据基础规则扩展起来的
其基本规则如下:
target : prerequisites command //每个command前都必须有一个TAB制表符
- target: 是指需要生成的目标文件,它可以是可执行文件,还可以是一个标签(Label),如果target是标签则表示“伪目标”
- prerequisites:要生成那个target所依赖的所有文件
- command:表示其生成规则定义,即prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的规则命令就会被执行
虽然写好一个Makefile并不简单,但是可以先给出简单列子来对Makefile语法有个初步认识。
文件名 | 描述 |
---|---|
main.c | 主函数 |
base.h | 相关头文件,为用到的方法进行声明 |
base.c | 为主函数提供需要用的方法 |
Makefile | 编译规则 |
这个例子主要功能就是打印字符,main.c源码如下:
#include #include"base.h"int main(){ printf("hello World,getNumber method can get the value is:%d",getNumber()); return 0;}
base.c源码如下:
#include"base.h"int getNumber(){ return 7;}
base.h源码如下:
int getNumber();
Makefile建立编译规则,其源码如下:
SimpleMakefile: main.o base.o //规则一: 它依赖main.o base.o2个文件,即它依赖规则二和规则三的结果 gcc -o SimpleMakefile main.o base.omain.o:main.c //规则二: 它依赖main.c文件 gcc -o main.cbase.o:base.c //规则三: 它依赖 base.c文件 gcc -c base.c
部分makefile语法总结
1. 符号%
表示一到多个文件
2. :=
是makefile中的一种赋值语法,它相比于普通的赋值语法=
,会禁止前面变量使用后面的变量。
如:
Y:=$(X)bar //此时 X为空,Y为barX:=foo //此时 X为fooY:=$(X)bar //此时Y为foobarX:=later //此时 X为later
- 用
#
定义空格:
nullString:=space:=$(nullString) //eng of line nullString表示一个空变量,space的值是一个空格
?=
是表示变量是否被定义过,如果没有定义过则该变量就为后面的值
Foo?=bar //如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做//其等价于:ifeq ($(origin FOO), undefined)FOO = barendif
- 变量替换,其格式为
$(var:a=b)
,表示把变量【var】中所有以a字符结尾的替换成b字符串
foo := a.o b.o c.o //先定义了一个“$(foo)”变量bar := $(foo:.o=.c) //把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c” bar := a.c b.c c.c
make在读取Makefile时就计算条件表达的值,并根据条件表达的值来选择语句。而自动化变量(如
$@
等)是在运行时才有的,所以Makefile不允许把整个条件语句分成两部分放在不同文件中命令执行,make解析程序会一条一条的执行其后面的命令,如果上一条命令的结果应用在下一条命令时,可用分号
;
隔开两条命令
//示例一:exec:cd /home/hchenpwd //cd没有作用,pwd会打印出当前的Makefile目录//示例二:exec:cd /home/hchen; pwd //cd起作用了,pwd会打印出“/home/hchen
- foreach 函数,类似于shell中的for循环,其格式为
$(foreach ,
- ,
)
把参数
中的单词逐一取出放到参数所指定的变量中,然后再执行
所包含的表达式。返回的字符串所组成的整个字符(以空格分隔)作为foreach
函数的返回值。
最好是一个变量名,
可以是一个表达式,而
中一般会使用这个参数来依次枚举
中的单词。举个例子:
names := a b c dfiles := $(foreach n,$(names),$(n).o) // 等价于files :=a.o b.o c.o d.o
- Makefile中的函数,很像变量的使用,以
$
符号标识,其语法如$(
。)
就是函数名,make支持的函数不多。是函数的参数,参数间以逗号
,
分隔,而函数名和参数之间以“空格”分隔。
comma:= ,empty:=space:= $(empty) $(empty)foo:= a b cbar:= $(subst $(space),$(comma),$(foo)) //函数“subst”是一个替换函数,这个函数有三个参数,第一个参数是被替换字串,第二个参数是替换字串,第三个参数是替换操作作用的字串//替换后的结果是 bar:=a,b,c
- if函数,很类似于shell中的ifeq,其语法是
$(if
或者是, ) $(if
。判定条件是, , )
是否为空字符串 - 执行指定Makefile,一般默认的Makefile执行顺序:
- ① GNUmakefile
- ② makefile
- ③ Makefile
也可以给make命令指定一个特殊名字的Makefile,需要使用make的“-f”或是“–file”参数。例如有个makefile的名字是“hchen.mk”,执行命令如下:
make –f hchen.mk
- call函数 ,用来创建新的参数化的函数,其语法是
$(call
,如:, , , ...)
reverse := $(1) $(2)foo := $(call reverse,a,b) //foo:=a breverse := $(2) $(1)foo := $(call reverse,a,b) //foo:=b a
2. Android编译系统
android 编译系统构建原则:
1. 同一套代码支持编译多个目标
2. 用唯一的Makefile组织编译所以文件
3. 单独模块可用独立编译
4. 中间文件、源码、编译结果在存储目录上分离
对于Android的编译系统,主要由如下4步构成,其中编译系统的核心是有效的构建依赖树。
Created with Raphaël 2.1.2 初始化环境 构建依赖树 执行编译流程 打包通过下面系统源码可以看到,该编译系统的根节点droid。但实际上droid只是一个伪目标,在编译系统中相当于有先占一个位置,声明一下依赖树的根节点,具体的编译依赖树还需要根绝传入的参数TARGET_BUILD_APPS
来确定。
#build/core/main.mk#(此处省略部分代码.......)# This is the default target. It must be the first declared target..PHONY: droidDEFAULT_GOAL := droid$(DEFAULT_GOAL):#(此处省略部分代码.......)ifneq ($(TARGET_BUILD_APPS),) # If this build is just for apps, only build apps and not the full system by default. unbundled_build_modules := ifneq ($(filter all,$(TARGET_BUILD_APPS)),) # If they used the magic goal "all" then build all apps in the source tree. unbundled_build_modules := $(foreach m,$(sort $(ALL_MODULES)),$(if $(filter APPS,$(ALL_MODULES.$(m).CLASS)),$(m))) else unbundled_build_modules := $(TARGET_BUILD_APPS) endif # Dist the installed files if they exist. apps_only_installed_files := $(foreach m,$(unbundled_build_modules),$(ALL_MODULES.$(m).INSTALLED)) $(call dist-for-goals,apps_only, $(apps_only_installed_files)) # For uninstallable modules such as static Java library, we have to dist the built file, # as . apps_only_dist_built_files := $(foreach m,$(unbundled_build_modules),$(if $(ALL_MODULES.$(m).INSTALLED),,\ $(if $(ALL_MODULES.$(m).BUILT),$(ALL_MODULES.$(m).BUILT):$(m)$(suffix $(ALL_MODULES.$(m).BUILT)))\ $(if $(ALL_MODULES.$(m).AAR),$(ALL_MODULES.$(m).AAR):$(m).aar)\ )) $(call dist-for-goals,apps_only, $(apps_only_dist_built_files)) ifeq ($(EMMA_INSTRUMENT),true) $(EMMA_META_ZIP) : $(apps_only_installed_files) $(call dist-for-goals,apps_only, $(EMMA_META_ZIP)) endif $(PROGUARD_DICT_ZIP) : $(apps_only_installed_files) $(call dist-for-goals,apps_only, $(PROGUARD_DICT_ZIP)) $(SYMBOLS_ZIP) : $(apps_only_installed_files) $(call dist-for-goals,apps_only, $(SYMBOLS_ZIP)).PHONY: apps_onlyapps_only: $(unbundled_build_modules)droid: apps_only# Combine the NOTICE files for a apps_only build$(eval $(call combine-notice-files, \ $(target_notice_file_txt), \ $(target_notice_file_html), \ "Notices for files for apps:", \ $(TARGET_OUT_NOTICE_FILES), \ $(apps_only_installed_files)))else # TARGET_BUILD_APPS $(call dist-for-goals, droidcore, \ $(INTERNAL_UPDATE_PACKAGE_TARGET) \ $(INTERNAL_OTA_PACKAGE_TARGET) \ $(BUILT_OTATOOLS_PACKAGE) \ $(SYMBOLS_ZIP) \ $(INSTALLED_FILES_FILE) \ $(INSTALLED_BUILD_PROP_TARGET) \ $(BUILT_TARGET_FILES_PACKAGE) \ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \ $(INSTALLED_RAMDISK_TARGET) \ ) # Put a copy of the radio/bootloader files in the dist dir. $(foreach f,$(INSTALLED_RADIOIMAGE_TARGET), \ $(call dist-for-goals, droidcore, $(f))) ifneq ($(ANDROID_BUILD_EMBEDDED),true) ifneq ($(TARGET_BUILD_PDK),true) $(call dist-for-goals, droidcore, \ $(APPS_ZIP) \ $(INTERNAL_EMULATOR_PACKAGE_TARGET) \ $(PACKAGE_STATS_FILE) \ ) endif endif ifeq ($(EMMA_INSTRUMENT),true) $(EMMA_META_ZIP) : $(INSTALLED_SYSTEMIMAGE) $(call dist-for-goals, dist_files, $(EMMA_META_ZIP)) endif# Building a full system-- the default is to build droidcoredroid: droidcore dist_filesendif # TARGET_BUILD_APPS
在代码中,可以看到根绝参数TARGET_BUILD_APPS
是否为空可以分成2条不同分支,具体如下图所示。
在编译整个android系统时,从droid: droidcore dist_files
中可以看到,它分别依赖droidcore和dist_files2个目标。
目标droidcore
它又分别依赖其他的子目标:
# Build files and then package it into the rom formats.PHONY: droidcoredroidcore: files \ systemimage \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(INSTALLED_RECOVERYIMAGE_TARGET) \ $(INSTALLED_USERDATAIMAGE_TARGET) \ $(INSTALLED_CACHEIMAGE_TARGET) \ $(INSTALLED_VENDORIMAGE_TARGET) \ $(INSTALLED_FILES_FILE)
字段 | 描述 |
---|---|
systemimage | 将生成system.img |
$(INSTALLED_BOOTIMAGE_TARGET) | 将生成boot.img |
$(INSTALLED_RECOVERYIMAGE_TARGET) | 将生成recovery.img |
$(INSTALLED_USERDATAIMAGE_TARGET) | 将生成userdata.img |
$(INSTALLED_CACHEIMAGE_TARGET) | 将生成cache.img |
$(INSTALLED_VENDORIMAGE_TARGET) | 将生成vendor.img |
$(INSTALLED_FILES_FILE) | 将生成install_files.txt,记录当前系统预安装的程序、库等模块 |
此处可以推测出,通过目标droidcore可以生成系统的所有可运行程序包。
目标dist_files
该目标主要是在out目录下产生专门的dist文件夹,用于存储多种分发包,相关描述如下所示:
# dist_files only for putting your library into the dist directory with a full build..PHONY: dist_files
3. Java编译链(java android compiler kit)
在android6.0之后,系统采用了全新的java编译链Jack(java android compiler kit),它的主要任务是取代以前版本中的javac、ProGuard、jarjar、dx等工具,它的优势是编译速度快内建shrinking、obfuscation、repackaging、multidex的工具,并且开源。
4. SDK的编译过程
5. Android GDB调试过程
参考:《深入理解android内核设计思想》
更多相关文章
- 理解 Android(安卓)Build 系统
- android avd配置路径
- Mac配置Android环境变量
- Android(安卓)JNI实战
- Android获得系统(system)权限
- Android(安卓)NDK概述
- 在ubuntu上编译android的mupdf
- android JNI 调用
- Android之NDK开发