Android.mk文件的语法详细定义
介绍:
这个文档详细描述了"Android.mk"编译文件的语法规则,这文件被写来描述你的Android
NDK的C和C++源文件。为了理解接下来说明,假设你阅读了说明其作用和用法的"docs/OVERVIEW.TXT"文本。
概要:
一个"Android.mk"文件被用来描述你的编译系统的源文件。更详细地描述:
--文件是一个真实的小的GNU
Makefile片段。它被编译系统分析一次或多次。因此,你应该试着尽量减少在这儿的变量申明。不要假设不会在解释期间定义任何东西。
--文件的语法规则被设计为了允许你组织你的源代码到模块(modules).一个模块是下面中的一个:
----一个静态库
----一个共享库
只有共享库将被安装或复制到你的应用程序包中。然而静态库只能被用来产生共享库。
你能定义一个或多个模块在每个"Android.mk"文件里。你能在几个模块中使用一样源文件。
--编译系统为你处理许多细节。例如,你不需要在你的"Android.mk"文件中列出头文件或明确依赖关系在生成文件之间。NDK编译系统将为你自己计算这些。
这也以为这,当跟新新版的NDK时,你应该能不用出击你的Android.mk文件情况下,收益于新的工具链(toolchain)或平台(platform)的支持。
注意:语法规则是非常近似被使用的一个Android.mk文件,这文件是全开源Android平台代码的分布式的。然而使用它们的编译系统实现是不同的,这是有意设计的决定,为了应用程序开发者被允许更容易的重用外部库的源代码。
简单例子:
在详细描述一个语法规则前,让我们来认识一个简单的"hello JNI"例子,即(i.e)一下文件:
apps/hello-jni/project
这儿,我们能看到:
--"src"目录包含了Java源代码为这个Android工程例子。
--"jni"目录包含了本地源代码为这个例子,即(i.e)"jni/hello-jni.c"
这源代码文件实现一个简单共享库,这库实现一个本地方法返回一个字符给VM应用程序
--"jni/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"是编译系统提供的。它被用来返回当前目录的路径(即(i.e)这个路径包含了"Android.mk"文件自己)。
include $(CLEAR_VARS)
这个"CLEAR_VARS"变量是编译系统提供的。它指出一个指定GNU
Makefile将清理许多"LOCAL_XXX"变量为你。(例
如,"LOCAL_MODULE","LOCAL_SRC_FILES","LOCAL_STATIC_LIBRARIES",等(etc...)),但
处了"LOCAL_PATH"变量。这被需要,因为所有编译控制文件都是在唯一的GNU
Make执行上下文中被剖析,这个执行上下文中所有的变量都是全局的。
LOCAL_MODULE := hello-jni
这个"LOCAL_MODULE"变量必须被定义,来标识你在你的Android.mk文件中描述的每个模块。名字必须是唯一的,同时不能包含任何空白
符。注意编译系统将自动地添加恰达的前缀和后缀到对应产生的文件。换而言之,共享库模块名字是"foo",那产生的为"libfoo.so"文件。
重要的备注:
如果你命名你的模块"libfoo"的名字,编译系统将不加另一个"lib"前缀,也将产生"libfoo.so"名字的文件。这是为了支持来至"Android"平台源代码的"Android.mk"文件,你需要用到这些。
LOCAL_SRC_FILES := hello-jni.c
这个"LOCAL_SRC_FILES"变量必须包含"C"和/或"C++"的源代码文件列表,这个源文件被编译成一个模块。注意你不必列出头文件和包含的文件在这儿,因为编译系统将自动地为你计算依赖关系。只要列出将被直接地传递给编译器的源代码文件。这对你该不错啊。
注意对于“C++”源文件的默认扩展名是".cpp".然而,可以通过定义"LOCAL_CPP_EXTENSION"变量来指定不同的扩展名。不要忘记初始的点(即(i.e.)".cxx"将能工作,但"cxx"不行)。
include $(BUILD_SHARED_LIBRARY)
这个"BUILD_SHARED_LIBRARY"是一个变量,编译系统提供的。这个变量指出一个"GNU
Makefile"脚本负责收集所有的信息,这些信息是你定义在从最近的"include
$(CLEAR_VARS)"开始的LOCAL_XXX变量中,这些变量决定你编译什么和怎样确切实现它。也能"BUILD_STATIC_LIBRARY"来产生一个静态的库。
在实例目录中有更多更复杂的例子,例子中你能看到带有注释的"Android.mk"文件。
参考:
在"Android.mk"文件中,你应该定义和依赖了一组变量。你能为你自己的使用定义其他的变量,但NDK编译系统保留了下面的变量名字:
--带"LOCAL_"开头的命名(例如(e.g.)LOACL_MODULE)
--带"PRIVATE_, NDK_ or APP_"开头的命名(内部使用的)
--小写字符命名(内部使用的,例如(e.g.)"my-dir")
如果你需要在"Android.mk"的文件中定义你自己的方便有用的变量,我们推荐使用"MY_"为前缀。如一个简单的例子:
---------- 裁剪这个 ----------
MY_SOURCES := foo.c
ifneq ($(MY_CONFIG_BAR),)
MY_SOURCES += bar.c
endif
LOCAL_SRC_FILES += $(MY_SOURCES)
---------- 裁剪这个 ----------
因此,我们去这儿:
NDK提供的变量:
这些"GNU
Make"变量是编译系统定义的,在你的"Android.mk"文件被剖析前。注意在某些情况下"NDK"可能会多次剖析你的"Android.mk"文件,对于这些变量一些每次有不同的定义。
CLEAR_VARS
指向一个编译脚本,它擦除了最近地所有的"LOCAL_XXX"变量的定义,这些变量在下面的模块描述("Module_description")章节中列出。你必须包含这个脚本,在一个新模块开始前。例如(e.g.):
inlcude $(CLEAR_VARS)
BUILD_SHARED_LIBRARY
指向一个编译脚本,它收集所有的关于模块的信息,模块在"LOCAL_XXX"变量中提供给你。这个脚本决定怎样编译来自你列表的源代码的一个目标共享
库。注意你必须有"LOCAL_MODULE"和"LOCAL_SRC_FILES"的定义,至少在包含这个文件前。使用例子(Example
usage):
include $(BUILD_SHARED_LIBRARY)
注意这将产生一个名叫"lib$(LOCAL_MODULE).so"的文件。
BUILD_STATIC_LIBRARY
一个"BUILD_SHARE_LIBRARY"的变种被用来编译一个目标静态库。静态库不会被拷贝到你的工程或包中
(project/packages),但他们能被用编译共享库(参考下面的"LOCAL_STATIC_LIBRARIES"
和"LOCAL_STATIC_WHOLE_LIBRARIES"描述)。
使用例子(Example usage):
include $(BUILD_STATIC_LIBRARY)
注意这将产生一个名叫"lib$(LOCAL_MODULE).a"的库文件。
TARTGET_ARCH
目标"CPU"架构的名字,像他是被整个"Android"的开放源代码编译指定的。这儿"arm"是为任何兼容“ARM"编译,不依赖于特定的"CPU"架构修订。
TARGET_PLATFORM
指定出了"Android"平台目标的名字,当这个"Android.mk"文件被剖析时。例子,"Android-3"对应于"Android
1.5"系统映像。为了平台名字的完整的数组和对应的Android系统映像,阅读"docs/STABLE-API.TXT"。
TARGER_ARCH_ABI
为目标"CPU+ABI"的名字,当剖析"Android.mk"文件时。此刻有两个值被支持:
armeabi 为了Arm5TE
armeabi-v7a
注意:直到"Android NDK
1.6_r1",这个变量被简单地定义为"arm"。然而,这个值被重新定义为更好的匹配被"Android"平台内部使用的名字。
为更多关于架构"ABIs"的细节和对应的兼容的说明,请参考"docs/CPU-ARCH-ABIS.TXT"。
另一个目标"ABIs"将被介绍在将来的"NDK"版本中.它将有不同的名字。注意所有基于"ARM"的"ABIs"将有定义为"arm"值的"TARGET_ARCH"。但可以有不同的"TARGET_ARCH_ABI"。
TARGET_ABI
目标平台和"abi"的串联,它真实地定义就像"$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)"。当我们想为真实的设备针对一个特定目标系统的映像测试时很有用。
默认,这个值是"android-3-armeabi"。
(直到"Android NDK 1.6-r1"时,默认值是"android-3-arm")
NDK提供宏函数(NDK-provided function macros):
下面是"GNU Make"函数宏定义。它们必须通过使用"$(call
)"来评估。它们返回文本的信息。
my-dir
返回当前的"Android.mk"文件的目录的路径。路径是相对于NDK编译系统的顶层的。这对于定义"LOCAL_PATH"变量有用在你的"Android.mk"文件的开始,正如(as
with):
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"的路经(即(i.e.)这个函数被调用的地方)
parent-makefile
返回父"Makefile"的路径在包含树中,即(i.e.)包含当前"Makefile"的"Makefile"的路径)
grand-parent-makefile
猜到什么了吧(Guess what...)
模块描述变量(Module-description variables):
下面变量被用来描述你的模块为编译系统。你应该定义它们的一些在"include $(CLEAR_VARS)"和"include
$(BUILD_XXXXX)"之间。像前面写的,$(CLEAR_VARS)是一个脚本,将清除/不定义所有这些变量,除非明确说明在他们的描述中。
LOCAL_PATH
这个变量被用来给出当前文件的路径。你必须定义它在你的"Android.mk"文件的开始,就像:
LOCAL_PATH :=$(call my-dir)
这个变量不被$(CLEAR_VARS)清除。因此每个Android.mk文件只有需要一个定义(如果你定义多个模块在一个文件中)。
LOCAL_MODULE
这个是你模块的名字。它必须在所有的模块名中唯一。它不应该包含任何空字符。你必须定义它在包含任何"$(BUILD_XXXX)"脚本前。
模块名决定产生文件的名字。例如(e.g.)"lib.so"因为共享库模块命名""。然而你应该只参考其
他模块用它们一般名字(例如),在你的"NDK"编译系统文件之中。(无论Android.mk文件还是
Application.mk文件)
LOCAL_SRC_FILES
这是源代码文件的列表,这些源代码文件被编译为了你的模块。所以只有列表的文件将被传递给编译器,因为编译系统自动地为你计算依赖关系。
注意源代码文件名字都是相对于"LOCAL_PATH"的,你能使用路径组件,例如(e.g.):
LOCAL_SRC_FILES := foo.c \
toto/bar.c
注意:总是使用Unix风格(Unix-style)前进的斜线(/)在编译文件中。Windows风格(Window-style)后退斜线将不能正确的处理。
LOCAL_CPP_EXTENSION
这个可选变量能用来定义说明C++源代码文件的文件后缀。这个默认值是".cpp",但你能改变它。例如:
LOCAL_CPP_EXTENSION := .cxx
LOCAL_C_INCLUDES
路径的可选列表,相对于NDK的根目录(NDK *root*
directory),这个将扩展搜索路径,当编译所有的源代码文件(C,C++和汇编(Assembly))。例如:
LOCAL_C_INCLUDES := sources/foo
或甚至:
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
他们被放置在LOCAL_CFLAGS/LOCAL_CPPFLAGS中的任何对应的包含标志前
LOCAL_CFLAGS
这编译器标记的可选的设置,在编译C和C++源代码文件时被传递给编译器。
这对于指定额外宏定义和编译选项是有用的。
重点:不要尝试改变优化或调式级别(optimization/debugging
level)在你的"Android.mk"文件中,能通过在你的"Application.mk"文件中指定的其当信息,来为你自动处理这个事。让NDK产生被调试期间使用的有用的数据文件。
注意:在"android-ndk-1.5_r1"中,对应的标志只应用在C源代码文件上,不对C++文件其作用。这被纠正来匹配整个"Android"编译系统的行为。(你现在只能使用"LOCAL_CPPFALGS"为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
静态库模块的列表(使用了"BUILD_STATIC_LIBRARY"编译的),列表将被被链接到这个模块上。这只在共享库模块中有意义。
LOCAL_SHARED_LIBRARIES
共享库模块的列表,这个模块在运行是依赖这些共享库的模块列表。这在链接时是必须的,为在产生文件中嵌入对应的信息。
注意这个不能追加列表模块到编译图形(the build
graph)。即(i.e.)你仍然应该在你的"Application.mk"文件中,添加共享库模块列表到你的应用程序的请求模块。
LOCAL_LDLIBS
当编译你的模块时,被使用的额外链接标志的列表。这对于传递使用"-l"前缀指定系统库的名字很有用。例如,下面告诉链接器产生一个模块,
这个模块在载入时链接到"/system/lib/libz.so"库上。
LOCAL_LDLIBS := -lz
参考"docs/STABLE-APIS.TXT"文件,为针对这个NDK版本,你能链接到的系统导出的库的列表。
LOCAL_ALLOW_UNDEFINED_SYMBOLS
默认情况下,在尝试编译一个共享库时遇到任何未定义的参考将导致一个未定义符号(undefine
symbol)错误。这对于在你的源代码中找到问题(catch bugs)非常有帮助。
然而,对于有些原因,你需要禁止这种检查,设置这个变量为"true"值。注意对应的共享库可能在运行时加载出错。
LOCAL_ARM_MODE
默认情况下,"ARM"目标二进制将以"thumb"模式来产生,这样每个命令是"16bit"宽。如果你想强制以"arm"模式(32-bit命令)来产生模块对象文件,你能定义这个变量为"arm"。例如(E.g.):
LOCAL_ARM_MODE := arm
注意你也能命令编译系统用"arm"模式只编译指定的源代码,是通过追加".arm"后缀到它的源文件名字。例如(for
expamle):
LOCAL_SRC_FILES := foo.c bar.c.arm
告知编译系统总是用"arm"模式编译"bar.c"文件,根据"LOCAL_ARM_MODE"变量的值来编译"foo.c"。
注意:在你的Application.mk文件中,设置APP_OPTIM为"debug"将也总是强制"ARM"二进制文件的产生。由于问题(bugs),在调试工具链中,不能很好的处理"thumb"编码。
LOCAL_ARM_NEON
定义变量为"true"是允许ARM Andvanced
SIMD(又名(a.k.a)NEON)的使用,在你的C和C++源代码中GCC内建这,即在汇编文件中是"NEON"指令。
当目标了"armeabi-v7a"ABI(对应了ARMv7指令集)时,你只能定义这个变量。注意不是所有ARMv7基础的"CPUs"都支
持"NEON"指令集扩展,你应该执行运行时侦测来能在运行时安全地使用这代码。为了学习关于这更多的,请阅读文本"docs/CPU-ARM-
NEON.TXT"和"docs/CPU-FEATURES.TXT"。
二选一地,你也能在指定只在指定的源代码文件可以使用"NEON"编译,是通过使用".neon"后缀来支持的。如(as
in):
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"后缀的后面,如果两个都有时。(即(i.e.)foo.c.arm.neon能工作,但foo.c.neon.arm不行)
LOCAL_DISABLES_NO_EXECUTE
"Android NDK r4"添加对"NX
bit"安全特性的支持。默认这个是打开的,但我们通过设置这个值为"true"来关闭它,如果你真地需要这样。
注意:这个特性不会修改"ABI",它只能在内核目标为"ARMv6+"的"CPU"设备上其作用。带有这个特性能产生的机器码将在运行着较早期CPU架构的设备上不修改的运行。
对应更多信息,参考: