概述
Android编译概述
下载好Android源代码后,通过在Android源代码工程下执行make snod命令,然后得到Android的系统镜像system.img。
那么当我们修改了android源代码中某个模块或者android源代码工程中新增了一个自己的模块。此时可以用make命令进行重新编译,不过重新编译比较浪费时间。google提供了另外的命令来进行单独模块的编译,以及重新打包到system.img镜像中的命令。
Android源码编译系统
android的编译系统可以分为三部分:
- build/core:在这个目录中包含了大量的.mk文件
- 子项目:每个子项目都包含自己的Android.mk或Android.bp,在编译时会被包含进去,而如何编译子项目由Andorid.mk或Android.bp文件决定;
- out/:编译结果输出到该目录下,编译的结果可以是jar包、APK、二进制文件等。
在Android系统主要就是根据.mk文件进行编译的。
他们之间的关系如下:

Android编译命令
(1) 设置命令环境
命令:source build/envsetup.sh
用于导入命令的环境,在envsetup.sh文件中定义了编译需要用到的命令,如m、mm、mmm、mma、lunch、croot等。如果不导入则无法使用这些命令。
(2) 指定目标设备和编译类型
命令格式:lunch <product_name>-<build_variant>
如:lunch aosp_arm64-eng
(3) 编译指定模块
如:make framework -j8
Android模块名称
部分模块的编译:
| 模块 | make命令 | 源码路径 |
|---|---|---|
| all | make | / |
| init | make init | system/core/init |
| zygote | make app_process | frameworks/base/cmds/app_process |
| system_server | make services | frameworks/base/services |
| framework | make framework | frameworks/base |
| framework-res | make framework-res | frameworks/base/core/res |
| framework-jni | make libandroid_runtime | frameworks/base/core/jni |
| binder | make libbinder | frameworks/native/libs/binder |
mmm系列命令
make、m、mm、mmm命令的区别
命令m是对make命令的简单封装,并且是用来对整个Android源代码进行编译,而命令mm和mmm都是通过make命令来对Android源码中的指定模块进行编译。
m、mm、mmm都定义在Android源码的build/envsetup.sh下。
- m: Makes from the top of the tree.
- mm: Builds and installs all of the modules in the current directory, and their
dependencies.
- mmm: Builds and installs all of the modules in the supplied directories, and their
dependencies.
To limit the modules being built use the syntax: mmm dir/:target1,target2.
- mma: Same as 'mm'
- mmma: Same as 'mmm'
(1) make命令
不带任何参数时,用于编译整个系统,编译时间比较长,除非是进行初次编译否则不建议使用此命令。
带参数时如make framework,是对单个模块的编译。它的优点是:会把该模块依赖的其他模块一起跟着编译。例如:make libmedia 就会把libmedia依赖库全部编译好。当然缺点也会很明显,那就是它会搜索整个源码来定位MediaProvider 模块所使用的Android.mk文件。并且还要判断该模块依赖的其他模块是否有修改。所以编译时间比较长。
(2) m命令
m是对make指令的简单封装,通常用于源码的第一次编译,时间较长。该命令很少使用,都是直接使用make指令。
使用m系列命令需要在android源码根目录执行source build/envsetup.sh命令设置环境,而make命令则不需要。
函数m的实现如下:
function m()
{
T=$(gettop)
if [ "$T" ]; then
make -C $T $@
else
echo "Couldn't locate the top of the tree. Try setting TOP."
fi
}
函数m调用函数gettop得到的是Android源代码根目录T。在执行make命令的时候,先通过-C参数指定工作目录为T,即Android源代码根目录,接着又将执行命令m指定的参数$@作为命令make的参数。
从这里就可以看出,命令m实际上就是对命令make的简单封装。
(3) mm命令
mm命令编译当前目录下的模块,当前目录下要有Android.mk文件。一般需要cd 进入指定模块的目录,然后执行mm指令。
使用方法:
cd 目标模块路径
mm
(4) mmm命令
编译指定路径下的模块,指定路径下要有Android.mk文件。mm和mmm一样,只编译目标模块。mm和mmm编译的速度都很快。
使用方法:mmm 相对路径
mma与mmma
mma 编译当前路径下所有模块,且包含依赖
mmma [module_path] 编译指定路径下所有模块,且包含依赖
m系列命令使用方法与注意事项
mm与mmm命令注意事项:
- 只能在第一次编译之后使用;
- 只对目标模块进行编译,不对依赖模块编译;
- 目标模块文件夹中需要包含android.mk文件。
使用上述三个命令之前,需要在android源码根目录执行source build/envsetup.sh命令设置环境,m和make的区别便在此处。
如果只知道目标模块的名称而不知道具体路径,则建议使用“make 模块名” 的方式编译目标模块。而且初次编译时也要采用这种方法。
如果不知道目标模块的名称,但知道目标模块所在的目录时,则可使用mm或者mmm 命令来编译。当然初次编译还必须使用make命令,以后编译就可以使用mmm或者mm了,这样会帮助我们节约不少时间。
一般的编译方式都会采用增量编译,即只编译发生变化的目标文件,但有时则需要重新编译所有目标文件,那么就可以使用make 命令行的-B选项。例如:mm -B 模块名,或者mm -B、mmm -B。在mm 和 mmm内部也是调用make命令的,而make的-B选项将强制编译所有的目标文件。
Android.mk
Android.mk和Android.bp区别
Android.mk是在Android使用的一种makefile文件,属于GUN makefile的一部分,Android.mk用一定的语法规范来告诉编译器需要编译哪些文件、需要引用的库,然后Android编译系统会根据Android.mk中的描述生成指定的文件。
从Android O开始,Android开始使用Android.bp代替Android.mk来管理代码的编译,Android通过soong和blueprint将Android.bp转换为ninja文件(保存在out/soong/build.ninja文件),通过ckati将Android.mk转换为ninja文件(保存在out/buil-$(target).ninja文件)。然后将这两个ninja文件include到一个文件,最后使用ninja来编译。
Android.mk文件编写
示例:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS :=optional
LOCAL_SRC_FILES :=xxx.cpp
LOCAL_SHARED_LIBRARIES:= libname
LOCAL_MODULE := targetname
include $(BUILD_EXECUTABLE)
(1) LOCAL_PATH
每个Android.mk文件必须以LOCAL_PATH开始,表示指定项目的根路径,用于在开发tree中查找源文件。
my-dir是一个宏,由Build System提供,call my-dir返回当前Android.mk文件的路径。
(2) LOCAL_MODULE_TAGS
该变量用于指定编译类型,如:
- optional,表示该模块可以在所有版本下进行编译;
- user,表示该模块只在user版本下进行编译;
- 其它的还有eng(engineering)表示工程版本,tests表示测试版本。
(3) CLEAR_VARS
CLEAR_VARS变量由Build System提供,并指向一个指定的GNU Makefile,由它清理各种LOCAL_xxx变量(因为所有的编译控制文件由同一个GNU make解析和执行,这些变量都是全局的,所以清理后才能避免相互影响),比如LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等,但是不清理LOCAL_PATH。
(4) LOCAL_SRC_FILES
后面接需要编译的源码文件,多个文件用空格分隔,换行使用“\”。根目录为LOCAL_PATH。
(5) LOCAL_SHARED_LIBRARIES
表示所需的动态库。其它类型的引用文件变量为:
- LOCAL_STATIC_LIBRARIES := 所需要的静态库
- LOCAL_SHARED_LIBRARIES := 所需的动态库
- LOCAL_C_INCLUDES := 头文件所在的路径
(5) LOCAL_MODULE
表示此Android.mk生成的目标模块名,名字在Android所有模块中必须唯一且不包含空格,Build System会自动添加适当的前缀和后缀,比如,自己要产生动态库且命名为foo,则生成后名字会变为libfoo.so,但如果自己定义名字为libfoo,则不加前缀,为libfoo.so。
(6) #include $(target_type)
表示编译的目标类型。
# 静态库,后缀为.a
include $(BUILD_STATIC_LIBRARY)
# 动态库,后缀为.so
include $(BUILD_SHARED_LIBRARY)
# java可执行文件,后缀为.jar
include $(BUILD_JAVA_LIBRARY)
# C/C++可执行文件,无后缀
include $(BUILD_EXECUTABLE)