本博客仅适合刚接触Android.mk的新人,简单上手所作。另外本人只想做一个纯粹的仅适合嵌入式C,不涉及 Java 的Android.mk粗略讲解,适合建立一个初步的概念框架,绝不涉及过多更为细节的内容,小伙伴也可以直接看编译module模板一节,做到快速上手。追求更详细内容的讲解,可以看CSDN 其他大佬博主的Android.mk详解,非常详细全面。
Android.mk文件用来告知NDK Build 系统关于Source 的信息,个人理解即是链接头文件、源文件、各类库来编译模块,其中无需额外添加依赖关系。由于Android.mk中所有的变量都是全局的,所以请尽量减少在Android.mk声明的变量,也不要认为某些变量在解析过程中不会被定义。
对于C/C++ 可编译的模块类型:
module | 说明 |
---|---|
C/C++应用程序 | 可执行的 C/C++应用程序 |
C/C++静态库 | 编译生成C/C++静态库,并打包生成 .a文件 |
C/C++共享库 | 编译生成共享库,也可以是动态链接库, 两者只是调用库的方式不同,并打包成 .so文件 |
编译指令:
我们可以在每一个Android.mk中定义一个或多个模块, 也可以在几个模块中使用同一个源代码文件。编译系统会为你处理许多细节问题,比如你无需在Android.mk中列出头文件和生成的文件之间的显式依赖关系,编译系统将会为你自动处理这些问题。
符号 | 作用 |
---|---|
:= | 赋值 |
+= | 追加 |
$ | 引用某变量的值 |
\ | ‘\’和 Enter键配合来换行,可以连接同一变量上下行的文件 |
‘\’ 使用实例
LOCAL_SRC_FILES:= \
main.c \
yan.c \
pwm.c
ps: 注意一行中’\‘后不能有空格,否则会编译出错;最后一个文件后尽量避免添加不必要的’\’,以免误将非空格的无关的下一行添加入变量。另外若一行足够,同一行的文件之间可以用空格键或Tab键进行分割。
在Android.mk中允许自定义变量,但是需要避开NDK编译系统已保留下来的变量名成分。
以下是要避开的变量名成分:
1. 以LOCAL_开头的名字,例如LOCAL_MODULE
2. 以PRIVATE_, NDK_, 或APP_开头的名字(内部使用)
3. 小写名字(内部使用),如" my_dir"
为了方便在Android.mk中定义自己的变量,建议使用"MY_ "作为前缀。
这些GNU Make变量在你的Android.mk文件解析之前,就由编译系统定义好了。注意在某些情况下, NDK可能分析Android.mk几次, 每一次某些变量的定义会有不同。
1) CLEAR_VARS
指向一个编译脚本,几乎所有未定义的LOCAL_XXX都在"Module-description"节中列出。必须在开始一个新模块之前定义这个脚本: include $(CLEAR_VARS),用于重置除LOCAL_PATH变量外的所有LOCAL_XXX系列变量。
2) BUILD_SHARED_LIBRARY
指向编译脚本,把在include $(CLEAR_VARS)之后,include $(BUILD_SHARED_LIBRARY)之前所有的LOCAL_XXX变量列出的源代码文件编译成一个共享库, 即生成一个libxxx.so文件。
3) BUILD_STATIC_LIBRARY
用于编译一个静态库,编译的源文件方法同BUILD_SHARED_LIBRARY。静态库不会复制到APK中, 但是能够用静态库编译共享库。
4) BUILD_EXECUTABLE
用于编译一个可执行程序,如include $(BUILD_EXECUTABLE),生成可执行的 C/C++应用程序。
5) TARGET_ARCH
目标CPU架构的名字,和android开放源码中指定的那样。如果是arm,表示要生成ARM兼容的指令,与CPU架构的修订版无关。
6) TARGET_PLATFORM:
目标Android.mk平台的名字,详情可参考/development/ndk/docs/stable- apis.txt。
android-3 -> Official Android 1.5 system images
android-4 -> Official Android 1.6 system images
android-5 -> Official Android 2.0 system images
下面的变量用于向编译系统描述你的模块,应该定义在 “include $(CLEAR_VARS) " 和"include $(BUILD_XXX)” 之间。正如前面描述的一样, include $(CLEAR_VARS)用于重置除LOCAL_PATH变量外的所有LOCAL_XXX系列变量。
1) LOCAL_PATH
用于给出当前需编译文件所在的路径,必须在 Android.mk的开头定义,使用方法如 LOCAL_PATH := $(call_my_dir),则$(LOCAL_PATH) 为当前 Android.mk所在的路径。这个变量不会被include $(CLEAR_VARS)清除,因此在每个Android.mk文件开头定义一次即可,即使该文件中已经定义了几个模块(LOCAL_MOUDEL)。
2)LOCAL_MOUDEL
给定模块的名字,必须是唯一的,且不含空格,必须在任一包含 $(BUILD_XXX) 的脚本前定义它。模块的名字决定了生成文件的名字。例如,一个共享库的模块的名字为libvp_vpu ,则生成的文件为libvp_vpu.so。另外此时共享库模块对应的LOCAL_MODULE的名字最好能有lib前缀,因为有的 Android.mk并不会自动生成前缀lib ,以致不能正确调用共享库编译可执行程序,编译出错。
3)LOCAL_MODULE_PATH
指定生成模块的路径,可和LOCAL_MODULE搭配使用。 若未设置,则生成的模块在编译系统默认的生成路径。用法如 LOCAL_MODULE_PATH := $(LOCAL_PATH)。
4)LOCAL_SRC_FILES
给定要编译的源代码文件列表,文件也可以是静态库,或共享库。只要列出要传递给编译器的文件,则编译系统就能自动计算文件之间的依赖关系。注意给定的源代码文件都相对于 $(LOCAL_PATH),在该路径下。也可以选择添加路径部分,例如 LOCAL_SRC_FILES := taobao/foo.c ,使用的是$(LOCAL_PATH)相对路径, 绝对路径为$(LOCAL_PATH)/taobao/。
5)LOCAL_STATIC_LIBRARIES
表示该模块需要使用哪些静态库,以便在编译时进行链接。
6)LOCAL_SHARED_LIBRARIES
表示该模块在编译时要依赖的共享库,在链接时就需要,以便在生成文件时嵌入其相应的信息,但不像静态库那样把程序代码加入。所以程序执行的开始,也要有相应的共享库和对应的库路径。 简单的库路径添加命令: $export LD_LIBRARY_PATH=/…/…/…/,也可以选择放在/system/lib/等lib目录下方便加载。
7)LOCAL_C_INCLUDES
这是一个可选变量,很常用,基本都会用到,表示头文件的搜索路径。默认的头文件搜索路径是 $(LOCAL_PATH)目录。如果要添加其他路径的头文件,添加路径的方法和 LOCAL_SRC_FILES一样。
8)LOCAL_CFLAGS
可选的编译器选项,在编译 C 代码文件的时候使用。这可能是有用的,指定一个附加的包含路径(相对于NDK的顶层目录),宏定义,或者编译选项。
注意:不要在 Android.mk中改变 optimization/debugging 级别,只要在 Application.mk中指定合适的信息,就会自动地为你处理这个问题,在调试期间,会让 NDK自动生成有用的数据文件。
ps: 以下是C++的内容,不需要的可以跳过
9)LOCAL_CPP_EXTENSION
这同样是一个可选变量,用来指定C++代码文件的扩展名,默认是".cpp", 但是可以改变扩展名,比如LOCAL_CPP_ENTENSION := .cxx ,则可以用.cxx的扩展名,本人尚未尝试使用。
10)LOCAL_CXXFLAGS
与LOCAL_CFLAGS同理,针对C++源文件。
11)LOCAL_CPPFLAGS
与LOCAL_CFLAGS同理,对C和C++ 源文件都适用。
其实编译各模块的模板都很相似,光看前面是不易看出究竟是编译什么模块的,然而在模块编译的最后一句可以明确区分是编译哪种类型的模块。
模块 | 模块类型确定 |
---|---|
C/C++应用程序 | include $(BUILD_EXECUTABLE) |
C/C++静态库 | include $(BUILD_STATIC_LIBRARY) |
C/C++共享库 | include $(BUILD_SHARED_LIBRARY) |
1. 编译C/C++ 应用程序模板
备注: 带#的变量是可选项
#Android.mk 开头初始化,
#确定当前编译文件路径以及重置除LOCAL_PATH以外的 LOCAL_XXX变量
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#生成的模块名,可选是否设置模块存放的路径,我选择放在当前路径下
LOCAL_MODULE := XXX
#LOCAL_MODULE_PATH := $(LOCAL_PATH)
#需要的材料,分别是源文件(必选)、静态库.a文件(可选)、共享库.so文件(可选)、头文件(可选)
#若未设置路径,则材料默认的搜索路径为 $(LOCAL_PATH),在该目录下
#可定义路径,绝对路径如 $(TOP)/hardware/rog.c
#或相对于 $(LOCAL_PATH)的路径,hardware/rog.c 等效于 $(LOCAL_PATH)/hardware/rog.c
LOCAL_SRC_FILES := main.c \
Xxx.c \
xxx.c
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
#LOCAL_C_INCLUDES :=
#生成可执行程序
include $(BUILD_EXECUTABLE)
ps: C/C++执行模块的目标文件夹为 XXX_intermediates,XXX是模块名 。
2. 编译C/C++静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#生成的静态库模块名,将生成xxx.a文件
LOCAL_MODULE := XXX
#LOCAL_MODULE_PATH := $(LOCAL_PATH)
#需要的材料,分别是源文件(必选)、静态库(可选)、共享库(可选)、头文件(可选)
LOCAL_SRC_FILES := Xxx.c \
xxx.c
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
#LOCAL_C_INCLUDES :=
#生成C/C++静态库
include $(BUILD_STATIC_LIBRARY)
ps: C/C++ 静态库模块的目标文件夹为 XXX_static_intermediates,或者XXX_intermediates,我的是后者,编译系统版本不同会有差异, XXX是模块名。这个在 Android.mk编译生成过程中会有显示。
3. 编译C/C++ 共享库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#生成的模块名,xxx最好能有lib前缀,生成libxxx.so文件,以适应不同版本的Android.mk
LOCAL_MODULE := XXX
#LOCAL_MODULE_PATH := $(LOCAL_PATH)
#需要的材料,分别是源文件(必选)、静态库(可选)、共享库(可选)、头文件(可选)
LOCAL_SRC_FILES := Xxx.c \
xxx.c
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
#LOCAL_C_INCLUDES :=
#生成C/C++ 共享库
include $(BUILD_SHARED_LIBRARY)
ps: 生成的目标文件夹名带 "_intermediates"结尾,在编译过程中有显示,版本不同则文件夹路径和文件夹名会有差异,根据实际的目录查找即可。同时静态库可用来编译共享库,则LOCAL_SRC_FILES := Xxx.a。
当然也可以在一个Android.mk编译多个模块,模块之间Include $(CLEAR_VARS) 和Include $(BUILD_)相隔, 其中LOCAL_PATH 只需在Android.mk开头定义一次即可,后续不必重复定义。
附本人的Android.mk实例:
#是否要编译库, true 确定编译库, false 不编译库,选择编译执行程序
COMPILE_LIB := true
LOCAL_PATH:= $(call my-dir)
ifeq ($(COMPILE_LIB),true)
include $(CLEAR_VARS)
LOCAL_MODULE:= libvp_yan
LOCAL_MODULE_PATH := $(LOCAL_PATH)
LOCAL_CFLAGS += -DVERSION=4.3
LOCAL_SRC_FILES:= \
VP_Yan.c \
VP_Pwm.c \
VP_Hdu.c
LOCAL_SHARED_LIBRARIES := \
libhardware \
liblog
LOCAL_C_INCLUDES += \
hardware/libhardware/include \
$(TOP)/hardware/log \
$(LOCAL_PATH)/hfile #有头文件位于当前编译路径的hfile子文件夹下
include $(BUILD_SHARED_LIBRARY)
else
#编译可执行程序vp_yin
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DVERSION=4.3
LOCAL_MODULE:= vp_yin
LOCAL_MODULE_PATH := $(LOCAL_PATH)
LOCAL_SRC_FILES:= \
VP_Main.c
LOCAL_SHARED_LIBRARIES := \
libhardware \
liblog \
libvp_yan #添加入true选项编译的libvp_yan.so 共享库
LOCAL_C_INCLUDES += \
hardware/libhardware/include \
$(TOP)/hardware/log \
$(LOCAL_PATH)/hfile
include $(BUILD_EXECUTABLE)
endif
本博客大量参考了下列博主的文章
1. Android NDK学习 <二> Android.mk的制作
2. Android.mk的用法
3. Android.mk 中的静态库和共享库
4. android.mk 的理解和使用
5. android.mk 详解