工欲善其事,必先利其器。 想要深入学习Android源码,首先必须掌握Android编译命令。
一、简介
关于Android Build系统,这个话题准备了很久,但是很久没有写出来,所以决定分享给大家。 先看下面的指令,相信编译过Android源码的人都比较熟悉。
source setenv.sh
lunch
make -j12
记得刚接触Android的时候,同事告诉我,用前面的命令就可以编译Android源码。 虽然命令很短,但是我记不住或者忘记了命令。 每次编译的时候都需要看一下我的云笔记。 我总是记不住指示。 后来我决定仔细研究一下这个命令的含义。 知道它是什么,就需要知道它为什么,这样才能更深层次地理解和记忆,这样才能与自己的知识体系有一个完美而牢固的联系,也许会有意想不到的收获。 果然,下面给大家分享一下在学习Android Build(编译)系统的过程中,上述指令的含义。
2. 编译命令
编译环境准备好后,编译Android源码的第一步是source build/envsetup.sh,其中source命令用于运行shell脚本命令,其作用相当于“.”,所以这个命令也相当于 .build/envsetup.sh。 在文件 envsetup.sh 中,声明了当前会话终端可用的命令。 这里需要注意的是当前会话终端,这意味着每次打开新终端时都必须再次执行该命令。 一开始我不明白为什么新打开的终端不能直接执行make命令,到这里终于明白了。
接下来解释一下本文开头引用的命令:
source setenv.sh //初始化编译环境,包括后面的lunch和make指令
lunch //指定此次编译的目标设备以及编译类型
make -j12 //开始编译,默认为编译整个系统,其中-j12代表的是编译的job数量为12。
所有编译命令都可以在envsetup.sh文件中找到对应的函数,比如上面提到的命令lunch和make,必须在该文件中找到
function lunch(){
...
}
function make(){
...
}
源envsetup.sh,需要cd到setenv.sh文件所在路径执行,该路径可能是build/envsetup.sh,也可能是integrate/envsetup.sh,也不排除有些厂家会各自封装了.sh脚本,但核心思想是一致的。
具体实现这里不再赘述,下面总结一下各指令的用法和作用。
2.1 代码编译及编译指令解释
在源代码树的根目录下编译
毫米
编译当前路径下的所有模块,但不包含依赖项
嗯[模块路径]
编译指定路径下的所有模块,但不包含依赖
MMA
编译当前路径下的所有模块,包括依赖
mmma [模块路径]
编译指定路径下的所有模块,并包含依赖
制作[模块名称]
不带参数,表示编译整个Android代码
部分模块的编译说明如下:
模块 make 命令 mmm 命令
在里面
进行初始化
mmm 系统/核心/init
受精卵
制作应用程序进程
mmm 框架/base/cmds/app_process
系统服务器
提供服务
mmm 框架/基础/服务
java框架
制作框架
mmm 框架/基础
框架资源
制作框架资源
mmm 框架/基础/核心/res
jni框架
制作 libandroid_runtime
mmm 框架/基础/核心/jni
活页夹
制作libbinder
mmm 框架/native/libs/binder
上述mmm命令也适用于mm/mma/mmma。 编译系统采用增量编译,只有发生变化的目标文件才会被编译。 当需要重新编译所有相关模块时,需要在编译命令后减少参数-B,如make -B [module_name],或者mm -B [module_path]。
尖端:
2.2 代码搜索搜索命令解释
正则表达式
对所有 C/C++ 文件执行搜索操作
jgrep
对所有 Java 文件执行搜索操作
格列普
所有 Gradle 文件都执行搜索操作
mangrep [关键字]
所有AndroidManifest.xml文件都执行搜索操作
mgrep [关键字]
所有Android.mk文件都执行搜索操作
sepgrep [关键字]
对所有 sepolicy 文件执行搜索操作
resgrep [关键字]
对所有本地 res/*.xml 文件执行搜索操作
sgrep [关键字]
对所有资源文件执行搜索操作
上述指令用法的最终实现方法是基于grep指令,各指令的用法格式如下:
xgrep [keyword] //x代表的是上表的搜索指令
例如,要搜索所有AndroidManifest.xml文件中launcher关键字所在文件的具体位置,指令
mangrep launcher
再比如,在所有Java代码中搜索包含zygote的文件
jgrep zygote
再比如,搜索所有system_app的selinux权限信息
sepgrep system_app
Tips:Android的源代码非常庞大。 使用grep直接搜索代码不仅笨拙且耗时,而且会得到很多无意义且令人困惑的结果。 根据具体需要选择合适的代码搜索命令amdroid源码部分编译,可以节省代码搜索时间,提高搜索结果的准确性,方便定位目标代码。
2.3 导航指令 导航指令说明
克鲁特
切换到Android根目录
项目组
切换到项目根目录
godir [文件名]
跳转到包含文件的目录
Tips:当一个文件每次修改后都需要编译时,执行cproj后会跳转到当前模块的根目录,也就是Android.mk文件所在的目录,然后执行mm命令进行编译目标模块; 当深入源码级别后,需要返回到根目录,使用croot命令来完成; 另外,cd - 命令可用于快速切换到下一个目录。
2.4 信息查询查询命令解释
唔
查询所有命令帮助信息
查找makefile
查询当前目录所在项目的Android.mk文件路径
打印午餐菜单
查询午餐可用产品
打印配置
查询各种编译变量的值
盖顶
查询Android源码根目录
获取目标架构
获取TARGET_ARCH值
2.5 其他说明
上面只列出了比较常用的指令,还有其他指令,不同的build编译系统支持的指令可能会有一些差异。 当你忘记这种编译指令时,可以通过执行hmm来查询该指令的帮助信息。
最后列出两个比较常用的命令:
3、编译系统
Android编译系统是Android源码的一部分,用于编译Android系统、Android SDK以及相关文档。 编译系统由Make文件、Shell和Python脚本组成,其中最重要的是Make文件。 关于构建系统,请参阅了解Android构建系统。
3.1 Makefile分类
整个Build系统的Make文件分为三类:
3.2 编译产品
make编译出来的产品都位于/out目录下。 在该目录中,主要关注以下目录:
在/out/target/product/[product_name]目录下,有几个重量级的镜像文件:
当然还有boot.img、reocovery.img等镜像文件,这里就不介绍了。
3.3 Android.mk分析
源码树中每个模块的所有文件一般都有自己的文件夹,在模块的根目录下有一个名为“Android.mk”的文件。 编译系统以模块为单位进行编译。 每个模块都有一个唯一的模块名称。 一个模块可以依赖于多个其他模块。 模块之间的依赖关系通过模块名称来引用。 也就是说,当模块需要依赖jar包或apk时,必须先将jar包或apk定义为模块,然后再依赖相应的模块。
对于Android.mk文件,通常是以下两行
LOCAL_PATH := $(call my-dir) //设置当编译路径为当前文件夹所在路径
include $(CLEAR_VARS) //清空编译环境的变量(由其他模块设置过的变量)
为了模块编译的方便amdroid源码部分编译,编译系统设置了很多编译环境变量,如下:
对于这个环境变量,编译系统还定义了一些方便的函数,如下:
例子:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 获取所有子目录中的Java文件
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# 当前模块依赖的动态Java库名称
LOCAL_JAVA_LIBRARIES := com.gityuan.lib
# 当前模块的名称
LOCAL_MODULE := demo
# 将当前模块编译成一个静态的Java库
include $(BUILD_STATIC_JAVA_LIBRARY)
微信公众号Gityuan | 微博 weibo.com/gityuan | 博客留言区供交流
发表评论