http://www.spridu.cn/article/android-dev/2011/09/05/android-ndk-r5ZhongWenFanYi.shtml
简介:
-------------
本文档介绍了Android NDK用Android.mk描述你的C/C++源文件.
假设你之前阅读docs/OVERVIEW.html文件,了解了一些其作用和用法.
概述:
---------
Android.mk文件是用来描述你的源代码如何构建系统.
具体来说:
- 这个文件只是GNU Makefile的一部分,在构建系统时它将被解析多次.
因此,您应尽量减少声明的变量那里,不要以为不会在解析定义任何东西。
- 文件的语法设计,允许把你的源分类在'modules'.一个模块是下列之一:
- 静态库
- 共享库
只有共享库将被安装/复制到您的应用程序包。
静态库可用于生成共享库。
你可以定义一个或多个模块在每个Android.mk文件,
你能使用相同的源文件在多个模块中
- 编译系统为您处理了许多细节。例如,你不需要在Android.mk中列出头文件或
依赖的文件.ndk build会自动帮你搜索。
意味着,当更新到新版本的NDK,你能尽可能的体会到它的好处,
你几乎不用修改你的Android.mk文件
注意:这语法是随着Android平台一起开源的.虽然编译系统实现使用他们是不同的,
这是作出一个故意设计的,使开发人员重复使用的外部库的更容易。
一个简单的例子:
---------------
在描述详细的语法之前,让我们思考一个简单
“你好hello JNI的”的例子,文件在:
apps/hello-jni/project
在这里,我们可以看到:
- Android项目的'src'的目录下包含了Java源代码
- 在'jni的'目录包含的本地源代码, 'jni/hello-jni.c'
该源文件实现简单的共享库,
实现了一个本地方法返回一个字符串
给虚拟机的应用程序.
- 在'jni/Android.mk'文件中,它描述了NDK是如何构建共享库的.其内容为:
---------- cut here ------------------
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
---------- cut here ------------------
现在,让我们解释一下这几行:
LOCAL_PATH := $(call my-dir)
Android.mk文件必须在开始为LOCAL_PATH变量的定义.
它用于在开发树中查找源代码文件.在这个例子中,
宏函数'my-dir',由编译系统提供的,用于返回
当前目录的路径(即目录包含Android.mk文件本身)。
include $(CLEAR_VARS)
CLEAR_VARS变量是由编译系统提供的,并指向一个特别GNU的make文件,
将清除你很多LOCAL_XXX变量
(例如LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等..)
LOCAL_PATH例外.这是必要的,
因为所有生成都在单个GNU Make中执行,它要分析控制文件位置所有变量,
这些变量都是全局的,保证唯一.
LOCAL_SRC_FILES := hello-jni.c
在Android.mk文件中LOCAL_MODULE变量必须明确定义每个模块。
该名称必须是唯一的,不包含任何空格.
请注意,编译系统将自动添加适当的前缀和后缀添加到相应的生成文件.
换句话说,一个共享库模块命名为'foo'将生成'libfoo.so'。
重要注意事项:
如果你给你的模块命名为'libfoo',编译系统不会在添加一个'lib'前缀,
这样会产生libfoo.so,这是为了支持来自Android.mk文件.
LOCAL_SRC_FILES:=您好,jni.c
LOCAL_SRC_FILES变量必须包含的C/C++源文件列表,这些模块将被加入
一个模块组装.请注意在这里您不用列出包含的头文件,因为生成系统将
自动为您计算依赖关系,只要列出源文件即可.
请注意,C++源文件的默认扩展名是'.cpp'.这是
可以通过LOCAL_CPP_EXTENSION变量来定义.
不要忘了那个点(即'.cxx'会工作,但'cpp'将不工作)
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY是由编译系统提供的变量,它指向GNU的Makefile脚本,
在负责收集所有的LOCAL_XXX定义的最新信息
'include $(CLEAR_VARS)',并确定如何正确的去编译.
BUILD_STATIC_LIBRARY生成一个静态库。
在示例目录下有复杂的示例,你可以打开Android.mk来看看.
参考:
----------
变量您应该定义在Android.mk中.你可以定义你自己使用的其他变量,
但NDK编译系统保留以下变量名:
- 名称以LOCAL_(如 LOCAL_MODULE)开始的
- 名称以PRIVATE_,NDK_或APP_(内部使用)开始的
- 小写名称(内部使用,例如:'my-dir')
如果你需要定义使自己方便的变量,我们建议使用MY_前缀,一个简单的例子:
---------- cut here ------------------
MY_SOURCES := foo.c
ifneq ($(MY_CONFIG_BAR),)
MY_SOURCES += bar.c
endif
LOCAL_SRC_FILES += $(MY_SOURCES)
---------- cut here ------------------
NDK提供的变量:
- - - - - - - - - - - -
在你的Android.mk被解析之前,这些变量是被GNU Make定义的.
请注意,在某些情况下,NDK会解析Android.mk多次
解析的NDK可能具有不同的Android.mk几次,这些变量可能都有
不同的定义
CLEAR_VARS
指向编译脚本取消几乎所有的LOCAL_XXX变量,
下面一节列出的“Module-description”的说明.
在一个新模块中,你必须包含它在开始:
include $(CLEAR_VARS)
BUILD_SHARED_LIBRARY
指向一个编译脚本,收集所有LOCAL_XXX有关的信息,然后
确定如何去编译.之前必须有LOCAL_MODULE和LOCAL_SRC_FILES定义,
示例:
include $(BUILD_SHARED_LIBRARY)
请注意,这会产生名为lib$(LOCAL_MODULE).so的文件
BUILD_STATIC_LIBRARY
不同于BUILD_SHARED_LIBRARY,用于编译一个静态库.
静态库不会复制到您的项目/包中,但可以用于构建共享库
(见LOCAL_STATIC_LIBRARIES和LOCAL_STATIC_WHOLE_LIBRARIES所述)
示例:
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变量
(更多的信息见 docs/PREBUILTS.html)
PREBUILT_STATIC_LIBRARY
类似于PREBUILT_SHARED_LIBRARY,它是针对静态库。
更多信息,请参阅文档docs/PREBUILTS.html。
TARGET_ARCH
目标CPU架构名,因为它是指定的完整的Android开源版本.
这是为了在不同CPU架构编译.
TARGET_PLATFORM
指定目标Android平台名,当Android.mk被解析。
例如,'android-3'对应Android 1.5系统映像。
Android平台名是怎么与版本对应的,
请阅读docs/STABLE-APIS.html.
TARGET_ARCH_ABI
指定在目标CPU+ABI名字,当Android.mk被解析。
目前支持两个值:
armeabi
For Armv5TE
armeabi-v7a
注:截至到Android NDK 1.6_r1,这个变量是简单地定义
为'arm',现在,这个值已被重新定义,以更好的知道
在Android内部使用的是什么
有关架构的ABI及相应的详细资料
请阅读文档docs/CPU-ARCH-ABIS.html
在未来版本的NDK中将有不同的名字。
请注意,所有ARM-based ABIs
有'TARGET_ARCH'定义为'arm',
但可能有不同'TARGET_ARCH_ABI'
TARGET_ABI
Android和ABI连接,它是定义$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI),
它是有用的针对一个特定的测试目标系统映像的真实设备。
默认情况下,'android-3-armeabi'
(截至到Android NDK 1.6_r1,默认使用'android-3-arm')
NDK提供的宏:
- - - - - - - - - - - - - - -
以下是GNU Make 'function'宏,通过使用'$(call <function>)'。它返回文本信息。
my-dir
返回最后包含Makefile文件的路径,通常是Android.mk的目录。
LOCAL_PATH := $(call my-dir)
重要提示:由于GNU make的工作方式,真正返回的路径是最后一个
包含Makefile文件的路径。所以,请不要包含另一个文件后。
例如,考虑下面的例子:
LOCAL_PATH := $(call my-dir)
...声明一个模块
include $(LOCAL_PATH)/foo/Android.mk
LOCAL_PATH := $(call my-dir)
...另一个模块声明
这里的问题是,第二次调用'my-dir'将返回$PATH/foo.
出于这个原因,最好把其他一切包含在最后一个Android.mk,如:
LOCAL_PATH := $(call my-dir)
... 声明一个模块
LOCAL_PATH := $(call my-dir)
... 另一个模块声明
# 特别的包含在最后一个Android.mk
include $(LOCAL_PATH)/foo/Android.mk
如果这是很不方便,保存第一次my-dir的值在一个变量,然后供其他地方调用:
MY_LOCAL_PATH := $(call my-dir)
LOCAL_PATH := $(MY_LOCAL_PATH)
... 声明一个模块
include $(LOCAL_PATH)/foo/Android.mk
LOCAL_PATH := $(MY_LOCAL_PATH)
... 另一个模块声明
all-subdir-makefiles
当前'my-dir'路径下列出所有子目录位置.
例如,考虑以下层次:
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的路径
grand-parent-makefile
你猜怎么着...
import-module
这个功能是通过名字找到包含Android.mk的另一个模块.
一个典型的例子是:
$(call import-module,<name>)
这将列表中查找标签<名称>中的模块
更多详情请阅读docs/IMPORT-MODULE.html
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)脚本之前。
默认情况下,模块名称决定了生成的文件名称,
例如:lib<foo>.so。对于一个共享库命名为<foo>.
你可以参考其他模块正常生成文件的名称(或Android.mk
或Application.mk)
您可以覆盖默认的LOCAL_MODULE_FILENAME(见下文)
LOCAL_MODULE_FILENAME
这个变量是可选的,允许您重新定义生成的文件的名称.
默认情况下,模块<foo>将生成一个静态库名字为lib<foo>.a
或者一个共享库lib<foo>.so,这是标准的Unix规定.
你可以通过覆盖定义LOCAL_MODULE_FILENAME,例如:
LOCAL_MODULE := foo-version-1
LOCAL_MODULE_FILENAME := libfoo
注意:您不应该把一个路径或文件扩展名在LOCAL_MODULE_FILENAME上,
它们将自动被编译系统处理.
LOCAL_SRC_FILES
这里列出要编译源文件.只列出将被传递到编译器的文件,
编译系统会自动帮你查找依赖关系.
请注意,所有源文件是相对于LOCAL_PATH,例如:
LOCAL_SRC_FILES := foo.c \
toto/bar.c
注意:编译文件始终使用Unix-style的正斜杠(/).
Windows风格的反斜线将不会得到正确处理.
LOCAL_CPP_EXTENSION
这个选项用来定义C++源文件扩展名.默认是'.cpp'
但你可以改变它。例如:
LOCAL_CPP_EXTENSION := .cxx
LOCAL_C_INCLUDES
一个可选列表的路径,相对于NDK根目录,
这将被追加到包含搜索路径时编译来源
(C语言,C++和汇编).例如:
LOCAL_C_INCLUDES := sources/foo
或者:
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
这些放在任何相应LOCAL_CFLAGS / LOCAL_CPPFLAGS的前面
LOCAL_C_INCLUDES路径自动使用ndk-gdb的本地调试。
LOCAL_CFLAGS
可选编译器标志描述何时编译C/C++源文件.
这是指定的宏定义编译选项。
重要事项:尽量不要在你的Android.mk更改优化/调试级别,
Application.mk可以自动处理您所指定适当的信息,
让NDK产生有用的调试过程中使用的数据文件。
注意:在android-ndk-1.5_r1,这个标志只使用于C,不适用于C/++.
这已得到纠正,以匹配Android编译系统的行为.
(您现在可以使用LOCAL_CPPFLAGS指定为C++)。
它可以指定额外的包含路径LOCAL_CFLAGS += -I<path>,
但是,它是最好使用LOCAL_C_INCLUDES,
对于这一点,因为路径将在本机也可使用ndk-gdb调试程序
LOCAL_CXXFLAGS
LOCAL_CPPFLAGS的别名.注意,使用此标志是过时的,
因为在未来的NDK版本它可能会消失.
LOCAL_CPPFLAGS
可选的编译器标志设置将要通过时,只编译c++源文件.
他们将出现在编译器的命令行。
注意:在android-ndk-1.5_r1,标志适用于
C和C++.这已得到纠正.以匹配Android编译系统的行为.
(您现在可以使用LOCAL_CPPFLAGS指定为C++)。
LOCAL_STATIC_LIBRARIES
链接静态库的列表(BUILD_STATIC_LIBRARY)
LOCAL_SHARED_LIBRARIES
链接共享库的列表,这个是必要的,因为会嵌入并生成对应的文件
LOCAL_LDLIBS
编译你的模块时,额外的链接标志.
可以通过模块的名字来链接,使用'-l'前缀.
例如,以下将告诉链接器生成一个模块链接到/system/lib/libz.so
LOCAL_LDLIBS := -lz
参见文档:docs/STABLE-APIS.html 哪些你不需要链接
LOCAL_ALLOW_UNDEFINED_SYMBOLS
默认情况下,你未引用但是却试图建立一个共享库会出现“未定义的符号”错误。
这是一很大的帮助,去捕捉你的源代码中的错误。
但是,如果由于某种原因,你需要禁用这一检查,请将此
变量为'true'。请注意,相应的共享库可能会在运行时加载失败.
LOCAL_ARM_MODE
默认情况下,ARM目标二进制文件将生成'thumb'模式,
其中每个指令都是16位宽。您可以定义该变量'arm' ,
如果你想强制模块的目标文件为'arm'(32位指令)模式。例如:
LOCAL_ARM_MODE := arm
请注意,您也可以指示编译系统只建立特定在ARM模式源通过追加一个'.arm'它的源文件后缀.
例如,使用:
LOCAL_SRC_FILES := foo.c bar.c.arm
告诉编译系统也编译'bar.c'在ARM模式,并
根据LOCAL_ARM_MODE编译foo.c
注意:在Application.mk中设置APP_OPTIM为'debug'会强制生成ARM二进制模式.
这种在工具链中的bug不太好用thumb代码处理
LOCAL_ARM_NEON
这个变量定义为'true'允许在C/C++源码中使用ARM高级SIMD
(a.k.a. NEON),以及NEON指令集文件中.
你应该只定义'armeabi-v7a' ABI符合ARMv7的设置.
请注意,并非所有的ARMv7基于CPU支持的NEON指令集扩展,
而且您应执行运行时的检测,以便能够在运行时使用这个代码安全。
要了解更多,请阅读docs/CPU-ARM-NEON.html和docs/CPU-FEATURES.html.
另外,您也可以指定只有特定的源文件
可能是编译的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'后缀之后.
(即foo.c.arm.neon,但不能为foo.c.neon.arm)
LOCAL_DISABLE_NO_EXECUTE
Android NDK r4的增加"NX bit"安全功能的支持。
这是默认启用,但是你可以禁用它。
需要通过设置此变量为'true'。
注意:此功能不修改ABI,只启用内核对象的ARMv6 + CPU的设备。
生成的机器码启用此功能在设备上运行不修改运行早期的CPU架构。
有关详细信息,请参阅:
http://en.wikipedia.org/wiki/NX_bit
http://www.gentoo.org/proj/en/hardened/gnu-stack.xml
LOCAL_EXPORT_CFLAGS
定义这个变量来记录的C/C++编译器标志.
将添加到所有LOCAL_CFLAGS定义中使用
LOCAL_STATIC_LIBRARIES或LOCAL_SHARED_LIBRARIES。
例如,考模块'foo'定义:
include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo/foo.c
LOCAL_EXPORT_CFLAGS := -DFOO=1
include $(BUILD_STATIC_LIBRARY)
而另一个模块,名为'bar',依赖于'foo':
include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar.c
LOCAL_CFLAGS := -DBAR=2
LOCAL_STATIC_LIBRARIES := foo
include $(BUILD_SHARED_LIBRARY)
这时,标志'-DFOO=1 -DBAR=2'将被传递给编译器
编译生成bar.c
导出标志预置到你的模块的LOCAL_CFLAGS,这样你就可以轻松覆盖它们。
他们也传递性:如果'zoo'取决于
'bar'就依赖于'foo',这下'zoo'也将继承所有标志依赖于'foo'。
最后,编译模块不使用导出标志。
在上述例子中,-DFOO=1就不会被传递到编译器,foo/foo.c.
LOCAL_EXPORT_CPPFLAGS
类似于LOCAL_EXPORT_CFLAGS,这是对于C++的
LOCAL_EXPORT_C_INCLUDES
类似LOCAL_EXPORT_CFLAGS,但是为C的包含路径。
这样你能使用有条件选择'bar.c'包含的头文件给'foo'
LOCAL_EXPORT_LDLIBS
类似LOCAL_EXPORT_CFLAGS,但链接标记不同。
请注意链接标志将被追加到你的模块的LOCAL_LDLIBS
但是,由Unix链接器完成
这通常是非常有用的,模块'foo'是一个静态库,并依赖于一个系统库。
然后可以LOCAL_EXPORT_LDLIBS导出依赖。例如:
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)
libbar.so将编译时,将链接-llog在最后,
因为它依赖于'foo'。
LOCAL_FILTER_ASM
shell命令定义这个变量,用于过滤程序集文件,
或LOCAL_SRC_FILES
当它被定义,下列情况:
- 任何C或C++源文件生成到一个临时文件
(而不是被编译成目标文件)
- 任何临时文件,LOCAL_SRC_FILES列出的文件
通过发送LOCAL_FILTER_ASM命令
生成_another_临时文件。
- 这些过滤汇编文件被编译成目标文件。
换句话说,如果你有:
LOCAL_SRC_FILES := foo.c bar.S
LOCAL_FILTER_ASM := myasmfilter
foo.c --1--> $OBJS_DIR/foo.S.original --2--> $OBJS_DIR/foo.S --3--> $OBJS_DIR/foo.o
bar.S --2--> $OBJS_DIR/bar.S --3--> $OBJS_DIR/bar.o
1 对应的编译器
2 过滤器
3 汇编
该过滤器必须是一个独立的shell命令取
名称作为它的第一个参数输入文件,输出的名称
文件作为第二个,如:
myasmfilter $OBJS_DIR/foo.S.original $OBJS_DIR/foo.S
myasmfilter bar.S $OBJS_DIR/bar.S