最近研究了下Android 的编译系统,下面结合编译我们自己的产品 mobot 来对整个编译系统进行必要的介绍,方便大家今 后对默认编译的修改。
先列出几个觉得重要的Make 文件:
build/buildspec.mk
build/envsetup.sh
build/core/main.mk
build/core/envsetup.mk
build/core/config.mk
总的来说,Android 以模块 (module/package) 的形式来组织各个系统的部件,每个模块(module/package) 的目录下都会有一个 Android.mk 。所谓 module 就是指系统的 Native Code ,而相对于 Java 写的 Android application 称为 package 。
Makefile的主要流 程 1
初始化参数设置 2
读取Product 的设定 3
读取BoardConfig 4
读取所有Module 4
产生相应的Rules ,生成 image 5
具体make 操作: 6
完整编译 6
模块编译 6
单独编译image 文件 7
一些疑问和解答: 7
Makefile的主要流程
以下主要流程都 在build/core/main.mk 里 安排。
l 初始化相关的参数设置(buildspec.mk 、 envsetup.mk 、 config.mk)
l 检测编译环境和目标环境
l 决定目标product
l 读取product 的配置信息及目标平台信息
l 清除输出目录
l 检查版本号
l 读取Board 的配置
l 读取所有Module 的配置
l 根据配置产生必要的规则(build/core/Makefile)
l 生成image
初始化参数设置
在 main.mk 里,简单设置 几个主要编译路径的变量后,来到config.mk :
——————————————config.mk ——————————————
其中设置了源文件的一系列路径,包括头文件、库文件、服务、API 已经编译工具的路径。(前 36 行)
从40 行开始,定义一些编译模块的生成规则:
这里面除了第一个CLEAR_VARS 外,其他都对应的一种模块的生成规则,每一个 module 都会来 include 其中某个来生成目标模块。
例如:
Camera模块的 makefile 里( Android.mk )就包含了其中的一种生成规则BUILD_PACKAGE :
也就是Camera 会按照 package.mk 里的生成规则去生成目标模块。
回到config.mk ,接着会尝试读取 buildspec.mk 的设置:
如同注释所说,会尝试查找 buildspec.mk ,如果文件不 存在会自动使用环境变量的设置,如果仍然未定义,会按arm 默认的设置去 build 。
这里的buildspec.mk 可以自己创建,也可以将原先 build/ 下的 buildspec.mk.default 直接命名为buildspec.mk 并移到根目录。
实际上,buildspec.mk 配置都被屏蔽了,我们可以根据需要直接打开和修改一些变量。在这里我们可以加入自己的目标产品信息:
ifndef TARGET_PRODUCT
TARGET_PRODUCT:=mobot
endif
以及输出目录设置:
OUT_DIR:=$(TOPDIR)mobot
读取Product 的设定
回到config.mk ,接着进行全局变量设置,进入 envsetup.mk :
——————————————envsetup.mk ——————————————
里面的大部分函数都在build/envsetup.sh 中定义。
首先,设置版本信息,(15 行 ) 在 build/core/version_defaults.mk 中具体定义平台版本、 SDK版本、 Product 版本,我们可以将 BUILD_NUMBER 作为我们产品 mobot 的 version 信息,当然,也可以自定义一个版本变量。
回到envsetup.mk ,接着设置默认目标产品 (generic) ,这里由于我们在 buildspec.mk 里设置过TARGET_PRODUCT ,事实上这个 变量值为mobot 。
然后读取product 的设置 (135 行 ) , 具体实现在 build/core/ product_config.mk 中,进而进入product.mk ,从 build/target/product/ AndroidProducts.mk 中读出PRODUCT_MAKEFILES ,这些 makefile 各自独立定义 product ,而我们的产品 mobot 也应添加一个 makefile 文件 mobot.mk。在 mobot.mk 中我们可以加入所需编译的 PRODUCT_PACKAGES 。
下面为HOST 配置信息及输出目录,最后打印相关信息:
读取BoardConfig
接着回到config.mk , (114 行 ) 这 里会搜索所有的 BoardConfig.mk ,主要有以下两个地方:
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk
vendor/*/$(TARGET_DEVICE)/BoardConfig.mk
这里的TARGET_DEVICE 就是 mobot ,就是说为了定义我们自己的产品 mobot ,我们要在build/target/board 下添加一个自己的 目录 mobot 用来加载自己 的 board 配置。
在BoardConfig.mk 中会决定是否编译 bootloader 、 kernel 等信息。
读取所有Module
结束全局变量配置后,回到main.mk ,马上对编译工具及版本进行检查,错误便中断编译。
142行,包含文件 definitions.mk ,这里面定义了许多变量和函数供main.mk 使用。 main.mk第 446 行,这里会去读取所有的 Android.mk 文件:
include $(ONE_SHOT_MAKEFILE)
这个 ONE_SHOT_MAKEFILE 是在前面提到 的mm(envsetup.mk) 函数中赋 值的:
ONE_SHOT_MAKEFILE=$M make -C $T files $@
而M=$(findmakefile), 最终实现在:
回到main.mk ,最终将遍历查找到的所有子目录下的 Android.mk 的路径保存到subdir_makefiles 变量里 (main.mk 里的 470 行 ) :
我们在package/apps 下每个模块根目录都能看到 Android.mk ,里面会去定义当前本地模块的Tag : LOCAL_MODULE_TAGS ,Android 会通过这个 Tag 来决定哪些本地模块会编译进系统,通过PRODUCT 和 LOCAL_MODULE_TAGS 来决定哪些应用包会编译进系统。 ( 前面说 过,你也能通过buildspec.mk 来 制定你要编译进系统的模块 )
这个过 程在mian.mk 的 445 行开始,最后需要编译的模块路径打包到ALL_DEFAULT_INSTALLED_MODULES (602行 ) :
产生相应的Rules ,生成 image
所有需要配置的准备工作都已完成,下面该决定如何生成image 输出文件了,这一过程实际上在build/core/Makefile 中处理的。
这里定义各种img 的生成方式,包括 ramdisk.img 、 userdata.img 、 system.img 、 update.zip、 recover.img 等。具体对应的 rules 可以参考下图:
http://p.blog.csdn.net/images/p_blog_csdn_net/yili_xie/EntryImages/20091214/make%20goals.png
当Make include 所有的文件,完成对所有 make 文件的解析以后就会寻找生成 对应目标 的规则,依次生成它的依赖,直到 所有满足的模块被编译好,然后使用相应的工具打包成相应的img 。
具体make 操作:
完整编译
我们在根目录下输入make 命令即可开始完全编译。这个命令实际编译生成的默认目标是 droid:
也就是说,大家敲入make 实际上执行的 make droid 。而接下来大家看看 main.mk 文件里最后面的部分,会有很多伪目标,如 sdk 、 clean 、 clobber 等,这些在默认的 make droid 的命令下是不会执行的。我们可以在 make 后加上这些标签来单独实现一些操作。如:输入 make sdk 将会生成该版本对应的 SDK ,输入 make clean 会清除上次编译的输出。
模块编译
有时候我们只修改了某一个模块,希望能单独编译这个模块而不是重新完整编译一次,这时候我们要用到 build/envsetup.sh中提供的几个 bash 的帮助函数。
在 源代码根目录下执行:
. build/envsetup.sh(.后面有空格 )
这 样大家相当于多了几个可用的命令。
这 时可以用help 命令查看帮助信息:
其中对模块编译有帮助的是tapas 、 m 、 mm 、 mmm 这几个命令。
1、 tapas——以交互方式设置 build 环境变量。
输入:tapas
第一步,选择目标设备:
第二步,选择代码格式
第三步,选择产品平台:
注意:这里,Google 源代码里默认是 generic ,而我们针对自己的产品应修改成 mobot
具体在build/envsetup.sh 里的函数 chooseproduct() 中对相应代码进行修 改。
2、 m、 mm 、 mmm 使用独立模块的 make 命令。
几个命 令的功能使用help 命令查看。
举个例 子,我们修改了Camera 模块的代码,现 在需要重新单独编译这一块,这时可以使用mmm 命令,后面跟指定模块的路径 ( 注意是模块的根目录 ) 。
具体如 下:
mmm packages/apps/Camera/
为了可 以直接测试改动,编译好后需要重新生成system.img
可以执 行:make snod
单独编译image 文件
一般我 们完整编译后,会生成三个重要的image 文 件: ramdisk.img 、 system.img 和userdata.img 。当然我们可以分开单独去编译 这三个目标:
make ramdisk —— ramdisk.img
make userdataimage —— userdata.img
make systemimage —— system.img
一些疑问和解答:
1) 什么是recovery.img ?
顾名思义,recovery.img 是为了恢复系统的,相对于普通的 boot.img , recovery.img 多了一些图片文件 ( 恢复时界面的背景 ) 、 /sbin/recovery/ 目录 ( 跟 恢复有关的二进制文件 ) ,一 些初始化文件也不相同 (init.rc 、 init.goldfish.rc 、 default.prop)
这就是为什么启 动恢复模式时会进入类似文本界面而不是图形界面。
将recovery.img 文件复制到 SD 卡中,进入 shell 下输入:
mount -a
flash_image recovery /sdcard/recovery.img
若提示“no space on device ”,可用 fastboot 模式刷
fastboot erase recovery
fastboot flash recovery recovery.img
在关机状态下按home+power 键进入 recovery 模式,根据选项选择需要的操作。
2) make sdk和 make droid 编译有什么不同?
make sdk:
其实,执行make sdk ,编译后会在目录 out/host/linux-x86 里生成 sdk 目录,这个 sdk 和官方下载的 sdk 包是一样的,可以直接使用。