Android编译系统学习总结

Android编译系统

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<stdio.h>
#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.o

main.o:main.c                                           //规则二: 它依赖main.c文件
    gcc -o main.c

base.o:base.c                                            //规则三: 它依赖 base.c文件
    gcc -c base.c

部分makefile语法总结
1. 符号%表示一到多个文件
2. :=是makefile中的一种赋值语法,它相比于普通的赋值语法=,会禁止前面变量使用后面的变量。
如:

Y:=$(X)bar       //此时 X为空,Y为bar
X:=foo         //此时 X为foo
Y:=$(X)bar       //此时Y为foobar
X:=later         //此时 X为later
  1. #定义空格:
nullString:=
space:=$(nullString) //eng of line  nullString表示一个空变量,space的值是一个空格
  1. ?=是表示变量是否被定义过,如果没有定义过则该变量就为后面的值
Foo?=bar //如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做
//其等价于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif
  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/hchen
pwd  //cd没有作用,pwd会打印出当前的Makefile目录

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

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

make –f hchen.mk
  1. call函数 ,用来创建新的参数化的函数,其语法是$(call <expression>,<parm1>,<parm2>,<parm3>...),如:
reverse := $(1) $(2)
foo := $(call reverse,a,b)  //foo:=a b
reverse := $(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: droid
DEFAULT_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 <module_name>.<suffix>
  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_only
apps_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 droidcore
droid: droidcore dist_files

endif # 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: droidcore
droidcore: 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内核设计思想》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值