一、Android.mk文件概述 Android.mk文件用来告诉NDK编译系统,应该如何编译这些源码。更确切地说,该文件其实就是一个小型的Makefile。该文件会被NDK的编译工具解析屡次,因此要注意不要过多使用环境变量,以避免第一次解析时产生的变量影响后面的解析。Android.mk把源码组织成不一样的模块,每一个模块能够是一个静态库也能够是一个动态库。动态库才会被拷贝到安装包中,静态库只能用于编译生成动态库。java
同一个Android.mk文件能够定义多个模块,不一样的模块能够共用同一个源文件。android
注意,NDK所使用的Android.mk文件的语法与Android操做系统的开放源码中的Android.mk的语法很是接近,可是两个编译系统对Android.mk的使用方法不一样,这是为了方便应用程序开发人员复用之前的代码。shell
二、一个简单的例子 在详细讨论Android.mk文件的语法以前,先看一个简单的 “hello JNI“ 的例子,文件位于 apps/hello-jni/project。其中的 src 子目录存放Android工程的java源码,jni子目录存放C/C++源码文件,即 jni/hello-jni.c (实现了一个返回字符串给虚拟机的函数)。jni/Android.mk 文件是这个模块的编译脚本,内容以下:安全
[plain] view plain copy LOCAL_PATH := $(call my-dir)架构
include $(CLEAR_VARS)app
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c函数
include $(BUILD_SHARED_LIBRARY)
以上内容解释以下: LOCAL_PATH := $(call my-dir)工具
每一个Android.mk文件都必须在开头定义 LOCAL_PATH 变量。这个变量被用来寻找C/C++源文件。在该例中,my-dir 是一个由编译系统提供的宏函数,用于返回Android.mk所在目录的路径。测试
include $(CLEAR_VARS)
CLEAR_VARS是编译系统预约义的一个变量,它指向一个特殊的Makefile,这个Makefile负责清除 LOCAL_xxx 的变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES 等)但不会清除 LOCAL_PATH。之因此须要清理这些变量是由于全部的编译控制文件是在一趟make执行过程当中完成的,而全部的变量都是全局的,会对其余Android.mk文件产生影响。优化
LOCAL_MODULE := hello-jni
LOCAL_MODULE 用来给每一个模块定义一个名字,不一样模块的名字不能相同,不能有空格。这里的名字会传给NDK编译系统,而后加上lib前缀和.so后缀 (例如,变成libhello-jni.so)。注意,若是你在LOCAL_MODULE定义中本身加上了lib前缀,则ndk在处理的时候就不会再加上lib前缀了(为了兼容Android系统的一些源码)。
LOCAL_SRC_FILES := hello-jni.c
在LOCAL_SRC_FILES 变量里面列举出对应于同一个模块的、要编译的那些文件,这里不要把头文件加进来,编译系统能够自动检测头文件依赖关系。默认状况下,C++源码文件的扩展名应该是cpp,若是想修改的话,将变量LOCAL_CPP_EXTENSION修改成你想要的扩展名,注意句点。例如:LOCAL_CPP_EXTENSION := .cxx
include $(BUILD_SHARED_LIBRARY)
这个 BUILD_SHARED_LIBRARY也是预约义的变量,也是指向一个Makefile,负责将你在 LOCAL_XXX 等变量中定义信息收集起来,肯定要编译的文件,如何编译。若是要编译的是静态库而不是动态库,则能够用 BUILD_STATIC_LIBRARY。
在NDK安装目录的samples目录下有更加丰富的例子,里面都有详细的注释。
三、变量名的限制 下面这些变量是你能够直接使用或者应该由你来定义的。你也能够定义本身的变量,可是不能用如下NDK所保留的变量名:
以 LOCAL_ 开头的名字(例如,LOCAL_MODULE) 以 PRIVATE_, NDK_,APP_ 开头的名字(供NDK内部使用) 小写字母的变量名也不能使用(供NDK内部使用,例如 my-dir)
例如,你能够随便用 MY_ 开头的变量名:
[plain] view plain copy MY_SOURCES := foo.c
ifneq ($(MY_CONFIG_BAR),)
MY_SOURCES += bar.c
endif
LOCAL_SRC_FILES += $(MY_SOURCES)
四、NDK预约义变量 下面这些变量是ndk提早定义好的变量。有时ndk会解析同一个Android.mk文件屡次,每次解析时,这些变量的值可能不相同。
CLEAR_VARS 指向一个特殊的Makefile,负责清理 LOCAL_XXX 变量(LOCAL_PATH除外)。通常在定义新模块以前使用这个变量,用法:
include $(CLEAR_VARS)
BUILD_SHARED_LIBRARY 该变量实际指向了一个Makefile,用来把全部名为 LOCAL_XXX的变量中的信息收集起来,而后肯定如何把你提供的源码编译成目标模块。用法:include $(BUILD_SHARED_LIBRARY) 默认文件名:lib<LOCAL_MODULE>.so
BUILD_STATIC_LIBRARY 相似于BUILD_SHARED_LIBRARY,不过它用来编译静态库。静态库不会被拷贝到你的安装包中去,它每每用来编译其余动态库。用法:include $(BUILD_STATIC_LIBRARY) 默认文件名:lib<LOCAL_MODULE>.a
PREBUILT_SHARED_LIBRARY 该变量指向一个已编译好的共享库。与BUILD_SHARED_LIBRARY和BUILD_STATIC_LIBRARY不一样,此时相应的LOCAL_SRC_FILES再也不指定源文件,而是指向这个预编译共享库文件(例如 foo/libfoo.so)。能够在其余模块中,经过使用LOCAL_PREBUILTS变量来引用这个预编译模块。参考Prebuilt。
PREBUILT_STATIC_LIBRARY 与PREBUILT_SHARED_LIBRARY相同,只不过这里是静态库。 参考 Prebuilt。
TARGET_ARCH 目标CPU架构的名字,与Android操做系统的CPU架构名一致。若是想兼容全部ARM的CPU,能够用 “arm” 这个名字。
TARGET_PLATFORM 目标Android平台的名字。例如 android-3 对应的是 Android 1.5 系统镜像(Cupcake)。全部系统镜像的名字和相应的系统镜像可参考Stable APIs。
TARGET_ARCH_ABI 目标CPU和ABI组合的名字,目前只有2个值能够用:
armeabi 对于ARMv5TE
armeabi-v7a
注意,一直到Android NDK 1.6_r1,这里的值都是用“arm”。然而,该值已被从新定义以更好地匹配Android平台内部所使用的。
关于架构和ABI及兼容性问题,参考文档 Cpu Arch ABIs。
其余的目标ABI会在未来的NDK版本中增长,而且是不一样的名字。注意,全部兼容ARM的ABI的TARGET_ARCH都是arm,可是TARGET_ARCH_ABI不一样。
TARGET_ABI 目标平台和ABI的组合,定义为 $(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)。当你想在真机上测试一种系统镜像时有用。该变量的默认值是android-3-armeabi。
直到Android NDK 1.6_r1,这个值一直是 android-3-arm。
五、NDK预约义的宏函数 下面是NDK预约义的“函数”宏,用法是 $(call <function>) ,返回的是文本信息
my-dir 返回上一个被包含的Makefile的路径,典型状况是Android.mk文件所在的路径。这个函数对于定义LOCAL_PATH特别有用,例如:
LOCAL_PATH := $(call my-dir)
注意:因为make的工做原理,该函数返回的确实是上一个被包含的Makefile的路径(也就是说返回的结果可能不是Android.mk所在目录)。因此,包含了另一个文件以后,就不要再使用 my-dir 了。
例如,下面的例子:
[plain] view plain copy LOCAL_PATH := $(call my-dir)
该模块其余声明
include $(LOCAL_PATH)/foo/Android.mk LOCAL_PATH := $(call my-dir) 另外一个模块的声明
上面的问题就是第二次调用my-dir的时候,获得的是 $PATH/foo 而不是 $PATH,由于它前面有一个include语句。
所以,最好在全部include语句以前,把LOCAL_PATH定义好:
[plain] view plain copy LOCAL_PATH := $(call my-dir)
... declare one module
LOCAL_PATH := $(call my-dir)
... declare another module
include $(LOCAL_PATH)/foo/Android.mk
若是以为这样作不方便,能够把第一次调用的结果保存到变量中,例如: [plain] view plain copy MY_LOCAL_PATH := $(call my-dir)
LOCAL_PATH := $(MY_LOCAL_PATH)
... declare one module
include $(LOCAL_PATH)/foo/Android.mk
LOCAL_PATH := $(MY_LOCAL_PATH)
... declare another module
all-subdir-makefiles 返回当前的my-dir目录下的全部子目录的Android.mk文件的列表。例如,文件组织以下:
sources/foo/Android.mk sources/foo/lib1/Android.mk sources/foo/lib2/Android.mk
若是 sources/foo/Android.mk 包含以下行: include $(call all-subdir-makefiles) 那么,该文件将自动把 sources/foo/lib1/Android.mk 和 sources/foo/lib2/Android.mk 包含进来。当源码被组织成不少层次时能够利用该函数,默认状况下,NDK只会寻找 sources/*/Android.mk。 this-makefile 返回当前的Makefile的路径(即该函数调用时的位置)
parent-makefile 若是当前这个Makefile被另外一个Makefile包含,则返回那个包含了本身的Makefile的路径(即parent)
grand-parent-makefile (你猜猜......)
import-module 该函数用于按模块名查找另外一个模块的Android.mk文件,并包含进来。用法以下:
$(call import-module,<name>)
上面将在 NDK_MODULE_PATH变量所指定的目录列表中寻找名为<name>的模块,找到以后将包含进来。
能够参考 Import Module。
六、模块描述变量 下面这些变量用于对模块进行描述,这些变量应该在 include $(CLEAR_VARS) 和 include $(BUILD_XXXX) 之间定义好。
LOCAL_PATH (必须) 这个变量表示当前文件(通常是Android.mk)所在的路径,该变量很重要,必须定义(在Android.mk文件的开头处定义)。常见写法以下:
LOCAL_PATH := $(call my-dir)
该变量不会被 include $(CLEAR_VARS) 清空,因此不论Android.mk定义了几个模块,一个Android.mk只须要在开头定义一次便可。
LOCAL_MODULE (必须) 该变量定义当前模块的名字,名字必须惟一,不能有空格。这个变量必须在 include $(BUILD_XXX) 以前定义好。默认状况下,这里的名字会用来获得输出文件的名字。例如模块名为foo,则获得的输出文件为libfoo.so。可是,若是你要在其余模块的Android.mk文件或Application.mk中引用这个模块,应该用foo这个模块名,而不要用libfoo.so这个文件名。
LOCAL_MODULE_FILENAME (可选) 该变量能够用来重定义输出文件的名字。默认状况下,foo模块获得的静态库的名字为 libfoo.a,动态库的名字为libfoo.so(UNIX规范)。当定义了LOCAL_MODULE_FILENAME以后,输出文件名就是这个变量指定的名字,例如:
[plain] view plain copy LOCAL_MODULE := foo-version-1
LOCAL_MODULE_FILENAME := libfoo
注意: LOCAL_MODULE_FILENAME不支持文件路径(因此不能有斜杠),不要写扩展名(文件路径和文件扩展名是由编译工具自动加上的) LOCAL_SRC_FILES (必须) 该变量用来指定该模块对应的源文件,只把须要传给编译器的源文件名加进LOCAL_SRC_FILES,编译系统会自动处理头文件依赖。这里的文件名都是以 LOCAL_PATH 做为当前目录的(即相对于LOCAL_PATH目录),例如:
LOCAL_SRC_FILES := foo.c toto/bar.c
注意:必须使用Unix风格的斜杠,Windows风格的斜杠不能正确处理。
LOCAL_CPP_EXTENSION (可选) 用来定义C++代码文件的扩展名。必须以句点开头(即 “.”),默认值是“.cpp”,能够修改,例如:
LOCAL_CPP_EXTENSION := .cxx
从 NDK r7 这个版本开始,该变量能够支持多个扩展名了,例如:
LOCAL_CPP_EXTENSION := .cxx .cpp .cc
LOCAL_CPP_FEATURES (可选) 该变量用来指定C++代码所依赖的特殊C++特性。例如,若是要告诉编译器你的C++代码使用了RTTI(RunTime Type Information):
LOCAL_CPP_FEATURES := rtti
若是要指定你的C++代码使用了C++异常,则:
LOCAL_CPP_FEATURES := exceptions
该变量能够同时指定多个特性。例如: LOCAL_CPP_FEATURES := rtti features
这个变量的做用就是在编译模块的时候,开启相应的编译器/连接器标志。对于预编译的文件,该变量代表该预编译的库依赖了这些特性,从而确保最后的连接工做正确进行。与该变量等价的作法是在LOCAL_CPPFLAGS中写上 -frtti -fexceptions 等标志选项。可是,推荐用这里的方法。
LOCAL_C_INCLUDES 一个路径的列表,是NDK根目录的相对路径(LOCAL_SRC_FILES中的文件相对于LOCAL_PATH)。当编译C/C++、汇编文件时,这些路径将被追加到头文件搜索路径列表中。例如:
LOCAL_C_INCLUDES := sources/foo 或者, LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
这里的搜索路径会放在LOCAL_CFLAGS/LOCAL_CPPFALGS等标志的前面。 当使用ndk-gdb的时候,LOCAL_C_INCLUDES中的路径也会被用到。
LOCAL_CFLAGS 指定当编译C/C++源码的时候,传给编译器的标志。它通常用来指定其余的编译选项和宏定义。
注意:尽可能不要在Android.mk中修改优化/调试等级,由于在Application.mk中定义了相关信息以后编译系统会自动处理这些问题。
LOCAL_CXXFLAGS (废除, LOCAL_CPPFLAGS的别名) LOCAL_CPPFLAGS (可选) 编译C++代码的时候传递给编译器的选项(编译C代码不会用这里的选项)。最后获得的命令行选项中,这里指定的选项在 LOCAL_CFLAGS 指定的选项的后面。
LOCAL_STATIC_LIBRARIES 指定应该连接到当前模块的静态库(可指定多个)。当前模块是动态库时,该选项才有意义。
LOCAL_SHARED_LIBRARIES 指定的是运行时该模块所依赖共享库(可指定多个)。这些信息是连接阶段必须的。
LOCAL_WHOLE_STATIC_LIBRARIES 它是LOCAL_STATIC_LIBRARIES的变体,用来表示它对应的模块对于linker来讲应该是一个“whole archive”(见GNU linker 文档,关于 --whole-archive的资料)。当静态库之间有循环依赖时,会用到这个选项。注意,当编译动态库时,这个选项会强行把全部的对象文件组装到一块儿;不过,在编译可执行文件的时候状况不是这样的。
LOCAL_LDLIBS 用来指定模块编译时的其他链接器标志。例如:
LOCAL_LDLIBS := -lz
告诉连接器在加载该共享库的时候必须连接 /system/lib/libz.so 这个共享库。
若是想知道Android系统中有哪些共享库能够连接,参考 Stable APIs。
LOCAL_ALLOW_UNDEFINED_SYMBOLS 默认状况下,当编译一个共享库的时候,遇到未定义符号引用就会报告一个“undefined symbol”错误。这有助于修复你的代码中存在的bug。
若是由于某种缘由,必须禁止该检测,能够把这个变量设置为true。注意,编译出的共享库有可能在加载的时候就报错致使程序退出。
LOCAL_ARM_MODE
LOCAL_ARM_NEON
LOCAL_DISABLE_NO_EXECUTE Android NDK r4增长了对“NX bit“安全特性的支持。它是默认开启的,若是你肯定本身不须要该特性,你能够将它关闭,即:
LOCAL_DISABLE_NO_EXECUTE := true
该变量不会修改ABI,只会在 ARMv6以上的CPU的内核上启用。开启该特性编译出的代码无需修改可运行在老的CPU上(也就是说全部ARM的CPU都能运行)。
参考信息:
http://en.wikipedia.org/wiki/NX_bit http://www.gentoo.org/proj/en/hardened/gnu-stack.xml
LOCAL_EXPORT_CFLAGS 这个变量定义一些C/C++编译器flags。这些flags(标志)会被追加到使用了这个模块(利用LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES)的模块的LOCAL_CFLAGS 定义中去。
假如foo模块的声明以下:
[plain] view plain copy include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo/foo.c
LOCAL_EXPORT_CFLAGS := -DFOO=1
include $(BUILD_STATIC_LIBRARY)
bar模块依赖foo模块,声明以下: [plain] view plain copy include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar.c
LOCAL_CFLAGS := -DBAR=2
LOCAL_STATIC_LIBRARIES := foo
include $(BUILD_SHARED_LIBRARY)
所以在编译bar模块的时候,它的编译器标志就是 “-DFOO=1 -DBAR=2”。 导出的flags 加上本模块的 LOCAL_CFLAGS,成为最后传给编译器的flags。这样修改起来就很容易。这种依赖关系是可传递的,例如,若是zoo依赖bar,bar依赖foo,那么zoo就会同时有bar和foo的导出flags。 注意,编译模块自身时,不会使用它所导出的flags。例如在编译上面的foo模块时, -DFOO=1 不会传递给编译器。
LOCAL_EXPORT_CPPFLAGS 与 LOCAL_EXPORT_CFLAGS 相同,是跟C++相关的标志。
LOCAL_EXPORT_C_INCLUDES 与 LOCAL_EXPORT_CFLAGS 相同,可是只用于头文件搜索路径。当你的共享库有多个模块,并且互相之间有头文件依赖时有用。用法详见 Import Module。
LOCAL_EXPORT_LDLIBS 与 LOCAL_EXPORT_CFLAGS 相同,可是只用于链接器的flag。注意这里被导人的连接器标志将追加到模块的 LOCAL_LDLIBS。
例如当foo模块是一个静态库而且代码依赖于系统库时,该变量很是有用。 LOCAL_EXPORT_LDLIBS 能够用于导出该依赖:
[plain] view plain copy include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo/foo.c
LOCAL_EXPORT_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar.c
LOCAL_STATIC_LIBRARIES := foo
include $(BUILD_SHARED_LIBRARY)
此处在编译bar模块的时候,它的连接器标志将加上一个 -llog,表示它依赖于系统提供的 liblog.so,由于它依赖 foo 模块。 LOCAL_FILTER_ASM 这个变量指定一个shell命令,用于过滤LOCAL_SRC_FILES 中列出的汇编文件或者LOCAL_SRC_FILES列出的文件所编译出的汇编文件。定义该变量后,将致使如下行为:
1)全部的C/C++源码首先翻译为临时汇编文件(若是不定义LOCAL_FILTER_ASM,则C/C++源码直接编译为 obj 文件) 2)这些汇编文件被传给 LOCAL_FILTER_ASM 所指定的shell命令处理,获得一批新的汇编文件。 3)这些新的汇编文件再被编译成obj文件。
换句话说,若是你定义:
[plain] view plain copy LOCAL_SRC_FILES := foo.c bar.S
LOCAL_FILTER_ASM := myasmfilter
则foo.c首先传给编译器(gcc),获得 foo.S.orignal,而后这个 foo.S.original 被传给你指定的过滤器(LOCAL_ASM_FILTER),获得 foo.S,而后再传给汇编器(例如as),获得 foo.o。 bar.S 直接传给过滤器获得 bar.S.new,而后再传给汇编器,获得 bar.o。即在从*.S到*.o的编译流程中增长了一个过滤的环节。 过滤器必须是独立的shell命令,输入文件做为它的第一个命令行参数,输出文件做为第二个命令行参数,例如:
[plain] view plain copy myasmfilter $OBJS_DIR/foo.S.original $OBJS_DIR/foo.S myasmfilter bar.S $OBJS_DIR/bar.S