NDK开发之Android.mk文件初探
Android.mk文件概述
Android.mk是一个向Android NDK 构建系统描述NDK 项目的GUN Makefile片段.
Android.mk是NDK项目的必备组件.
NDK构建系统要求将Android.mk放置在jni子目录中.
Android.mk是一个GUN Makefile片段,所以语法和其他的Makefile一样, 每一行都包含一个指令.
Makefile文件使用 #作为指令注解的开头, GUN Make工具不处理注解行.
根据GUN Make 的命名规范, 变量名要大写.
Android.mk内容详解
声明 LOCLA_PATH 变量
LOCLA_PATH := $(call my-dir)
根据Android构建系统的要求,Android.mk文档必须以 LOCAL_PATH 变量的定义开头.
Android构建系统利用LOCAL_PATH来定位源文件.
由于LOCAL_PATH设置为硬编码并不合适, 所以构建系统提供 my-dir 的宏功能.
通过将该变量设置为 my-dir 的宏的返回值, 可以将其放置在当前目录下.
引入 CLEAR_VARS 指令
Include $(CLEAR_VARS)
Android 构建系统将 CLEAR_VARS 变量设置在 clear-vars.mk片段, 其作用是, 引入的Makefile 片段可以清除除了 LOCAL_PATH 以外的 LOCAL_ 的变量.
eg: LOCAL_MODULE, LOCAL_SRC_FILES等.
每一个原生组件被成为一个模块, Android构建系统在单次执行中解析多个构建文件和模块定义, 而LOCAL_ 是全局变量, 执行清除指令可以避免冲突.
声明 LOCAL_MODULE 变量
LOCAL_MODULE := hello-jni
LOCAL_MODULE变量用来给模块设定唯一的名称, 以上则为设定该模块的名称为: hello-jni.
模块名称也被用于构建系统生成的文件命名, 所以构建系统给该文件添加了适当的前缀和后缀.
上面的hello-jni模块会生成一个共享库文件且构建系统会将他命名为: libhello-jni.so.
声明 LOCAL_SRC_FILES 变量
LOCAL_SRC_FILES := hello.c
LOCAL_SRC_FILES 变量定义用来建立和组装该模块的源文件列表.
eg: LOCAL_SRC_FILES := subDir<name>/.../hello.c
LOCAL_SRC_FILES 可以包含用空格分开的多个源文件名, 如果jni下有多级子目录则如下.
可以使用: ‘空格 + \’ 进行换行.
eg: LOCAL_SRC_FILES := \ hello.c \ ... \ world.c
多级子目录
eg: LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \ subDir<name>/.../hello.c \ ... \ subDir<name>/.../world.c
引入 BUILD_SHARED_LIBRARY 指令 # 构建共享库
include $(BUILD_SHARED_LIBRARY)
为建立可供主程序使用的模块, 必须将模块变成共享库.
Android NDK 构建系统将 BULID_SHARED_LIBRARY 变量设置为 build-shared-library.mk文件的保存位置, 改Makefile片段包含了将源文件构建和组装成为共享库的必要过程.
基于不同的应用的体系结构, 一个单独的Android.mk 文档可能产生多个共享库模块,
eg: # 模块1 开始 include $(CLEAR_VARS) LOCAL_MODULE := <module1_name> LOCAL_SRC_FILES := <module1.xx.c ...> # 可有多个 include $(BUILD_SHARED_LIBRARY) # 模块1 结束 # 模块2 开始 include $(CLEAR_VARS) LOCAL_MODULE := <module2_name> LOCAL_SRC_FILES := <module2.xx.c ...> # 可有多个 include $(BUILD_SHARED_LIBRARY) # 模块2 结束 .... # 可以有多个模块 # 处理完Android.mk构建文档后, Android NDK 构建系统会产生 lib<module1_name>.so 和 lib<module2_name>.so 对应个共享库.
引入 BUILD_STATIC_LIBRARY 指令
incline $(BUILD_STATIC_LIBRARY) # 构建静态库
静态库可以保证源代码模块化, 当静态库和共享库连接时, 他就变成共享库的一部分.在多个共享库于同一个静态库连接时, 需要将多个副本和不同的共享库重复链接, 这样就增加了应用程序的大小.
Android NDK 构建系统支持链接静态库.
实际的应用并不直接使用 静态库, 且应用程序包中不包含静态库, 静态库用来构建共享库.
在将第三方代码添加到现有原生项目中时, 不直接将三方源码包含在原生项目中, 而是将第三方代码编译成静态库并入到共享库.
eg: # 程序清单 LOCAL_PATH := $(call my-dir) # 三方AVI库 include $(CLEAR_VARS) LOCAL_MODULE := vailib LOCAL_SRC_FILES := avilib.c platform.c # 引入静态库指令 include $(BUILD_STATIC_LIBRARY) # 三方库配置完成 # 原生模块 include $(CLEAR_VARS) LOCAL_MODULE := <module_name> LOCAL_SRC_FILES := <srcxxx>.c ... # 声明静态库变量, 指向 三方静态库 vailib LOCAL_STATIC_LIBRARIES := vailib include $(BUILE_SHARED_LIBRARY) # 原生模块配置完成 # 说明: 在将三方代码模块生成静态库之后, 共享库就可以通过他的模块名添加到 LOCAL_STATIC_LIBRARY 变量中来使用该模块
用共享库共享通用模块
静态库可以保证源代码模块化, 当静态库和共享库连接时, 他就变成共享库的一部分.在多个共享库于同一个静态库连接时, 需要将多个副本和不同的共享库重复链接, 这样就增加了应用程序的大小.
在这种情况下, 不用构建静态库, 而是将通用模块作为共享库建立, 二动态链接依赖模块以便消除重复的副本.eg: LOCAL_PATH := #(call my-dir) # 将三方通用模块作为共享库 include $(CLEAR_VARS) LOCAL_MODULE := vailib LOCAL_SRC_FILES := vai.c vai-xx.c include $(BUILD_SHARED_LIBRARY) # 原生模块1, 链接vailib模块 #include $(CLEAR_VARS) LOCAL_MODULE := module1 LOCAL_SRC_FILES := module1.c LOCAL_SHARED_LIBRARIES := vailib include $(BUILD_SHARED_LIBRARY) # 原生模块1 结束 # 原生模块2, 也链接到 vailib模块 include $(CLEAR_VARS) LOCAL_MODULE := module2 LOCAL_SRC_FILES := module2.c LOCAL_SHARDE_LIBRARIES := vailib include $(BUILD_SHARED_LIBRARY)
在多个NDK项目间共享模块
同时使用静态库和共享库, 可以在模块间共享通用模块, 但这都是在同一个NDK项目中. 从R5版本开始, Android NDK 开始允许在NDK项目间共享和重用模块.
在Android NDK 构建系统中共享模块路径不能包含空格.
以上面的vailib为例 具体如下
- 将avilib的源代码移到NDK 项目以外的位置, 为避免命名冲突, 目录结构可以包含模块提供者.
eg: C:/android\shared-modules\transcode\avilib
- 作为共享模块, vailib需要有自己的Android.mk文件, 清单如下:
# vailib 模块配置清单 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := vailib LOCAL_SRC_FILES := valib.c include $(BUILD_SHARED_LIBRARY)
- 将vailib模块的配置信息在原生项目中移除, 为了使用这个共享模块, 将以transcode/vailib为参数调用函数宏 import-module 部分增加在构建文档的末尾.
为避免构建系统冲突, 应将 import-module 函数宏放在Android.mk文档的末尾.
# 原生模块配置清单 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := module LOCAL_SRC_FILES := module.c include $(BUILD_SHARED_LIBRARY) $(call import-module, transcode/vailib)
import-module函数宏需要先定位共享模块, 然后再将其导入 NDK 项目中.
默认 import-module 函数宏只搜索/sources 目录下的模块, 为了能够搜索 C:/android\shared-modules\transcode\avilib 目录, 需要定义一个名为: NDK_MODULE_PATH 的环境变量, 并将其设置为共享模块的根目录 如上中: C:/android\shared-modules\