一、什么是android.mk?
Android.mk文件用来告知NDK Build系统关于Source的信息。Android.mk是GNU Makefile的一部分,且将被Build System解析一次或多次。(android.mk文件中的语法格式只有NDK编译系统才能识别)
Build System帮我们处理了很多细节而不需要我们再关心。例如:你不需要在Android.mk中列出头文件和外部依赖文件。
NDK Build System自动帮我们提供这些信息。这也意味着,当用户升级NDK后,你将可以受益于新的toolchain/platform而不必再去修改Android.mk。
ref:英文原文件在/development/Ndk/Docs/android-mk.txt
二、Android.mk语法
1. 基本语法
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
讲解如下:
LOCAL_PATH := $(call my-dir)
每个Android.mk文件必须以定义LOCAL_PATH为开始,宏my-dir则由Build System提供,返回包含Android.mk的目录路径,即Android.mk文件所在的文件夹。
include $(CLEAR_VARS)
CLEAR_VARS 变量由Build System提供,并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx。例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等。但不清理LOCAL_PATH,这个清理动作是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的,所以清理后才能避免相互影响。
LOCAL_MODULE := hello-jni
LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块。名字必须唯一且不包含空格。这个名字就是最终编译生成的文件的名字。当然,Build System会自动添加适当的前缀和后缀。例如,foo,要产生动态库,则生成libfoo.so。 但请注意:如果模块名被定为:libfoo,则生成libfoo.so,不再加前缀。
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SRC_FILES 变量必须包含将要打包如模块的 C/C++ 源码。
不必列出头文件,Build System 会自动帮我们找出依赖文件。
缺省的C++源码的扩展名为.cpp。 也可以修改,通过LOCAL_CPP_EXTENSION。
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY 是Build System提供的一个变量,指向一个GNU Makefile Script。
它负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息,并决定最终编译成什么文件。
2. NDK提供的函数宏
GNU Make函数宏,必须通过使用'$(call )'来调用,返回值是文本化的信息。
$(call my-dir) | 返回当前 Android.mk 所在的目录的路径 |
all-subdir-makefiles | 返回一个位于当前'my-dir'路径的子目录中的所有Android.mk的列表 |
$(call> all-makefiles-under, ) | 获取指定目录下的所有 Make 文件。 1.只include Android.mk文件,叫其他名字的mk文件,不include. 源码: define all-makefiles-under |
include $(call first-makefiles-under,$(LOCAL_PATH)) | |
$(call all-c-files-under, ): | 获取指定目录下的所有 C 语言文件。通常用法: |
$(call all-java-files-under, ) | 获取指定目录下的所有 Java文件。 |
this-makefile: | 返回当前Makefile(译者注:指的应该是GNU Makefile)的路径(即,这个函数是在哪里调用的) |
$(call import-module,<name>) | 同意寻找并导入其他modules到本Android.mk中来。它会从NDK_MODULE_PATH寻找指定的模块名。 |
$(call intermediates-dir-for, , <app_name>, , <common?> ):获取 Build 输出的目标文件夹路径。
-
parent-makefile: 返回调用树中父 Makefile 路径。即包含当前Makefile的Makefile 路径。
-
grand-parent-makefile:返回调用树中父Makefile的父Makefile的路径
3.模块描述变量
此类变量用来给Build System描述模块信息。在'include $(CLEAR_VARS)' 和 'include $(BUILD_XXXXX)'之间,必须定义此类变量。
- include $(CLEAR_VARS) 用来清空这些变量。
- include $(BUILD_XXXXX) 收集和使用这些变量。
变量名 | 说明 |
---|---|
LOCAL_MODULE | 当前模块的名称,这个名称应当是唯一的,通常由此变量名决定最终生成的目标文件名,模块间的依赖关系就是通过这个名称来引用的。 |
LOCAL_SRC_FILES | 当前模块包含的所有源代码文件,不需要列出依赖文件。 注意:文件相对于LOCAL_PATH存放,且可以提供相对路径。 |
LOCAL_C_INCLUDES | C 或 C++ 语言需要的头文件的路径。 |
LOCAL_STATIC_LIBRARIES | 要链接到当前模块的静态库list |
LOCAL_WHOLE_STATIC_LIBRARIES | 静态库全链接,不同于LOCAL_STATIC_LIBRARIES,类似于使用--whole-archive,LOCAL_WHOLE_STATIC_LIBRARIES在连接静态连接库的时候不会移除"daed code",何谓dead code呢,就是调用者模块永远都不会用到的代码段和变量。 |
LOCAL_SHARED_LIBRARIES | 要链接到当前模块的动态库list |
LOCAL_STATIC_JAVA_LIBRARIES | 当前模块依赖的 Java 静态库。 |
LOCAL_JAVA_LIBRARIES | 当前模块依赖的 Java 共享库。 |
LOCAL_CPPFLAGS | C++ Source 编译时添加的C Flags,这些Flags将出现在LOCAL_CFLAGS flags 的后面。 |
LOCAL_CFLAGS | 提供给 C/C++ 编译器的额外编译参数。 |
注意:不要尝试在此处修改编译的优化选项和Debug等级,它会通过您Application.mk中的信息自动指定。
-
-Wall:是打开警告开关。
-
-O:代表默认优化,可选:-O0不优化,-O1低级优化,-O2中级优化,-O3高级优化,-Os代码空间优化。
-
-g:是生成调试信息,生成的可执行文件具有和源代码关联的可调试的信息。
-
-fopenmp:OpenMp是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,用于共享内存并行系统的多处理器程序设计的一套指导性的编译处理方案(Compiler Directive)。OpenMP支持的编程语言包括C语言、C++和Fortran;而支持OpenMp的编译器包括Sun Compiler,GNU Compiler和Intel Compiler等。OpenMp提供了对并行算法的高层的抽象描述,程序员通过在源代码中加入专用的pragma来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。当选择忽略这些pragma,或者编译器不支持OpenMp时,程序又可退化为通常的程序(一般为串行),代码仍然可以正常运作,只是不能利用多线程来加速程序执行。
-
-D:增加全局宏定义
-
-ffast-math:浮点优化选项,极大地提高浮点运算速度。
-
-mfloat-abi=softfp 浮点运算
LOCAL_CFLAGS += -DXXX # 相当于在所有源文件中增加一个宏定义'#define XXX'
4、指定编译生成的目标文件的格式
Android 源码中包含了许多的模块,模块的类型有很多种,例如:Java 库,C/C++ 库,APK 应用,以及可执行文件等 。并且,Java 或者 C/C++ 库还可以分为静态的或者动态的库或可执行文件。这些文件或者库既可能是针对设备(本文的“设备”指的是 Android系统将被安装的设备,例如某个型号的手机或平板)的也可能是针对主机(本文的“主机”指的是开发 Android 系统的机器,例如装有 Ubuntu 操作系统的 PC 机或装有 MacOS 的 iMac 或 Macbook)的。不同类型的模块的编译步骤和方法是不一样,为了能够一致且方便的执行各种类型模块的编译,在 config.mk
中定义了许多的常量,这其中的每个常量描述了一种类型模块的编译方式,这些常量有
- BUILD_HOST_STATIC_LIBRARY 编译静态库(适用与主机)
- BUILD_HOST_SHARED_LIBRARY 编译动态库(适用与主机)
- BUILD_HOST_EXECUTABLE 编译可执行程序(适用与主机)
- BUILD_HOST_PREBUILT 预编译(适用与主机)
- BUILD_HOST_JAVA_LIBRARY 编译java包(适用与主机)
- BUILD_JAVA_LIBRARY 编译java包
- BUILD_STATIC_JAVA_LIBRARY 编译java静态包
- BUILD_STATIC_LIBRARY 编译静态库,最终生成的是.a文件
- BUILD_SHARED_LIBRARY 编译动态库,最终生成的是.so文件
- BUILD_EXECUTABLE 编译可执行程序,
- BUILD_PACKAGE 编译apk
- BUILD_PREBUILT 预编译(针对单个预编译文件)
- BUILD_MULTI_PREBUILT 预编译(针对多个预编译文件)
通过名称大概就可以猜出每个变量所对应的模块类型。(在模块的 Android.mk文件中,只要包含进这里对应的常量便可以执行相应类型模块的编译。
这些常量的值都是另外一个 Make 文件的路径,详细的编译方式都是在对应的 Make 文件中定义的。这些常量和 Make 文件的是一一对应的,对应规则也很简单:常量的名称是 Make 文件的文件名除去后缀全部改为大写然后加上“BUILD_”作为前缀。例如常量 BUILD_HOST_PREBUILT 的值对应的文件就是 host_prebuilt.mk。
5、编译生成的目标文件存放在什么地方?
生成结果分别在如下,generic依具体target会变:
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY
每个模块的目标文件夹分别为:
可执行程序:XXX_intermediates
静态库: XXX_static_intermediates
动态库: XXX_shared_intermediates
ref
Android.mk文件语法规范(Android.mk File)_smfwuxiao的专栏-优快云博客_call my-dir
坑爹的all-makefiles-under函数-jizhao-ChinaUnix博客
call all-subdir-makefiles和call all-makefiles-under_技术笔记-优快云博客
Android.mk文件语法规范及使用模板 - 海王 - 博客园
Android.mk 中常用“LOCAL_” 变量 - 追问图 - 博客园
Android.mk文件详解 - VictorLee - 博客园
Android.mk 使用方法_程序员Android-优快云博客
https://blog.youkuaiyun.com/weixin_30569153/article/details/99801938
Android系统源码Build系统入门详解_zhonglunshun的专栏-优快云博客_android build系统