下面是我个人对android-ndk-r4版本中Android.mk文件做的翻译,由于自己英语水平和专业知识的限制,有些地方可能翻译的不是很准确,敬请指正,本文仅希望对做android NDK开发的同仁们能提供一点点的帮助而已。
Android.mk文件是用来描述你想要编译进系统的资源的。
这个文件的语法允许你把你的资源打包进“modules”。Module应该是下面module中的一种:
- a static library
- a shared library
只有shared library会被打包到你的应用程序中,尽管static library也能用来生产shared library。
你可以在一个Android.mk中定义多个module,你也可以在几个module中使用相同的资源文件。
编译系统会为你处理一些细节。例如,你不需要在你的Android.mk文件中列出你的头文件或是你的文件之间明确的依赖关系。NDK编译系统会自动帮你处理。
这同时意味着,当升级到新版本的NDK时,你不需要动你的Android.mk文件就可以使用心得toolchain/platform支持。
注意,用在Android.mk文件中的语法和整个的android平台的开源资源语法是不同的。编译系统实现了对他们不同的使用,这是故意这样设计的,用来允许应用开发人员更容易地重用外部库资源代码。
一个简单的例子:
在语法详细说明之前,让我们先来看一个简单的“hello-jni”例子,文件在$NDK/samples/hello-jni下:
在这我们可以看到:
-src目录下包含这个简单的android工程的一个java源文件
-jni目录下包含了这个例子的本地源文件
这个源文件实现了一个简单的共享库,这个共享库实现了一个想vm应用程序返回一个字符串的本地方法。
-Android.mk文件向NDK编译系统描述了一个共享库。它的内容如下:
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”宏函数用来返回当前目录的路径(Android.mk文件所在的目录)。
include $(CLEAR_VARS)
编译系统提供的一个指向特殊的GNU Makefile文件的CLEAR_VARS变量将为你清理很多LOCAL_PATH中的LOCAL_XXX变量(例如: LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等)。这是有必要的,因为所有的编译控制文件都会在一个单独的GNU Make可执行上下文中被解析,在这里所有的变量都是全局的。
LOCAL_MODULE := hello-jni
用来标识分布在Android.mk文件中的每一个module的LOCAL_MODULE变量必须定义。名字必须是唯一的而且不能有空格。
注意,编译系统会给相应的生成文件自动地添加适当的前缀和后缀。换句话说,一个命名为“foo”的共享库module将生成名为“libfoo.so”的文件。
重点注意:
如果你的module命名为“libfoo”,编译系统不会为你添加另外一个“lib”前缀,还是会生成一个“libfoo.so”的文件而已。这个为了支持你需要使用的android平台资源的Android.mk文件。
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SRC_FILES变量必须包含一个将要被编译和汇编进module的一个C/C++资源文件列表。注意你不需要列出头文件和包含文件,因为编译系统会自动帮你计算依赖关系;仅仅需要列出直接传递给编译器的资源文件就可以了。
注意:默认的C++资源文件的扩展名是“.cpp”。然而可以通过定义LOCAL_DEFAULT_CPP_EXTENSION变量来指定一个不同的扩展名。不要忘记最开始的dot(例如:.cxx是可以的,但是cxx是不行的)。
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY是编译系统提供的一个指向GNU Makefile脚本的变量,它负责收集从最近的include $(CLEAR_VARS)' 变量开始的所有内定义的 LOCAL_XXX变量的信息,然后决定哪些需要编译以及怎样编译。BUILD_STATIC_LIBRARY变量可以生成一个static library。
在samples目录下有几个复杂的例子,在他们的Android.mk文件中你可以看到一些注释。
Reference:
下面是你可以在Android.mk文件里可以直接使用或是定义的变量的列表。你能够定义其它的变量供自己使用,但是下面的变量名是NDK编译系统的保留字:
- 以LOCAL_ 开头命名的 (例如LOCAL_MODULE)
- 以PRIVATE_,NDK_ 或是 APP_ 命名的(仅供内部使用)
- 以小写字母命名的(仅供内部使用, 例如 'my-dir')
如果你需要在Android.mk文件里定义一个你自己使用比较方便的变量,我们建议使用MY_前缀,这是一个普通的例子:
MY_SOURCES := foo.c
ifneq ($(MY_CONFIG_BAR),)
MY_SOURCES += bar.c
endif LOCAL_SRC_FILES += $(MY_SOURCES)
好,我们接着看:
NDK提供的变量:
在你的Android.mk文件解析之前,编译系统定义了这些GNU Make变量。注意,在某种情况下NDK可能会多次解析你的Android.mk文件,每一次解析某些变量的定义可能不同。
CLEAR_VARS
指向一个几乎所有在"Module-description"部分列出的LOCAL_XXX变量都没有定义的编译脚本(我的理解就是在编译前清空所有变量,然后在这个变量的下面再重新定义)。在开始一个新的module之前你必须包含这个脚本,例如:
include $(CLEAR_VARS)
BUILD_SHARED_LIBRARY
指向一个收集所有你提供的关于module的LOCAL_XXX变量的信息的编译脚本,然后决定如何根据你列出的资源编译一个目标shared library。注意,你在包含这个变量之前必须至少OCAL_MODULE 和LOCAL_SRC_FILES 变量。可以这样使用:
include $(BUILD_SHARED_LIBRARY)
注意它将生成一个以lib$(LOCAL_MODULE).so命名的文件。
BUILD_STATIC_LIBRARY
BUILD_STATIC_LIBRARY变量的不同之处在于它是用来生成一个目标static ibrary 。static ibrary 不会被打包进你的工程包,但是可以用来生成shared library(看下面的LOCAL_STATIC_LIBRARIES 和LOCAL_STATIC_WHOLE_LIBRARIES说明)。可以这样使用:
include $(BUILD_STATIC_LIBRARY)
注意,它将生成一个以lib$(LOCAL_MODULE).a命名的文件。
TARGET_ARCH
指明一个full Android open-source build明确说明的目标CPU架构。如果是“arm”用来所有的兼容ARM的编译,不依赖与CPU架构版本。
TARGET_PLATFORM
当Android.mk文件解析时,指明一个android目标平台。例如"android-3”对应"android1.5"系统镜像。完整的平台名字和android系统镜像的对应列表可以阅读docs/目录下STABLE-APIS.TXT文档。
TARGET_ARCH_ABI
当Android.mk文件解析时,指明一个目标CPU+ABI。有两个值可供使用:
armeabi
对应Armv5TE
armeabi-v7a
注意:Android NDK 1.6_r1之上的版本,这个变量被定义为'arm'。然而,这个变量的重定义能够被Android platform内部更好的使用。
对于更多的ABI架构和对应的兼容性问题的细节,可以阅读docs目录下的CPU-ARCH-ABIS.TXT文档。
其他的ABI架构将会在以后的NDK版本中引入,并且有不同的名字。注意,所有基于ARM的ABIs都会被'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提供的宏函数
下面是GNU Make宏函数,通过使用'$(call <function>)'来获得他的值。它们返回文本信息。
my-dir
返回相对于上层的NDK编译系统的当前Android.mk文件目录的路径。这对于以下面这种方式在你的Android.mk文件的开始定义LOCAL_PATH变量很有用:
LOCAL_PATH := $(call my-dir)
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文件的路径。
Module描述变量
下面的变量是用来向系统描述你的module的。你应该在一个'include $(CLEAR_VARS)'和'一个include $(BUILD_XXXXX)'变量之间定义它们中的某些变量。前面的部分已经介绍过了,$(CLEAR_VARS)变量是取消/清除所有这些变量的定义,除非在他们的描述说明确地注明。
LOCAL_PATH
这个变量用来给出当前文件的路径。你必须在你的Android.mk文件的开始定义它,比如下面这样定义是可以的:
LOCAL_PATH := $(call my-dir)
这个变量不会被$(CLEAR_VARS)清除掉,所以在每一个Android.mk文件中仅需要定义一次(在你的一个文件中定义了几个module的情况下)。
LOCAL_MODULE
你的module的名字。它必须在所有的module名字中是唯一的,而且不能有空格。你必须在任何一个$(BUILD_XXXX)脚本之前定义它。
module名字决定了生成文件的名字,例如:一个名字为lib<foo>.so的shared library的module名字为<foo>。可是,你应该在你的NDK编译文件中(无论是Android.mk还是 Application.mk)进引用其它module的通用的名字(例如<foo>)。
LOCAL_SRC_FILES
这是一个将用于你的module编译的资源文件列表。只用在列表中的文件才会传递给编译器,因为编译系统自动帮你计算依赖关系。
注意,所有的资源文件名字都是相对于LOCAL_PATH的,你能够使用路径分隔符,例如:
LOCAL_SRC_FILES := foo.c \
toto/bar.c
注意:只有使用Unix风格的正斜线(/)在编译文件中才可以,windows风格的反斜线(\)不能被正确处理。
LOCAL_CPP_EXTENSION
这是一个可选择的变量,它能用来定义c++源文件的文件后缀名。默认的是'.cpp',但是你可以改变它。例如:
LOCAL_CPP_EXTENSION := .cxx
LOCAL_C_INCLUDES
一个可选的编译所有源文件(C,C++,汇编)时会添加到include搜索路径相对于NDK *root*文件夹的路径的列表。例如:
LOCAL_C_INCLUDES := sources/foo
或者
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
他们会在LOCAL_CFLAGS / LOCAL_CPPFLAGS中的任何inclusion flag之前被替换。
LOCAL_CFLAGS
当编译 C 和 C++源文件时传递的一组可选的编译器标识。
它可以用来指定额外的宏定义或编译选项。
重要信息:不要在你的Android.mk文件中尝试去改变优化/调试等级,这个可以通过在你的Android.mk文件中指明适当的信息来让系统自动处理,将会使NDK生成在调试中可以使用的有用的数据文件。
注意:在android-ndk-1.5_r1中,对应标记仅能用在c源文件上,不能用在c++源文件上。这能够正确匹配所有的android编译系统行为。(你现在可以使用LOCAL_CPPFLAGS来仅为c++源文件设置标识)。
LOCAL_CXXFLAGS
LOCAL_CPPFLAGS的一个别名。注意,这种标识的使用是过时的,因为在以后的NDK版本中它可能被取消。
LOCAL_CPPFLAGS
当仅编译 C++源文件时传递的一组可选的编译器标识。他们将在LOCAL_CFLAGS之后显示在命令行上。
注意:在android-ndk-1.5_r1中,对应标记在c源文件上和在c++源文件上都能使用。这能够正确匹配所有的android编译系统行为。(你现在可以使用LOCAL_CFLAGS来为c++源文件和c源文件设置标识)。
LOCAL_STATIC_LIBRARIES
将要被链接进这个module的static libraries modules的列表(用BUILD_STATIC_LIBRARY编译过的)。这个仅在shared library modules中能被检测到。
LOCAL_SHARED_LIBRARIES
依赖于运行时的shared libraries *modules*的列表。这在链接时是必要的,为了把对应的信息嵌入到生成的文件中。
注意:他不会把列出的module添加到编译表中,例如:你仍然需要在你的Application.mk文件中把他们添加到你的应用程序需要的module中。
LOCAL_LDLIBS
编译你的module时使用到的额外的链接器标识列表。它能够用一个带 "-l" 前缀的表达式传递一个指明的系统库的名字。例如:下面的定义将会通知编译器在加载的时候生成一个链接到/system/lib/libz.so的module:
LOCAL_LDLIBS := -lz
阅读docs目录下的STABLE-APIS.TXT文档,你将能了解到在这个NDK版本中你可以链接的系统库的列表。
LOCAL_ALLOW_UNDEFINED_SYMBOLS
默认情况下,当尝试编译一个shared library时任何一个没有定义的引用都将产生一个"undefined symbol"错误。这对在你的源代码中捕获bug很有帮助。
可是,如果由于某种原因你需要让这些检查失效,把这个变量设置为'true'。注意,对应的shared library在运行时加载可能出错。
LOCAL_ARM_MODE
默认情况下,ARM的二进制文件将生成每一条指令都是16位的'thumb'模式。你可以定义这个变量为'arm',如果你想强制生成的module目标文件为 'arm'(每条指令32位)模式的。例如:
LOCAL_ARM_MODE := arm
注意,你也可以通过在源文件名后面添加一个'.arm'来指示编译系统只把指定的文件编译'bar.c'。例如:
LOCAL_SRC_FILES := foo.c bar.c.arm
通知编译系统总是把'bar.c'编译成'.arm'模式,而依据LOCAL_ARM_MODE变量的值来编译 foo.c 文件。
注意:在你的Application.mk文件中设置APP_OPTIM为 'debug'也能强制生成ARM二进制文件。这是因为在thumb模式下toolchain debugger 中的bug不能得到较好的处理。
LOCAL_ARM_NEON
把这个变量设置为'true'能够允许你在你的C和C++源码中使用ARM Advanced SIMD(a.k.a. NEON)GCC,也能在汇编文件中使用NEON指令。
你仅仅应该在想要对应ARMv7指令序列的'armeabi-v7a' ABI时定义它。注意,不是所有基于ARMv7的CPU都支持NEON指令序列扩展,你应该开启运行时检查来确保你能安全地使用这些代码在运行期间。更多的关于这些的内容,可以阅读docs目录下的CPU-ARM-NEON.TXT 和CPU-FEATURES.TXT.文档。
二选一,你也可以在指定的文件后面加上'.neon'来指示表仪器把指定的文件编译成NEON支持的文件,例如:
LOCAL_SRC_FILES = foo.c.neon bar.c zoo.c.arm.neon
在这个例子中'foo.c'将被编译成thumb+neon模式,'bar.c'将被编译成'thumb'模式,'zoo.c' 将被编译成'arm+neon'模式。
注意,如果 '.neon' 后缀和'.arm' 后缀都是用,那么 '.neon' 后缀和一定要添加到'.arm' 后缀之后。(例如: foo.c.arm.neon 可以,但是 foo.c.neon.arm 不可以)。
LOCAL_DISABLE_NO_EXECUTE
Android NDK r4添加的用来支持"NX bit"安全特性的。默认情况下是启用的,但是你也可以设置这个变量为 'true'来启用它,如果你真的需要这么做的话。
注意:这个特性不会修改ABI,它仅仅是使内核能够指向ARMv6+ CPU设备。在这种特性下生成的机器码将不需要做修改就能运行在更早的CPU架构上。