Android.mk基础

1、前言

Android.mk用于向编译系统描述源文件和共享库,它实际上是编译系统解析一次或多次的微小GNU makefile片段。它的语法支持将源文件分组为模块,模块是静态库、共享库或独立的可执行文件。

2、简单示例

首先来看一个最简单的Android.mk的例子,如下所示:

# A simple Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := hello.c
LOCAL_MODULE := helloworld
include $(BUILD_EXECUTABLE)

对该Android.mk文件进行解析,如下:

LOCAL_PATH := $(call my-dir)

每个Android.mk文件必须以定义LOCAL_PATH为开始,它用于在开发项目文件中查找源文件,而宏my-dir则由编译系统提供,返回包含Android.mk的目录路径。

include $(CLEAR_VARS)

CLEAR_VARS变量由编译系统提供,并指向一个特定的GNU Makefile,由它负责清理很多LOCAL_XXX的值,例如:LOCAL_MODULE、LOCAL_SRC_FILES等,但是不清理LOCAL_PATH,这个清理动作是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的,清理后才能避免相互影响。

LOCAL_SRC_FILES := hello.c

LOCAL_SRC_FILES变量必须包含将要打包成模块的C/C++源文件,不必列出头文件,编译系统会自动找出依赖的文件,缺省的C++源码的拓展名为.cpp,也可以通过LOCAL_CPP_EXTENSION进行修改。

LOCAL_MODULE := helloworld

LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块,名字必须唯一并且不包含空格,Build System会自动添加适当的前缀和后缀。

include $(BUILD_EXECUTABLE)

BUILD_EXECUTABLE是编译系统提供的一个变量,指向一个特定的GNU Makefile脚本,表示要编译成一个可执行的文件,如果想编译成动态库则可以用BUILD_SHARED_LIBRARY,如果想编译成静态库则可以用BUILD_STATIC_LIBRARY。

3、保留的变量

在编写Android.mk文件时应该使用或者定义的变量,可以视具体情况定义其他的变量,但是NDK编译环境保留以下变量名:

(1)LOCAL_开头的变量名(例如:LOCAL_MODULE)

(2)PRIVATE、NDK_以及APP_开头的变量名(内部使用)

(3)小写命名(内部使用,如my-dir)

如果要在Android.mk文件中定义自己的变量,建议使用前缀MY_开头,例如:

MY_SOURCES := foo.c
ifneq ($(MY_CONFIG_BAR),)
    MY_SOURCES += bar.c
LOCAL_SRC_FILES += $(MY_SOURCES)

4、NDK提供的变量

GNU Make在解析Android.mk文件之前由编译环境定义的一些变量,需要注意的是,在某些条件下,NDK可能会多次解析你的Android.mk文件,每一次都会为某些变量进行不同的定义。

(1)CLEAR_VARS

指向一个脚本,取消定义在模块变量下面几乎所有的LOCAL_XXX的定义,必须在一个新的模块下面包含这个脚本,如:

include $(CLEAR_VARS)

(2)BUILD_SHARED_LIBRARY

指向一个脚本,收集你提供的所有LOCAL_XXX变量的信息,决定如何根据你列出的源文件编译对应的共享库,注意,在包含这个脚本之前,你必须至少已经定义了LOCAL_MODULE和LOCAL_SRC_FILES,如:

include $(BUILD_SHARED_LIBRARY)

注意:这会生成一个名为lib$(LOCAL_MODULE).so文件。

(3)BUILD_STATIC_LIBRARY

该变量类似于BUILD_SHARED_LIBRARY,用来编译生成静态库,静态库不会拷贝到你的工程,但是可以用来生成共享库,如:

include $(BUILD_STATIC_LIBRARY)

注意:这会生成一个名为lib$(LOCLA_MODULE).a文件。

(4)PREBUILT_SHARED_LIBRARY

指定一个脚本,用来指定一个预置的共享库,和BUILD_SHARED_LIBRARY以及BUILD_SHARED_LIBRARY不同的是,LOCAL_SRC_FILES的值必须是一个预置共享库的路径,而不是一个源文件,如foo/libfoo.so文件,还可以在另一个模块中使用LOCAL_PREBUILTS变量来引用预置库。

(5)PREBUILT_STATIC_LIBRARY

和PREBUILT_SHARED_LIBRARY类似,但指定一个静态库。

(6)TARGET_ARCH

由AOSP指定的CPU架构名称,arm表示与ARM兼容。

(7)TARGET_PLATFORM

解析Android.mk时,目标Android平台的名称。

(8)TARGET_ARCH_ABI

解析Android.mk时,目标CPU+ABI的名称,如armeabi-v7a。

(9)TARGET_ABI

目标平台和abi的连接,由$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)定义。

5、NDK提供的函数宏

下面是GNU Make的宏方法,必须使用$(call <function>)的形式使用,返回文本信息。

(1)my-dir

返回上一个包含Makefile文件的路径,通常是当前Android.mk的目录,在Android.mk文件起始位置定义LOCAL_PATH时很有用,如:

LOCAL_PATH := $(call my-dir)

注意:由于GNU Make的工作方式,该方法返回在解析编译脚本时最后一次包含Makefile文件的路径,在包含其他文件之后不要调用my-dir,例如下面的例子:

# Android.mk

LOCAL_PATH := $(call my-dir)
# Declare one module
...
include $(LOCAL_PATH)/foo/Android.mk
# Declare another module
...

这里的问题是,第二次调用my-dir会将LOCAL_PATH定义为$(LOCAL_PATH)/foo/而不是$(LOCAL_PATH)。

基于上面的这个原因,在一个Android.mk文件中,将额外的包含动作放在所有内容的后面,如下:

# Android.mk

LOCAL_PATH := $(call my-dir)
# Declare one module
...
LOCAL_PATH := $(call my-dir)
# Declare another module
...
# Extra include at the end of Android.mk
include $(LOCAL_PATH)/foo/Android.mk

还可以用一个变量保存第一次调用my-dir的值,如下:

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

(2)all-subdir-makefiles

返回位于当前my-dir路径下的所有子目录中的Android.mk组成的列表,例如下面的结构:

sources/foo/Android.mk
sources/foo/lib1/Android.mk
sources/foo/lib2/Android.mk

如果source/foo/Android.mk文件中包含了这句:

include $(call all-subdir-makefiles)

那么,source/foo/lib1/Android.mk和source/foo/lib2/Android.mk会自动地被包含进来。

(3)this-makefile

返回当前Makefile文件的路径。

(4)parent-makefile

返回包含树中父Makefile文件的路径,如包含当前Makefile的Makefile的路径。

(5)grand-parent-makefile

与parent-makefile类似。

(6)import-module

该方法通过名字查找和包含另一个模块,通常的用法是:

$(call import-module,<name>)

该方法会在NDK_MODULE_PATH环境变量中查找标签为<name>的模块,然后自动地将它的Android.mk包含进来。

6、模块描述变量

下面的变量用来向编译环境描述模块,应该在include $(CLEAR_VARS)和include $(BUILD_XXX)之间定义这些变量,$(CLEAR_VARS)会取消定义或者清除所有这些变量。

(1)LOCAL_PATH

这个变量表示当前文件所在的路径,必须在Android.mk的开始处定义,可以由下面的命令来完成:

LOCAL_PATH := $(call my-dir)

$(CLEAR_VARS)不会清除该变量,因此,每个Android.mk只需定义一次即可。

(2)LOCAL_MODULE

模块的名字,每一个模块的名字必须唯一并且不含空格,必须在包含$(BUILD_XXX)脚本之前定义这个变量,默认的,这个名字决定了生成文件的名字,如名为<foo>的模块,共享库的名字为lib<foo>.so。

(3)LOCAL_MODULE_FILENAME

这个变量是可选的,帮助重新定义生成文件的名字,默认的情况下,<foo>模块会按照Unix的一般标准生成名为lib<foo>.a的静态库或者名为lib<foo>.so的共享库,可以通过LOCAL_MODULE_FILENAME重新定义生成文件的名字,如下:

# Android.mk
LOCAL_MODULE := foo-version-1
LOCAL_MODULE_FILENAME := libfoo

注意:不要在LOCAL_MODULE_FILENAME中放路径或者拓展名,这些会被编译系统自动处理。

(4)LOCAL_SRC_FILES

用来编译的模块的源文件列表,只需要列出需要被传给编译器的文件,编译环境会自动计算依赖关系,所有的源文件名是相对LOCAL_PATH的,也可以使用路径分隔符,如:

LOCAL_SRC_FILES := foo.c 
                  fun/bar.c

(5)LOCAL_CPP_EXTENSIONS

这是一个可选的变量,用来指示C++的源文件的文件拓展名,默认的是.cpp,但是可以改变它,如:

LOCAL_CPP_EXTENSION := .cxx

(6)LOCAL_C_INCLUDES

这是可选的变量,相对NDK根目录的相对路径列表,在编译所有的源文件时会被添加到搜索路径后面,如:

