Android编译系统

  • Android编译系统
    • 1. makefile入门
    • 2. Android编译系统
    • 3. Java编译链(java android compiler kit)
    • 4. SDK的编译过程
    • 5. Android GDB调试过程

1. makefile入门

  1. makefile本质是创建的一种“规则”,根据“规则”按指定顺序一步步执行,可以用它来编译系统、生成文档、打印log等
  2. makefile也是一种脚本,和shell、python等类似,由make程序来解析
  3. make解析程序种类有很多,android中采用的是GNU make程序来编译系统
  4. 不同的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
  1. #定义空格:
nullString:=space:=$(nullString) //eng of line  nullString表示一个空变量,space的值是一个空格
  1. ?=是表示变量是否被定义过,如果没有定义过则该变量就为后面的值
Foo?=bar //如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做//其等价于:ifeq ($(origin FOO), undefined)FOO = barendif
  1. 变量替换,其格式为$(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
  1. make在读取Makefile时就计算条件表达的值,并根据条件表达的值来选择语句。而自动化变量(如$@等)是在运行时才有的,所以Makefile不允许把整个条件语句分成两部分放在不同文件中

  2. 命令执行,make解析程序会一条一条的执行其后面的命令,如果上一条命令的结果应用在下一条命令时,可用分号;隔开两条命令

//示例一:exec:cd /home/hchenpwd  //cd没有作用,pwd会打印出当前的Makefile目录//示例二:exec:cd /home/hchen; pwd  //cd起作用了,pwd会打印出“/home/hchen
  1. foreach 函数,类似于shell中的for循环,其格式为$(foreach ,,)
    把参数中的单词逐一取出放到参数所指定的变量中,然后再执行所包含的表达式。返回的字符串所组成的整个字符(以空格分隔)作为foreach函数的返回值。
    最好是一个变量名,可以是一个表达式,而中一般会使用这个参数来依次枚举中的单词。举个例子:
names := a b c dfiles := $(foreach n,$(names),$(n).o)   // 等价于files :=a.o b.o c.o d.o
  1. Makefile中的函数,很像变量的使用,以$符号标识,其语法如$( )就是函数名,make支持的函数不多。是函数的参数,参数间以逗号,分隔,而函数名和参数之间以“空格”分隔。
comma:= ,empty:=space:= $(empty) $(empty)foo:= a b cbar:= $(subst $(space),$(comma),$(foo))  //函数“subst”是一个替换函数,这个函数有三个参数,第一个参数是被替换字串,第二个参数是替换字串,第三个参数是替换操作作用的字串//替换后的结果是   bar:=a,b,c
  1. if函数,很类似于shell中的ifeq,其语法是$(if , )或者是$(if ,, )。判定条件是是否为空字符串
  2. 执行指定Makefile,一般默认的Makefile执行顺序:
    • ① GNUmakefile
    • ② makefile
    • ③ Makefile

也可以给make命令指定一个特殊名字的Makefile,需要使用make的“-f”或是“–file”参数。例如有个makefile的名字是“hchen.mk”,执行命令如下:

make –f hchen.mk
  1. 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内核设计思想》

更多相关文章

  1. 理解 Android(安卓)Build 系统
  2. android avd配置路径
  3. Mac配置Android环境变量
  4. Android(安卓)JNI实战
  5. Android获得系统(system)权限
  6. Android(安卓)NDK概述
  7. 在ubuntu上编译android的mupdf
  8. android JNI 调用
  9. Android之NDK开发

随机推荐

  1. Android中Intent传递对象的两种方法(Seri
  2. 【Android】小白进阶之接口和抽象类的使
  3. 获取Android开机启动项列表
  4. Retrofit使用总结
  5. Android中文API(99)—— RelativeLayout
  6. Android中IntentService的使用及其源码解
  7. Android采用Junit进行应用单元测试
  8. Android Debug keystore系统位置
  9. Android中的四种动画效果
  10. monoTouch for android visual studio c#