Android编译脚本,source、lunch以及make在编译时都做了什么

   日期:2024-12-27    作者:hbbaize13 移动:http://ljhr2012.riyuangf.com/mobile/quote/57470.html

1. Make

Make是一款自动化编译工具,在执行编译任务时会解析在Makefile文件中定义的编译规则,然后依据编译规则对源文件进行编译。

1.1 为什么要使用它

在编译小项目时,我们可以使用手动的编译每一个源文件,但是在编译大项目时(比如Android,如果再使用gcc手动编译,那么工作量是不能想象的。这时就需要使用自动化编译工具。使用make还有一个好处,避免不必要的重新编译,当修改了部分源文件时,它可以监测到哪些文件被修改了,然后只重新编译被修改的源文件。

1.2 编译规则

Makefile中定义的编译规则包括有源文件、编译的目标、模块之间的依赖关系以及编译条件等。

make的基础规则非常简单,但是经过扩展修饰后,就可以编译各种庞大的工程。基础编译规则如下

 
  • TARGET:编译目标,通常是最后需要生成的文件名或者是中间文件名,比如编译C时的和。
  • PREREQUISITES:先决条件,也就是编译的目标文件所依赖的文件。
  • COMMAND:编译目标时需要执行的命令。

注意:COMMOD前必须有一个制表符[TAB]

1.3 示例

main.c

 

utils.h

 

utils.c

 

Makefile

 

编写完源码以及Makefile后,执行make命令,它会解析Makefile文件。首先make会读取到第一条规则,查看目标是否有依赖,如果有依赖就先生成它的依赖目标,然后在生成自身。当然,如果对应的依赖也有自己的依赖,那么会先去生成依赖的依赖,这就形成了一个树形结构。最后,完成整个项目的编译。

在执行make 命令时,也可以指定编译目标,比如, 这样就只会执行utils.o所在规则对应的命令,将utils.c编译成utils.o。

注意到它虽然也是目标但是没有为它生成目标文件,这种叫做伪目标,在执行 make 时可以指定它来执行对应的的命令。

2. Android源码编译流程

对Make做了简单了解后,现在来说一下Android源码的编译流程。

Android源码的编译步骤分为3部,每一步都做了什么

 
2.1 执行envsetup.sh

envsetup.sh中定义了很多变量和函数,执行envsetup.sh之后,就可以在当前终端中使用这些函数和变量。

  • hmm:列出函数及其介绍
  • lunch: 选择要编译的目标产品和版本
  • croot: 切换到源码根目录
  • m: 整编源码
  • mm: 编译当前目录下的所有模块(不会编译它们的依赖)
  • mmm: 编译指定目录下所有的模块(不会编译它们的依赖)
  • mma: 编译当前目录下所有的模块以及它们所依赖的模块
  • mmma: 编译指定目录下所有的模块以及它们所依赖的模块
  • provision: 将设备所有需要的分区刷入,选项将传递给fastboot
  • cgrep: 在C/C++文件中搜索指定关键字
  • ggrep: 在gradle文件中搜索指定关键字
  • Jgrep: 在java文件中搜索指定关键字
  • resgrep: 在资源xml文件中搜索指定关键字
  • mangrep: 在AndroidManifest.xml文件中搜索指定关键字
  • mgrep:在Makefiles和android.mk文件中搜索指定关键字
  • sepgrep: 在sepolicy文件中搜索指定关键字
  • sgrep: 在所有本地文件中搜索指定关键字
  • godir: 切换到包含某个文件的目录下
  • cproj: 向上切换到最近包含Android.mk的目录下
  • findmakefile: 打印当前目录所在工程的Android.mk的文件路径
  • getsdcardpath: 获取Sd卡路径
  • getscreenshotpath: 获取屏幕截图的路径
  • getlastscreenshot: 获取最后一张截图,导出到当前目录下
  • getbugreports: 将bug报告从设备上导出到本地,bug报告存放于目录/sdcard/bugreports
  • gettop: 获取Android源码根目录
  • pid: pid processname 查看某个可执行程序对应的进程id
  • key_back: 模拟按返回键
  • key_home: 模拟按Home键envsetup.sh
  • key_menu: 模拟按菜单键
2.2 lunch
 

在print_lunch_menu函数中首先会获取COMMON_LUNCH_CHOICES变量,然后输出所有product

 

COMMON_LUNCH_CHOICES定义在AndroidProducts.mk中

device/qcom/qssi/AndroidProducts.mk

 
2.3 make

执行make命令后,会解析在源码根目录中的Makefile,可以看到只是包含了main.mk

 

在main.mk中包含了和。

 

config.mk中定义了一系列编译需要用到的变量,比如常用的CLEAR_VARS、BUILD_PACKAGE。这些变量记录着对应脚本的路径。每一个.mk完成一个基本功能,比如,CLEAR_VARS对应的build/make/core/clear_vars.mk是清除编译的临时变量,BUILD_PACKAGE对应的build/make/core/package.mk是编译APK。

 

而definitions.mk中用define也定义了一系列变量,比如常用的my-dir、all-subdir-makefiles、all-subdir-java-files等,这些编译主要用于处理目录相关的功能,比如my-dir是获取当前目录路径,all-subdir-makefiles调用所有子目录的android.mk。

 

在上面提到make在解析Makefile时,会形成一个树型结构。那么对于Android来说,这个树型结构的根是谁呢?在中可以看到,定义的默认target为,而又依赖。这里定义的只是一个空的规则,就相当于先占一个位置,后面有真正的定义。

build/make/core/main.mk

 

根据TARGET_BUILD_APPS值的不同,这里分成了单编和整编。

 

对于编译整个系统来说,droid_targets依赖droidcore和dist_files。

先看一下droid_core。通过droidcore可以生成分区镜像以及各种程序包,modules_to_install这个变量描述了系统需要安装的模块,比如framework、Settings。

 

dist_files的作用是在out目录下创建dist文件夹,用于存储多种分发包。

3. Android.mk

上面提到在执行make时,会定义很多变量以及函数,我们在Android.mk中就可以使用这些函数和变量。

Android.mk用于在构建模块时,向构建系统描述源文件以及使用到的共享库。构建的模块可以是静态库、共享库、可执行文件、jar或apk。

3.1 示例
3.1.1 编译动态jar

HelloWorld.java

 

Andoird.mk

 

执行进行编译

3.1.2 编译APK

APK源码放在附件MaterialMe.tar.gz中

Android.mk

 
 
3.2 常用函数和变量

常用获取源文件的方法

 

编译目标类型

 

变量及其含义

 

Android.mk其实还是Makefile文件,那么Android.mk自然也支持定义变量以及使用条件语句

 
3.3 静态库动态库的区别及使用场景

动态库编译后会放在;而静态库编译后会放在

引入了静态jar的模块实际上是把jar包里用到class都包含到引用的模块里了,会增大APK占用的空间;而共享jar在系统中仅有一份,使用到时,由系统加载提供。

什么时候使用动态jar,什么时候又使用静态jar呢

一般打包系统库的时候,都会打包成动态jar,比如services.jar,framework.jar等。

在引用第三方库时,会打包成静态jar。

4. Android.bp

4.1 构建系统简介

Ninja

Ninja 是一个专注于速度的小型构建系统,它被设计为尽可能快地运行构建。Ninja与其他构建系统的区别在于,它的输入文件被设计为由更高级的构建系统生成;其他构建系统基于高级语言,而Ninja基于汇编。

ninja的构建文件虽然是可读的(human-readable),但是编写不是特别方便,因此就需要一些ninja构建文件的生成器。这些生成器就是一些元构建系统(meta-build system),例如Blueprint、CMake等等。

Blueprint

Blueprint 是一个元构建系统,该系统读取Blueprint文件来描述需要构建的模块,并生成一个Ninja清单来描述需要运行的命令及其依赖项。 Blueprint是ninja构建文件的生成器。android编译系统soong集成了Blueprint,Blueprint可将我们编写的android.bp解析生成一个ninja构建文件。

Kati

kati就是一个转换工具,它可以将Makefile和.mk文件转换为ninja。

Soong

在android 6.0版本之前,编译android源码采用的是基于make的编译系统,由于make在编译时表现出效率不够高、增量编译速度慢等问题,Google在android 7.0版本引进了编译速度更快的soong来替代make。

Soong集成了Ninja, 而Ninja专注于速度,没有条件或流程控制语句,也不支持逻辑运算。但它允许以其它语言如来维护这些复杂的编译流程和逻辑。例如,我们可以继续采用makefile, 或者采用go语言来维护编译流程和逻辑。上面已经提到了Ninja,Blueprint, kati等等好几种工具,为了完整、快速的构建一个android系统,就需要一个“管家”来协调这些工具。这个选择转换工具、选择解析框架、解析维护构建逻辑的“管家”就是soong。

androidmk

soong中还集成了一个非常有用的工具androidmk。androidmk可以将android.mk转换成android.bp。注意:androidmk工具可以转换变量,模块,注释和某些条件,但是自定义的Makefile规则,复杂的条件语句或其它的额外的include语句,必须手动转换。

4.2 services的Android.bp

Android.bp的格式

 

开头为模块类型,比如的意思就是将次模块编译成动态jar。后面跟着的是一个类似Json的结构,它定义着模块的属性。每个模块都必须有一个 name属性,也就是模块的名称。

services模块

 

可以看到属性的值并不是文件路径,它是引用的一个filegroup

 

另外在Android.bp中还可以定义默认模块,默认模块可用于在多个模块中重复使用相同的属性。定义方法是将模块类型写为xxx_defaults,比如。引用时,可以给其他模块添加defaults属性,比如。默认模块还有cc_defaults, java_defaults, doc_defaults, stub_defaults等。

 

services/wifi的Android.bp中就使用了这个默认模块

 
4.3 模块类型

在build/soong/androidmk/cmd/androidmk/android.go可以看到Android.bp支持的模块类型以及预编译类型

 
 
 
4.4 常用属性
 
4.5 与Android.mk的差异
  • Android.bp支持单行和多行注释
  • Android.bp也可以定义变量,通过赋值,变量定义后,它的值是不可变的,除了使用改变值,但也仅限在使用之间。
  • Android.mk文件通常可以包含多个同名模块(例如,用于库的静态(static)和共享(shared)版本,用于不同主机(host)的版本,用于不同设备(device)版本;Android.bp中的每一个模块都需要唯一的,但是单个模块可以构建为多个变体。

将不同变体添加到中,这样为arm平台构建时,将构建generic.cpp和arm.cpp。 在为x86平台构建时,将构建generic.cpp和x86.cpp。

 
4.6 示例

MaterialMe的Android.bp


 

特别提示:本信息由相关用户自行提供,真实性未证实,仅供参考。请谨慎采用,风险自负。


举报收藏 0评论 0
0相关评论
相关最新动态
推荐最新动态
点击排行
{
网站首页  |  关于我们  |  联系方式  |  使用协议  |  隐私政策  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报  |  鄂ICP备2020018471号