博客
关于我
Android.mk文件分析
阅读量:829 次
发布时间:2019-03-25

本文共 2717 字,大约阅读时间需要 9 分钟。

从对Makefile一无所知开始,折腾了一个多星期,终于对Android.mk有了一个全面些的了解。了解了标准的Makefile 后,发现Android.mk其实是把真正的Makefile 包装起来,做成了一个对使用者来说很简单的东西。使用它来编译程序时,不管是动态库、可执行的二进制文件,还是Jar库、APK包,只要沿着一个简单的思路来做三大步就可以了:清除旧变量,设置新变量,调用编译函数。

明白了以后,发现Makefile语法不是问题,有很多教程和高手。编译模块时如何清除变量、调用编译函数等也不是问题,源码当中无处不在这样的例子。而对初学者来说,更需要明白的可能是,Android如何让使用脚本的人从Makefile语法中解放出来,简单地按照上面的三大步就可以编译出任意模块。

拿AlarmClock来做例子的话,首先需要清除旧变量,然后设置新的变量,最后调用编译函数。具体步骤如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) # 清除旧变量
LOCAL_SRC_FILES := $(call all-subdir-java-files) # 设置新变量
LOCAL_PACKAGE_NAME := AlarmClock # 设置新变量
include $(BUILD_PACKAGE) # 调用编译函数

清除旧变量是因为Android.mk 中所有的变量都是全局的,编译函数在编译时会使用这些变量。为了防止编译函数使用了编译其它模块时设置的变量,每次开始编译一个新的模块时清除所有的变量是个好习惯。

设置新变量是为了将本次编译时要用到的源码地址、包名等设置好,为后续的编译准备工作做好。

调用编译函数其实就是include一个固定的mk文件,这个mk文件会根据设置的变量提取出编译模块需要的target、Command等信息并执行固定的编译命令。

Makefile这个东西,往最简单处说,就是这样的模式:

目标:信赖文件
执行命令

这里暂不考虑它的语法细节,举个不完整的例子,就是这样的:

AlarmClock.apk:AlarmClock.java
javac AlarmClock.java
java AlarmClock.class

要生成AlarmClock.apk,就需要AlarmClock.java这个信赖文件,然后执行后面两行的两个命令。假如我们简单粗暴地用这种写法去编译Android系统的话,会相当累。Android.mk就很巧妙地把它们进行了抽象。我们现在来模拟一下。

定义三个变量target, source, command,分别代表编译目标、信赖文件和编译命令,就成了这样:

$(target):$(source)
$(command)

这三个变量的值分别是:

target := AlarmClock.apk
source := AlarmClock.java
command := javac AlarmClock.java
java AlarmClock.class

这样的话,在编译每个APK时,只要分别给target、source、command赋值,然后再这样写就可以了。

$(target):$(source)
$(command)

嗯,还是有点儿麻烦,那再抽象一次。定义一个变量name,让它存放要编译的模块的名字:

name := AlarmClock

然后再更改一下前面的三个变量:

target := $(name).apk
source := $(name).java
command := javac $(name).java
java $(name).class

现在要编译一个模块Contacts需要做些什么呢?

name := Contacts
$(target):$(source)
$(command)

三行代码就可以了,对吧?但后面两行还是每次都要写,那再抽象。把后面两行放到一个build_apk.mk文件当中。然后定义一个变量:

BUILD_PACKAGES := build_apk.mk

全部完了,这回编译一个模块Contacts时只要这样做就可以了:

name := Contacts
include $(BUILD_PACKAGES)

一个不够复杂,那就多点儿:

name := Contacts
include $(BUILD_PACKAGES)
name := Phone
include $(BUILD_PACKAGES)
name := Email
include $(BUILD_PACKAGES)

//================强壮的分隔线=========

现在我们来完善一下上面的过程,把它系统化。

定义一个专门用来清除变量的clear_vars.mk里面的内容如下:

name :=
target :=
source :=
command :=

再定义一个变量CLEAR_VARS := clear_vars.mk。上面的编译脚本就变成了这样:

include $(CLEAR_VARS)
name := Contacts
include $(BUILD_PACKAGES)
include $(CLEAR_VARS)
name := Phone
include $(BUILD_PACKAGES)
include $(CLEAR_VARS)
name := Email
include $(BUILD_PACKAGES)

这回,清除旧变量、设置新变量、调用编译函数三大步全都有了。

好了,现在我们进入到Android源码的build/core/目录下。先看其中的main.mk, config.mk这两个文件。

main.mk里面是具体的脚本,在这里控制编译模块。

config.mk中定义了下面这样的文件调用:

CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk

编译模块时,每一个include其实都是一次功能调用,或者对mk文件的调用,或者对变量和函数的调用。做为单独的功能模块抽象出来的mk文件都在build/core/目录下,而函数的定义全部在definitions.mk中。

万事开头难,有了上面的思路,再看这些脚本就很简单了。一路顺风,呵。。。

转载地址:http://mkjuk.baihongyu.com/

你可能感兴趣的文章
OpenCV与AI深度学习 | 使用单相机对已知物体进行3D位置估计
查看>>
OpenCV与AI深度学习 | 初学者指南 -- 什么是迁移学习?
查看>>
OpenCV与AI深度学习 | 十分钟掌握Pytorch搭建神经网络的流程
查看>>
OpenCV与AI深度学习 | 基于GAN的零缺陷样本产品表面缺陷检测
查看>>
OpenCV与AI深度学习 | 基于OpenCV和深度学习预测年龄和性别
查看>>
OpenCV与AI深度学习 | 基于OpenCV实现模糊检测 / 自动对焦
查看>>
OpenCV与AI深度学习 | 基于Python和OpenCV将图像转为ASCII艺术效果
查看>>
OpenCV与AI深度学习 | 基于PyTorch实现Faster RCNN目标检测
查看>>
OpenCV与AI深度学习 | 基于PyTorch语义分割实现洪水识别(数据集 + 源码)
查看>>
OpenCV与AI深度学习 | 基于YOLO11的车体部件检测与分割
查看>>
OpenCV与AI深度学习 | 基于YoloV11自定义数据集实现车辆事故检测(有源码,建议收藏!)
查看>>
OpenCV与AI深度学习 | 基于YOLOv8 + BotSORT实现球员和足球检测与跟踪 (步骤 + 源码)
查看>>
OpenCV与AI深度学习 | 基于YOLOv8实现高级目标检测和区域计数
查看>>
VS2003 Front Page Server Extension
查看>>
OpenCV与AI深度学习 | 基于YOLOv8的停车对齐检测
查看>>
OpenCV与AI深度学习 | 基于YoloV8的药丸/片剂类型识别
查看>>
OpenCV与AI深度学习 | 基于YOLO和EasyOCR从视频中识别车牌
查看>>
OpenCV与AI深度学习 | 基于图像处理的火焰检测算法(颜色+边缘)
查看>>
OpenCV与AI深度学习 | 基于拉普拉斯金字塔实现图像融合(步骤 + 代码)
查看>>
OpenCV与AI深度学习 | 基于改进YOLOv8的景区行人检测算法
查看>>