Android.mk文件说明

来源:互联网 发布:com域名攻击 编辑:程序博客网 时间:2024/06/09 20:51

以下内容根据官方文档翻译,转载请注明出处。

概述


Android.mk文件位于项目目录下的jni/子文件夹中。它向编译系统描述你的资源文件以及共享库文件。它实际上是一个小型的GNU makefile片段。编译系统在编译过程中会对它进行一次或多次解析。Android.mk文件主要是用于定义在Application.mk、编译系统以及环境变量中所没有定义的项目级别的设定。它还能复写指定模块的项目级别的设定。

Android.mk的语法允许你将源文件组织为模块的形式。一个模块可以是一个静态库、一个共享库或者是一个独立的可执行文件。你可以在每个Android.mk中定义一个或多个模块,也可以在多个模块用使用同一个源文件。编译系统只会把共享库放到你的程序包中。另外,静态库可以生成共享库。

除了打包库文件之外,编译系统还会为你处理许多细节问题。泪如,你不需要在你的Android.mk中列出头文件或列出生成文件的显式的依赖,NDK编译系统会自动为你计算它们之间的依赖关系。这样,当未来的NDK版本中出现新的工具链/平台支持时,你就不需要再改动你的Android.mk文件了。

尽管使用的编译系统不同,这里的语法与Android Open Source项目中所发布的Android.mk文件的语法非常相似。这样做的目的是为了让应用开发人员更容易的服用他们的外部库文件的源代码。

基础知识


在详细了解语法知识之前,最好先对Android.mk文件中包含什么信息有一个基础的了解。这一节使用Hello-JNI示例中的Android.mk文件解释一下每一行都起到了什么作用。

一个Android.mk文件必须以定义LOCAL_PATH变量起始:

LOCAL_PATH := $(call my-dir)

这个变量指示了源文件在开发树中的位置。变量中的宏函数my-dir由编译系统提供,它返回当前目录的路径(Android.mk所在目录)。

下一行声明了CLEAR_VARS变量,它的值由编译系统提供。

include $(CLEAR_VARS)

CLEAR_VARS变量指向一个特定的GNU Makefile文件,它会为你清除许多LOCAL_XXX变量,例如LOCAL_MODULELOCALE_SRC_FILESLOCAL_STATIC_LIBRARIES。记住,它并不清除LOCAL_PATH变量。GNU编译系统在同一个环境下解析所有的编译控制文件,在这里,所有的变量都是全局的,因此,你必须在描述每一个模块之前重新声明CLEAR_VARS

下一行的LOCAL_MODULE变量存储的是你想要构建的模块的名字。应用中的每一个模块都应声明一次该变量。

LOCAL_MODULE := hello-jni

每个模块的名字必须唯一,并且不含有空格。编译系统在生成最终的共享库文件时,会自动为它添加相应的前缀和后缀。例如,上面的例子中,最后生成的库文件名为libhello-jni.so

: 如果你的名字中已经以lib起始。编译系统不会再为它添加lib前缀。因此若模块名为libfoo,最后生成的库文件为libfoo.so

再下一行枚举了所有的源文件,通过空格来分隔多个文件:

LOCAL_SRC_FILES := hello-jni.c

LOCAL_SRC_FILES变量必须包含编译进模块中的C或C++文件列表。

最后一行帮助编译系统将所有一切整合在一起

include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY指向一个GNU Makfile脚本,它会收集你在LOCAL_XXX变量中定义的所有信息。这个脚本决定编译什么以及如何编译。

变量与宏定义


编译系统在Android.mk文件中提供了许多可用的变量。这些变量都有预设的值。

除了这些变量之外,你还能定义自己的变量。当你这么做的时候,需要记住以下名字是被NDK编译系统自己保留的:
* 以LOCAL_开头的名字,如LOCAL_MODULE
* 以PRIVATE_NDK_APP开头的名字,这些名字在编译系统内部使用
* 小写的名字,如my-dir,同样,这些名字在编译系统内部使用。
因此,如果你要在Android.mk中定义自己的变量,最好使用MY_开头。

NDK 预定义变量


这一节讨论编译系统在解析你的Android.mk之前预定义的一些GNU Make变量。在特定环境下,NDK可能会多次解析你的Android.mk文件,每次使用一套不同的变量定义。

CLEAR_VALS

这个变量指向一个脚本,它会解除在”开发者定义变量”中列出的几乎所有LOCAL_XXX变量。每次描述一个新的模块时,使用这个变量来包含这个脚本。

include $(CLEAR_VARS)

BUILD_SHARED_LIBRARY

这个变量指向一个编译脚本,它会收集所有你定义的关于这个模块的LOCAL_XXX变量信息,并决定如何根据你所列出的源文件来编译一个共享库。记住,在使用这个脚本之前,你必须至少定义了LOCAL_MODULE以及LOCAL_SRC_FILES变量。
使用语法为:

include $(BUILD_SHARED_LIBRARY

使用共享库变量会让编译系统生成一个.so文件。

BUILD_STATIC_LIBRARY

BUILD_STATIC_LIBRARY用于生成静态库。编译系统不会将静态库拷贝到你的项目/包中。但是它可以用于生成共享库文件(参考LOCAL_STATIC_LIBRARIES以及`LOCAL_WHOLE_STATIC_LIBRARIES)。

include $(BUILD_STATIC_LIBRARY)

使用静态库变量会让编译系统生成一个.a库文件。

PREBUILT_SHARED_LIBRARY

指向一个用于指定已编译好的共享库文件的编译脚本。与BUILD_SHARED_LIBRARY以及BUILD_STATIC_LIBRARY不同,这里的LOCAL_SRC_FILES不能是源文件,而应该是指向一个单独的已编译好的共享库文件,如foo/libfoo.so。使用语法如下:

include $(PREBUILT_SHARED_LIBRARY)

你也能通过使用LOCAL_PREBUILTS变量指向另一个模块预先编译好的库文件。关于使用预编译库的跟多信息,可参考使用预编译库

PREBUILT_STATIC_LIBRARY

PREBUILT_SHARED_LIBRARY相同,但是应用于静态库文件。

TARGET_ARCH

由Android Open Source Project给出的目标CPU架构的名字。对于任意ARM兼容的构建,使用arm,独立于ABI变量所指定的CPU架构版本(参考下面的TARGET_ARCH_ABI)。

TARGET_PLATFORM

编译系统目标Android API等级。例如,Android 5.1系统对应的是Android API 22:android-22。关于完整的对应信息,查看Android NDK Native APIs。使用示例:

TARGET_PLATFORM := android-22

TARGET_ARCH_ABI

这个变量存储目标CPU与架构的信息。你可以指定一个或多个下表中的值,通过空格分隔。
表1. ABI设定与不同的CPU架构

CPU and architecture Setting ARMv5TE armeabi ARMv7 armeabi-v7a ARMv8 AArch64 arm64-v8a i686 x86 x86-64 x86_64 mips32(r1) mips mips64(r6) mips64 All all

以下示例表示设定目标为 ARMv8 AArch64:

TARGET_ARCH_ABI := arm64-v8a

注: 直到Android NDK 1.6_r1为止,这个变量都被定义为arm

关于架构ABI以及相关的兼容问题,参考ABI Management。

TARGET_ABI

目标Android API等级与ABI的串联信息。在你希望在一个真实设备上进行测试时非常有用。例如,可以指定一个运行于Android API level 22下的64位ARM设备。

TARGET_ABI := android-22-arm64-v8a

注:直到Android NDK 1.6_r1为止,它的默认值都是android-3-arm

模块描述变量


本节中描述的变量用于向编译系统描述你的模块。每一个模块描述都应遵循以下流程:
1. 使用CLEAR_VARS初始化变量
2. 为模块相关变量赋值
3. 使用BUILD_XXX变量编译模块

LOCAL_PATH

定义当前文件路径。你必须在Android.mk开头定义这个变量。

LOCAL_PATH := $(call my-dir)

CLEAR_VARS不会清除这个变量的值。因此,若你在Android.mk中描述了多个模块,只需要定义一次即可。

LOCAL_MODULE

模块名,必须唯一并且不含空格。你必须在包含任意脚本前定义该变量(除了CLEAR_VARS)。你不需要添加lib前缀或.a.so后缀。

LOCAL_MODULE := "foo"

如果你需要指定生成文件的名字,而不是系统自动生成。你可以使用LOCAL_MODULE_FILENAME变量。

LOCAL_MODULE_FILENAME

可选变量,用于替换系统自动生成的文件名,如下例,会生出libnewfoo文件而不是libfoo文件。

LOCAL_MODULE := fooLOCAL_MODULE_FILENAME := libnewfoo

对于共享库文件,示例所生成的文件名为libnewfoo.so

注: 你不能更改文件路径或拓展名

LOCAL_SRC_FILES

指定生成模块所需的文件列表。只需列出编译系统实际进行编译的文件,因为编译系统会自动计算相关的依赖。
既可以使用相对路径(相对于LOCAL_PATH),也可以使用绝对路径。
建议使用相对路径以便于移植。

注: 系统无法识别Windows形式的斜杠(\),只能识别Unix格式的斜杠(/)

LOCAL_CPP_EXTENSION

可选变量,用于指定非.cpp后缀作为C++源文件的后缀。例如,你可以使用.cxx作为文件后缀(必须包含点)。

LOCAL_CPP_EXTENSION := .cxx

从NDK r7开始,你可以使用多种类型的格式,如

LOCAL_CPP_EXTENSION := .cxx .cpp .cc

LOCAL_CPP_FEATURES

可选变量,指定代码所依赖的C++特性。它会在编译过程中启用正确的编译与链接选项。对于预编译二进制文件,这个变量同样声明了二进制文件所依赖的特性,以便于保证最终链接顺利。建议使用该变量而不是直接在LOCAL_CPPFLAGS中启用-frtti-fexceptions
使用LOCAL_CPP_FEATURES允许编译系统为每一个模块提供合适的编译选项,而LOCAL_CPPFLAGS会导致编译器为所有的模块使用同一套编译选项,而不考虑实际所需。
例如,指定使用RTTI(运行时类型信息)

LOCAL_CPP_FEATURES := rtti

指定使用C++异常

LOCAL_CPP_FEATURES := exceptions

你也可以因此指定多个值,如

LOCAL_CPP_FEATURES := rtti features

指定顺序没有影响

LOCAL_C_INCLUDES

可选变量,指定头文件所在目录,相对于NDKroot的路径,如:

LOCAL_C_INCLUDES := sources/foo

或者

LOCAL_C_INCLUDES := $(LOCAL_PATH)//foo

你必须在使用LOCAL_CFLAGSLOCAL_CPPFLAGS进行任何相应的包含之前定义该变量。
ndk-gdb调试同样会自动调用LOCAL_C_INCLUDES信息。

LOCAL_CFLAGS

可选变量,设置在编译时使用的编译标志。例如添加额外的宏定义,或编译选项。
尽量不要再Android.mk中更改优化/调试级别。编译系统会根据Application.mk中的信息自动处理相关设定。

注:在android-ndk-1.5_r1中,这个标志只会用于C源文件,而不会用于C++。

你可以通过它指定额外的头文件路径:

LOCAL_CFLAGS += -I<path>,

但是最好还是通过LOCAL_C_INCLUDES来进行,以便于调试。

LOCAL_CPPFLAGS

可选,设置在编译时仅用于C++源文件的编译选项。这些编译选项在编译时会显示在LOCAL_CFLAGS之后。

注: 在android-ndk-1.5_r1中,这个标志同时应用于C与C++文件。

LOCAL_STATIC_LIBRARIES

指定当前模块所依赖的静态库。
若当前模块为共享库或可执行文件,这个变量所指定的库会被强制链接到生成的二进制文件中。
若当前模块为静态库,则该变量只是指示当前库所依赖的库列表。

LOCAL_SHARED_LIBRARIES

当前模块运行所需的共享库。该信息在链接时所使用,并被嵌入到生成的二进制文件中。

LOCAL_WHOLE_STATIC_LIBRARIES

这个变量为LOCAL_SHARED_LIBRARIES的变种。主要用于解决循环依赖问题。

LOCAL_LDLIBS

这个变量包含构建共享库或可执行文件所需的额外链接选项。它能让你用-l前缀指定想要链接的系统库。例如,你可以通过如下示例告诉链接器生成一个在启动时链接到/system/lib/libz.so库的模块:

LOCAL_LDLIBS := -lz

关于可用的系统库列表,参考Android NDK Native APIs。

注:若你定义的模块是一个静态库,编译系统会忽视它,并输出一个警告信息。

LOCAL_LDFLAGS

编译时使用的其它链接标志。如在 ARM/X86 GCC 4.6+ 上使用ld.bfd链接器而不是默认的ld.gold

LOCAL_LDFLAGS += -fuse-ld=bfd

注:若你定义的 模块 是一个静态库,编译系统会忽视它,并输出一个警告信息。

LOCAL_ALLOW_UNDEFINED_SYMBOLS

是否允许未定义的符号变量存在。

注:若你定义的 模块 是一个静态库,编译系统会忽视它,并输出一个警告信息。

LOCAL_ARM_MODE

编译系统默认生成thumb模式的二进制文件,即指令为16位宽度,并链接thumb/目录下的STL库。将这个变量设定为arm会强制编译系统生成32位的arm模式文件。

LOCAL_ARM_MODE := arm

你也可以针对单独的源文件提供该编译选项,如下例中的bar.c会一直以ARM模式编译,而foo.c的编译模式则取决于LOCAL_ARM_MODE

LOCAL_SRC_FILES := foo.c bar.c.arm

注:Application.mk中将APP_OPTIM设置为debug同样可以启用ARM模式,因为调试器无法正确处理Thumb代码。

LOCAL_ARM_NEON

这个参数只有在目标为armeabi-v7a时才有影响。它允许你在C和C++源码中使用ARM Advanced SIMD(NEON) GCC函数,以及在汇编文件中使用NEON指令。
记住这点,并不是所有的ARMv7架构的CPU都支持NEON指令集拓展。因此,你必须在运行时检测是否能够安全的使用这些代码。更多信息,请参考NEON Support与The cpufeatures Library。
同样,你可以针对指定源文件启用NEON支持:

LOCAL_SRC_FILES = foo.c.neon bar.c zoo.c.arm.neon

同时指令两个后缀时,.arm必须在.neon之前。

LOCAL_DISABLE_NO_EXECUTE

未了解

LOCAL_DISABLE_RELRO

未了解

LOCAL_DISABLE_FORMAT_STRING_CHECKS

编译系统编译代码时,默认提供字符串格式化保护。当使用printf之类的函数格式化一个非常量的字符串时会引发一个编译器错误。
保护默认打开,可通过设置该值为true进行关闭。没有无法避免的理由不建议这么做。

LOCAL_EXPORT_CFLAGS

该变量记录的编译选项会被添加到通过LOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIES使用该模块的其它模块的LOCAL_CFLAGS中。
如下例:

include $(CLEAR_VARS)LOCAL_MODULE := fooLOCAL_SRC_FILES := foo/foo.cLOCAL_EXPORT_CFLAGS := -DFOO=1include $(BUILD_STATIC_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := barLOCAL_SRC_FILES := bar.cLOCAL_CFLAGS := -DBAR=2LOCAL_STATIC_LIBRARIES := fooinclude $(BUILD_SHARED_LIBRARY)

bar.c编译时会同时启用-DFOO=1以及-DBAR=2
另外,这种模块间的关系是可传递的。若zoo依赖于bar,则zoo也会继承foo中导出的标志位。
最后,导出的变量在编译本模块时是不是用的。因此,编译foo时,-DFOO=1并未被传递给编译器。

LOCAL_EXPORT_CPPFLAGS

LOCAL_EXPORT_CFLAGS相同,但仅用于C++标志。

LOCAL_EXPORT_C_INCLUDES

LOCAL_EXPORT_CFLAGS相同,用于头文件包含路径。

LOCAL_EXPORT_LDFLAGS

导出链接器设置

LOCAL_EXPORT_LDLIBS

导出需要链接的系统库,例如

include $(CLEAR_VARS)LOCAL_MODULE := fooLOCAL_SRC_FILES := foo/foo.cLOCAL_EXPORT_LDLIBS := -lloginclude $(BUILD_STATIC_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := barLOCAL_SRC_FILES := bar.cLOCAL_STATIC_LIBRARIES := fooinclude $(BUILD_SHARED_LIBRARY)

在上例中,编译系统在构建libbar.so时,会将-llog放到链接命令的最后。因为·libbar.so·依赖于foo,而foo依赖于系统日志库。

LOCAL_SHORT_COMMANDS

主要用于Windows系统。Windows系统命令行最多只能接收8191个字符,当项目非常复杂时,编译命令往往会超出这个限制。将这个变量设为true可以缩短编译命令。
可以在Application.mk中定义APP_SHORT_COMMANDS以在所有模块中启用该行为。
不建议默认开启该特性,因为它会导致编译速度变慢。

LOCAL_THIN_ARCHIVE

用于静态库,可减小库文件大小,未做详细了解

LOCAL_FILTER_ASM

未做了解

NDK提供的函数宏


本节介绍NDK提供的GNU Make函数宏。使用$(call <function>)来执行它们,它们会返回文本形式的信息。

my-dir

返回最近一次包含的makefile文件的路径,一般是当前Android.mk文件的路径。在Android.mk文件开头定义LOCAL_PATH时,my-dir非常有用。

LOCAL_PATH := $(call my-dir)

基于GNU Make的工作方式,这个宏返回的实际上是编译系统在解析构建脚本时所引入的最后一个makfile文件的路径。因此,你不应该在包含另一个文件之后再调用my-dir
例如:

LOCAL_PATH := $(call my-dir)# ... declare one moduleinclude $(LOCAL_PATH)/foo/`Android.mk`LOCAL_PATH := $(call my-dir)# ... declare another module

这里的问题在于,当第二次调用my-dir来定义LOCAL_PATH时,实际上反悔的是$PATH/foo而不是$PATH,因为它才是最近所包含的文件。
你可以将额外文件的包含放在最后来避免这个问题,如:

LOCAL_PATH := $(call my-dir)# ... declare one moduleLOCAL_PATH := $(call my-dir)# ... declare another module# extra includes at the end of the Android.mk fileinclude $(LOCAL_PATH)/foo/Android.mk

如果这个方法不太方便,可以将my-dir的值存入另一个变量,例如:

MY_LOCAL_PATH := $(call my-dir)LOCAL_PATH := $(MY_LOCAL_PATH)# ... declare one moduleinclude $(LOCAL_PATH)/foo/`Android.mk`LOCAL_PATH := $(MY_LOCAL_PATH)# ... declare another module

all-subdir-makefile

返回当前my-dir目录下所有子目录中的Android.mk路径。
你可以通过这个函数来向编译系统提供嵌套式的源代码目录结构。默认情况下,NDK只会在包含Android.mk文件的目录下查找文件。

this-makefile

返回当前makefile的路径

parent-makefile

返回包含当前makefile的makefile的路径

grand-parent-makefile

返回更上一级的makefile的路径

import-module

通过模块的名字查找并包含对应的Android.mk文件,举例:

$(call import-module,<name>)

在这个例子中,编译系统会在NDK_MODULE_PATH环境变量所指向的目录中查找名为<name>的模块,并自动包含其Android.mk文件。

0 0
原创粉丝点击