工欲善其事,必先利其器,对于想要深入学习 Android 源码,必须先掌握 Android 编译命令.

一、引言  

关于 Android Build 系统,这个话题很早就打算整理下,迟迟没有下笔,决定跟大家分享下。先看下面几条指令,相信编译过 Android 源码的人都再熟悉不过的。

source /opt/android1204_17.conf 
source setenv.sh
lunch
make -j12
  

记得最初刚接触 Android 时,同事告诉我用上面的指令就可以编译 Android 源码,指令虽短但过几天就记不全或者忘记顺序,每次编译时还需要看看自己的云笔记,冰冷的指令总是难以让我记忆。后来我决定认真研究下这个指令的含义。知其然还需知其所以然,这样能更深层次的理解并记忆,才能与自身的知识体系建立强连接,或许还有意外收获,果然如此,接下来跟大家分享一下在研究上述几条指令含义的过程中,深入了解到的 Android Build(编译) 系统。

二、编译命令  

准备好编译环境后,编译 Android 源码的第一步是 source build/envsetup.sh,其中 source 命令就是用于运行 shell 脚本命令,功能等价于”.”,因此该命令也等价于. build/envsetup.sh。在文件 envsetup.sh 声明了当前会话终端可用的命令,这里需要注意的是当前会话终端,也就意味着每次新打开一个终端都必须再一次执行这些指令。起初并不理解为什么新开的终端不能直接执行 make 指令,到这里总算明白了。  

接下来,解释一下本文开头的引用的命令:  

source /opt/android1204_17.conf  //初始化 jdk 环境变量(这个不是必需的,因厂商而异)
source setenv.sh  //初始化编译环境,包括后面的 lunch 和 make 指令
lunch  //指定此次编译的目标设备以及编译类型
make  -j12 //开始编译,默认为编译整个系统,其中-j12 代表的是编译的 job 数量为 12。

  

所有的编译命令都在 envsetup.sh 文件能找到相对应的 function,比如上述的命令 lunch,make,在文件一定能找到

function lunch(){
...
}

function make(){**
...
}
  

source envsetup.sh,需要 cd 到 setenv.sh 文件所在路径执行,路径可能在 build/envsetup.sh,或者 integrate/envsetup.sh,再或者不排除有些厂商会封装自己的.sh 脚本,但核心思路是一致的。 

具体实现这里就不展开说明,下面精炼地总结了一下各个指令用法和功效。

2.1 代码编译

image  

下面列举部分模块的编译指令:

image  

上述 mmm 命令同样适用于 mm/mma/mmma,编译系统采用的是增量编译,只会编译发生变化的目标文件。当需要重新编译所有的相关模块,则需要编译命令后增加参数-B,比如 make -B [module_name],或者 mm -B [module_path]。  

Tips:

2.2 代码搜索

image  

上述指令用法最终实现方式都是基于 grep 指令,各个指令用法格式:

xgrep [keyword]  //x 代表的是上表的搜索指令   

例如,搜索所有 AndroidManifest.xml 文件中的 launcher 关键字所在文件的具体位置,指令  

mangrep launcher  

再如,搜索所有 Java 代码中包含 zygote 所在文件  

jgrep zygote

又如,搜索所有 system_app 的 selinux 权限信息

sepgrep system_app   

Tips: Android 源码非常庞大,直接采用 grep 来搜索代码,不仅方法笨拙、浪费时间,而且搜索出很多无意义的混淆结果。根据具体需求,来选择合适的代码搜索指令,能节省代码搜索时间,提高搜索结果的精准度,方便定位目标代码。

2.3 导航指令

Tips: 当每次修改完某个文件后需要编译时,执行 cproj 后会跳转到当前模块的根目录,也就是 Android.mk 文件所在目录,然后再执行 mm 指令,即可编译目标模块;当进入源码层级很深后,需要返回到根目录,使用 croot 一条指令完成;另外 cd - 指令可用于快速切换至上次目录。

2.4 信息查询

2.5 其他指令  

上述只是列举比较常用的指令,还有其他指令,而且不同的 build 编译系统,支持的指令可能会存在一些差异,当忘记这些编译指令,可以通过执行 hmm,查询指令的帮助信息。  

最后再列举两个比较常用的指令:

三、编译系统  

Android 编译系统是 Android 源码的一部分,用于编译 Android 系统,Android SDK 以及相关文档。该编译系统是由 Make 文件、Shell 以及 Python 脚本共同组成,其中最为重要的便是 Make 文件。关于编译系统可参考 理解 Android Build 系统。

3.1 Makefile 分类  

整个 Build 系统的 Make 文件分为三大类:

3.2 编译产物 

经过 make 编译后的产物,都位于/out 目录,该目录下主要关注下面几个目录:

3.3 Android.mk 解析  

在源码树中每一个模块的所有文件通常都相应有一个自己的文件夹,在该模块的根目录下有一个名称为 “Android.mk” 的文件。编译系统正是以模块为单位进行编译,每个模块都有唯一的模块名,一个模块可以有依赖多个其他模块,模块间的依赖关系就是通过模块名来引用的。也就是说当模块需要依赖一个 jar 包或者 apk 时,必须先将 jar 包或 apk 定义为一个模块,然后再依赖相应的模块。  

对于 Android.mk 文件,通常都是以下面两行

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)

版权声明:此文章转载自码农网


↙↙↙阅读原文可查看相关链接,并与作者交流