Android.mk 文件解析

Android.mk 文件其实是把 Makefile 包装起来,做成了一个对使用者来说很简单的东西。使用它来编译程序时,不管是动态库、可执行的二进制文件,还是Jar库、APK包,只要沿着一个简单的思路来做三大步就可以了:清除旧变量;设置新变量;调用编译函数。
对于初学者来说,需要明白的是,Android 如何让使用脚本的人从 Makefile 语法中解放出来,简单地按照上面的三大步就可以编译出任何模块。

一、概述

1、举例

## 拿 AlarmClock 举例说明
// 1.清除旧变量
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

// 2.设置新变量
LOCAL_SRC_FILES := $(call all-subdir-
LOCAL_PACKAGE_NAME := AlarmClock

// 3.调用编译函数
include $(BUILD_PACKAGE)

下面简单解释一下这三步:
(1)清除旧变量,是因为 Android.mk 文件中,所有的变量都是全局的,编译函数在编译时会调用这些变量。为了防止编译函数使用了编译其它模块时设置的变量,每次开始编译一个新的模块时,需要清除之前所有的变量。
(2)设置新变量,就是把本次编译时用到的源码地址,包名等设置好。
(3)调用编译函数,其实就是 include 一个固定的 .mk文件,这个 .mk文件会根据设置的变量,提取出编译模块需要的 target、source、command 等信息,并执行固定的编译命令。

2、Makefile 文件的封装
Makefile 文件,简单而言,就是如下模式:
(1)目标:信赖文件
(2)执行命令

举个不完整的例子:

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
commnd := /

javac AlarmClock.java/
java AlarmClock.class

这样的话,在编译每个APK时,只要分别给 target、source、command 赋值即可。

完善一下上面的过程,把它系统化。
(1)定义一个专门用来清除变量的clear_vars.mk里面的内容如下:

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

(2)再定义一个变量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)

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

二、示例

1、选取adb项目中 Androrid.mk 中核心实现,如下:(代码路径:system/core/adb)

LOCAL_PATH:= $(call my-dir)     # LOCAL_PATH 的位置先于 CLEAR_VARS
include $(CLEAR_VARS)   # CLEAR_VARS的定义在/build/core/clear_vars.mk中,它清除了上百个除LOCAL_PATH外的变量。
                        # 因而CLEAR_VARS通常被认为是一个编译模块的开始标志。
# adb内部定义下面两个变量,将在不同的操作系统环境下赋予不同的文件值。
# 因为不同的操作系统所需的usb驱动是不一样的,所以有此区分。
USB_SRCS :=
EXTRA_SRCS :=
# Linux环境下
ifeq ($(HOST_OS),linux) 
  USB_SRCS := usb_linux.c
  EXTRA_SRCS := get_my_path_linux.c
  LOCAL_LDLIBS += -lrt -ldl -lpthread
  LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
endif
…    # 省略其它操作系统下的类似处理
LOCAL_SRC_FILES := \     # LOCAL_SRC_FILES是一个很重要的变量,它定义了本模块编译所涉及的所有源文件。
						 # 可以看到,adb自定义的两个变量(USB_SRCS、EXTRA_SRCS)也在这里被加入到了编译列表中。
     adb.c \
     …
     $(EXTRA_SRCS) \
     $(USB_SRCS) \
     utils.c \
     usb_vendors.c
…

ifneq ($(USE_SYSDEPS_WIN32),)
  LOCAL_SRC_FILES += sysdeps_win32.c      # 根据实际情况来扩展LOCAL_SRC_FILES变量
else
  LOCAL_SRC_FILES += fdevent.c
endif
# 下面两行用于添加编译标志,这在编译过程中会起作用。
LOCAL_CFLAGS += -O2 -g -DADB_HOST=1  -Wall  -Wno-unused-parameter
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE

LOCAL_MODULE := adb       # 所要生成的模块的名称!
…
LOCAL_STATIC_LIBRARIES := libzipfile libunz libcrypto_static $(EXTRA_STATIC_LIBS)    # 编译过程中要用到的静态库# 下面这个语句是整个Android.mk的重点。
# BUILD_HOST_EXECUTABLE:表示我们希望生成一个HOST可执行程序。当然,你也可以根据需要来选择其他“BUILD_XXX”变量。
# 每一个编译模块从CLEAR_VARS开始,到这里结束。
include $(BUILD_HOST_EXECUTABLE)
######################################################### 到目前为止的语句仅定义了adb host工具的生成过程,这个程序将运行于开发环境所处的机器上。
# 如:在Windows操作系统中的Eclipse集成开发环境中开展产品研发,那么adb host就运行在Windows中。
# 它实际上分饰:adb client、adb server两个角色(通过源码中的ADB_HOST宏进行区分)。