LOCAL_C_INCLUDES := sources/foo
or
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo

它们需要放置在LOCAL_CFLAGS/LOCAL_CPPFLAGS之前,在使用ndk-gdb进行native调试时将自动使用LOCAL_C_INCLUDES路径。

(7)LOCAL_CFLAGS

这是一个可选的变量,编译C/C++源文件时的编译器flags,可以用来指定额外的宏定义和编译选项。

(8)LOCAL_CXXFLAGS

LOCAL_CPPFLAGS的别名。

(9)LOCAL_CPPFLAGS

可选的变量,编译C++文件时编译器flags,在编译器命令行中,他们出现在LOCAL_CFLAGS之后。

(10)LOCAL_STATIC_LIBRARIES

需要链接到这个模块的静态库module(使用BUILD_STATIC_LIBRARY编译的)列表,只在共享库中有用。

(11)LOCAL_SHARED_LIBRARIES

该模块在运行时需要依赖的共享库列表,在链接时需要,并在生成文件中嵌入相应的信息。

(12)LOCAL_LDLIBS

编译模块时需要的额外链接flags列表,传递指定的以-l为前缀的系统库,例如,下面的指令会告诉链接器生成一个在加载的时候链接到/system/lib/libz.so的模块:

LOCAL_LDLIBS := -lz

(13)LOCAL_ALLOW_UNDEFINED_SYMBOLS

默认情况下,在编译共享数据库时会遇到未定义的引用,导致未定义符号的错误,这对在源码中抓取log的作用很大,但是,如果考虑到某些原因需要关闭这个选项,将变量设置为true即可。

(14)LOCAL_ARM_MODE

默认情况下,以thumb模式生成ARM目标二进制,每一个指令都是16比特宽,如果想强制生成arm(32比特指令)模式下的模块,可以定义这个变量为arm,如下:

LOCAL_ARM_MODE := arm

也可以通过在源文件名称后面添加后缀.arm的方式编译系统以指定的arm模式编译源文件,如:

LOCAL_SRC_FILES := foo.c bar.c.arm

这会告诉编译系统以arm模式编译bar.c,但根据LOCAL_ARM_MODE的值编译foo.c。

(15)LOCAL_ARM_NENO

定义这个变量为true允许在C/C++文件中使用ARM Advanced SIMD GCC(也叫NEON)指令,就像在程序集文件中使用NEON指令一样。

(16)LOCAL_CFLAGS

定义这个变量来记录一组C/C++编译flags,这些会被添加到使用这个变量的模块的LOCAL_STATIC_LIBRARIES或者LOCAL_SHARED_LIBRARIES的LOCAL_CFLAGS中,例如:

# Define "foo" module
include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo/foo.c
include $(BUILD_STATIC_LIBRARY)

# Define "bar" module
include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar.c
LOCAL_CFLAGS := -DBAR=2
LOCAL_STATIC_LIBRARYS := foo
include $(BUILD_SHARED_LIBRARY)

在编译bar.c时会将-DFOO=1 –DBAR=2传给编译器,exported flags会被一层一层的传递,如果zoo依赖于bar,而bar依赖于foo,那么foo中的所有flags会被传给zoo,exported flags在当前module编译时不会被使用,在上面,编译foo/foo.c时不会传递-DFOO=1。

(17)LOCAL_EXPORT_CPPFLAGS

和LOCAL_EXPORT_CFLAGS一样,但是只对C++文件。

(18)LOCAL_EXPORT_C_INCLUDES

和LOCAL_EXPORT_CFLAGS一样,但记录的是C头文件路径。

(19)LOCAL_EXPORT_LDLIBS

和LOCAL_EXPORT_CFLAGS一样,记录链接器flags,导入的链接器flags会被添加到模块的LOCAL_LDLIBS中,这由Unix链接器的工作方式决定,当foo是一个静态库并且部分代码依赖系统库,该变量将会很有用,LOCAL_EXPORT_LDLIBS可以被用来导出依赖,如:

# Define "foo" module
include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo/foo.c
LOCAL_EXPORT_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)

# Define "bar" module
include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar.c
LOCAL_STATIC_LIBRARY := foo
include $(BUILD_SHARED_LIBRARY)

这里,libbar.so在链接时编译-llog,表示它依赖于系统日志库,因为它依赖于foo。

(20)LOCAL_FILTER_ASM

使用一个shell命令定义这个变量,用来过滤LOCAL_SRC_FILES中的或者由其生成的程序集文件。

原文地址:https://www.cnblogs.com/Cqlismy/p/11801450.html