# 以下内容是adbd的编译配置,这个程序运行于设备端。
include $(CLEAR_VARS)      # 第二个模块编译的开始标志
LOCAL_SRC_FILES := \       # 从LOCAL_SRC_FILES变量可以发现,adb daemon和adb host所涉及的文件是有重叠的。
     adb.c \
     …
     Utils.c

…        # 省略和第一个模块中类似的语句
LOCAL_MODULE := adbd         # 所要生成的模块的名称!
…
LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt    # 编译过程中要用到的静态库
include $(BUILD_EXECUTABLE)        # 到这里为止,第二个模块的编译配置结束,最终生成adbd可执行程序。

三、Androrid.mk中常用变量

变量名说明
LOCAL_PATH用于确定源码所在的目录,最好把它放在CLEAR_VARS变量引用的前面,因为它不会被清除,每个Android.mk只需要定义一次即可
CLEAR_VARS它清理了很多以”LOCAL_“开头的变量(LOCAL_PATH除外)。由于所有的Makefile都是在一个编译环境中执行的,因此变量理论上都是全局的,在每个模块编译开始前进行清理工作是必要的
LOCAL_MODULE模块名,需保证在整个编译系统中是唯一存在的,而且中间不可以有空格
LOCAL_MODULE_PATH模块的输出路径
LOCAL_SRC_FILES模块编译过程所涉及的源文件,如果是Java程序,可以考虑调用all-subdir-java-files来一次性添加目录(包括子目录)下所有的Java文件,因为有LOCAL_PATH,这里只需要给出文件名(相对路径)即可。而且编译系统有比较强的推导功能,可以自动计算依赖关系
LOCAL_CC用于指定C编译器
LOCAL_CXX用于指定C++编译器
LOCAL_CPP_EXTENSION用于指定特殊的C++文件后缀名
LOCAL_CFLAGSC语言编译时的额外选项
LOCAL_CXXFLAGSC++语言编译时的额外选项
LOCAL_C_INCLUDES编译C和C++程序所需要的额外头文件
LOCAL_STATIC_LIBRARUES编译所需要的静态库列表
LOCAL_SHARED_LIBRARIES编译所需要的共享库列表
LOCAL_JAVA_LIBRARIES编译所需要的Java类库
LOCAL_LDLIBS编译时所需要的链接选项
LOCAL_COPY_HEADERS安装应用程序时所需要复制的头文件列表,需要和LOCAL_COPY_HEADERS_TO变量配合使用
LOCAL_COPY_HEADERS_TO上述头文件列表的复制目的地
BUILD_STATIC_LIBRARY各种形式的编译模板,如:生成设备端或Host端的静态、动态库文件(Java、C/C++等)、可执行文件、文档等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当使用 Android NDK(Native Development Kit)开发安卓应用时,Android.mk 文件是一个重要的构建配置文件,用于描述和控制 C/C++ 源代码的编译和构建过程。它是使用 GNU Make 构建系统的一部分,用于构建 Native 库或可执行文件Android.mk 文件的作用主要包括以下几个方面: 1. 定义模块:Android.mk 文件用于定义一个或多个模块,每个模块对应一个 Native 库或可执行文件的构建过程。你可以在文件中指定模块的名称、类型(例如静态库、共享库、可执行文件等)以及所需的源文件和依赖库。 2. 指定源文件:通过 Android.mk 文件,你可以列出构建所需的源文件,包括 C/C++ 源代码文件、头文件等。你可以使用通配符来指定源文件的匹配模式,也可以手动列出每个源文件。 3. 配置编译选项:Android.mk 文件允许你配置编译选项,如编译器标志、链接选项等。你可以指定编译器的标准、优化级别、预定义宏等,并可以添加额外的编译选项以满足特定需求。 4. 管理依赖关系:Android.mk 文件允许你指定当前模块所依赖的其他模块或库。通过声明这些依赖关系,构建系统可以自动解析和处理模块之间的依赖关系,并确保正确的构建顺序。 5. 控制构建过程:Android.mk 文件中的规则和命令可以用于控制构建过程。你可以定义编译、链接以及其他自定义的构建规则,并指定执行这些规则的命令。这样,构建系统会根据 Android.mk 文件中的规则来执行相应的操作,从而生成目标文件。 总而言之,Android.mk 文件是用于描述和控制 C/C++ 源代码的编译和构建过程的配置文件。通过 Android.mk 文件,你可以定义模块、指定源文件、配置编译选项、管理依赖关系,并控制构建过程中的规则和命令。这使得使用 NDK 构建 Native 库或可执行文件变得更加灵活和可配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值