前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android.mk语法解释[转]前言原文

Android.mk语法解释[转]前言原文

作者头像
用户2930595
发布2018-08-23 09:53:47
1.2K0
发布2018-08-23 09:53:47
举报

前言

本文转载自Android.mk语法解释,方便收藏。

原文

大家在编写Android的Native代码时,经常会接触到一个叫做Android.mk的文件。 虽然编译的时候都用到的是make,但是这个Android.mk文件里的语法还跟一般的make文件语法不太一样。 本质上,Android.mk只是GNU MakeFile的一个片段,编译系统在编译的时候有可能会多次解释Android.mk文件,所以要尽量少在脚本里面申明变量,也不要假设任何没有在脚本中定义的条件。 Android.mk文件是用来让你把源码组织成各个“模块”。所谓模块,由以下三种构成:

  • 静态库
  • 共享库
  • 独立的可执行文件

只有共享库可以被包含到apk应用程序包里,但是静态库可以被用来生成共享库。 可以在一个Android.mk文件中定义一个或者多个模块,并且可以多个模块复用同样的源代码。 编译系统已经替你处理了很多琐碎的事情。例如,你不需要在Android.mk文件中罗列.h头文件和显式声明生成文件之间的依赖关系。NDK编译系统会自动为你计算出来。 这也意味着,当升级到新版的NDK时,不需要更改Android.mk文件就可以相互兼容。 NDK中的Android.mk文件语法和Android源码中的Android.mk文件语法非常相近。但是其实编译系统实现是不一样的,这是有意这样设计的,为了让应用程序开发者可以更加方便的复用第三方库的源码。

简单的例子

在正式描述语法细节之前,让我们来看一个简单的例子“hello JNI”,这个例子包含在NDK里的以下目录中:

代码语言:javascript
复制
samples/hello-jni  

在这个目录里,我们可以看到

  • src目录,里面包含了例子用到的Java代码
  • jni目录,里面包含了例子用到的Native代码(jni/hello-jni.c)
  • jni/Android.mk文件,描述了要NDK编译系统编译出来的共享库。内容如下:
代码语言:javascript
复制
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LBRARY)

下面来稍微解释一下:

代码语言:javascript
复制
LOCAL_PATH := $(call my-dir)

所有的Android.mk文件必须要以LOCAL_PATH变量定义开头。它用来定位要编译的源代码在代码树中的位置。在本例中,宏函数“my-dir”是由编译系统提供的,用来返回当前目录的路径(也就是包含此Android.mk文件的目录)。

代码语言:javascript
复制
include $(CLEAR_VARS)  

CLEAR_VARS变量也是由编译系统提供的,它指向了一个特殊的GNU MakeFile文件,这个文件的用处是为你清理许多LOCAL_XXX变量(例如,LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARY),除了前面才定义的LOCAL_PATH变量。必须要这样做的目的是,所有的编译脚本都在同一个GNU Make执行环境中分析,所有这些变量都是全局的。如果不及时清理,前面编译脚本定义的变量会对当前编译脚本产生影响。

代码语言:javascript
复制
LOCAL_MODULE := hello-jni  

LOCAL_MODULE变量必须定义,用来标识你在Android.mk文件中描述的每一个模块。名字必须唯一,并且不能包含空格。注意,编译系统会自动给生成的文件加上适当的前缀和后缀。也就是说,如果一个共享模块名是“foo”的话,那么生成的文件就是“libfoo.so”。 重要提示:如果你把你的模块命名成“libfoo”的话,编译系统是不会再加上“lib”前缀的,还是会生成“libfoo.so”文件。这样设计的目的是为了支持从AOSP那移植过来的代码。

代码语言:javascript
复制
LOCAL_SRC_FILES := hello-jni.c  

变量LOCAL_SRC_FILES必须包含编译模块必须要的C或者C++代码源文件。注意,请不要在此列出头文件和其它的各种包含文件,因为编译系统会自动帮你算出依赖关系,请只列出需要编译器编译的代码源文件。 注意,缺省的C++代码源文件的扩展名是“.cpp”。但是,可以通过定义变量LOCAL_CPP_EXTENTION来指定成其它的名字。定义的时候不要忘记起始的点(例如“.cxx”可以,但是“cxx”就不行)。

代码语言:javascript
复制
include $(BUILD_SHARED_LIBRARY)  

变量BUILD_SHARED_LIBRARY也是由编译系统提供的,指向了一个GNU MakeFile脚本文件。这个脚本文件是用来负责收集所有你从“include $(CLEAR_VARS)”开始定义的所有LOCAL_XXX变量中包含的信息,来决定如何编译,编译成什么。相应的还有BUILD_STATIC_LIBRARY变量,用来生成一个静态库。 在samples目录下,还有很多更加复杂的例子,每个Android.mk都包含注释。

自定义变量

NDK编译系统预留了如下的变量名:

  • 所有以LOCAL_开头的变量(如LOCAL_MODULE)
  • 所有以PRIVATE_、NDK_或者APP_开头的变量(供内部使用)
  • 小写字母构成的变量(内部使用,例如my-dir)

只要符合以上三个规则,其它的你就可以自由定义了。如果你要定义的话,我们建议用MY_前缀,下面是一个简单的例子:

代码语言:javascript
复制
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变量。这个脚本必须要在定义一个新模块之前被包含进来

代码语言:javascript
复制
include $(CLEAR_VARS)  

BUILD_SHARED_LIBRARY

指向一个编译脚本,这个编译脚本可以收集所有你定义的LOCAL_XXX变量中提供的信息,然后决定如何编译目标共享库。注意,最少你要在包含这个脚本之前定义好LOCAL_MODULE和LOCAL_SRC_FILES变量。用法如下:

代码语言:javascript
复制
include $(BUILD_SHARED_LIBRARY)  

这将会生成一个名字为lib$(LOCAL_MODULE).so的目标文件。

BUILD_STATIC_LIBRARY

变量BUILD_STATIC_LIBRARY是专门用来编译静态库的。静态库是不能直接用在应用程序中的,但是可以用来构建共享库(参照下面的对LOCAL_STATIC_LIBRARIES和LOCAL_WHOLE_STATIC_LIBRARIES变量的说明)。示例用法如下:

代码语言:javascript
复制
include $(BUILD_STATIC_LIBRARY)  

这将会生成一个名为lib$(LOCAL_MODULE).a的目标文件。

PREBUILT_SHARED_LIBRARY

指向一个编译脚本,该脚本用来指定一个预先编译好的共享库。这时候变量LOCAL_SRC_FILES值的含义,就和在BUILD_SHARED_LIBRARY和BUILD_STATIC_LIBRARY里面的不同。前者要设置成一个指向预编译好的共享库文件的路径,而后者是要编译的源文件。

PREBUILD_STATIC_LIBRARY

含义基本和PREBUILD_SHARED_LIBRARY相同,只不过这是指定一个静态库文件。

TARGET_ARCH

目标 CPU架构的名字。如果是“arm”,表示要生成 ARM 兼容的指令,与 CPU架构的修订版无关。

TARGET_PLATFORM

目标平台的名字。表示要编译的这个模块,将来要跑在哪个Android目标平台上,例如“android-18”代表Android 4.3系统。 当然,Android系统提供向上兼容性,即使指定了android-18,编译出来的程序还是可以在android-19平台上跑的。

TARGET_ARCH_ABI

目标ABI(Application Binary Interface)的名字。目前支持四个值: 1)armeabi:表示对ARMv5TE的支持; 2)armeabi-v7a:表示对ARMv7的支持; 3)x86:表示对i686的支持; 4)mips:表示对于mips32(r1)的支持。 注意,在未来NDK中还会引入更多的ABI,它们的名字各不相同。但是所以基于ARM的ABI,尽管它们的ABI名字不一样,但是它们的TARGET_ARCH变量都会被定义成“arm”。

TARGET_ABI

目标平台和 ABI 的组合。它实际上被定义成(TARGET_PLATFORM)-(TARGET_ARCH_ABI) 。 在默认的情况下,它会是“android-3-armeabi”。

NDK提供的宏函数

本节将介绍编译系统预先定义好了的GNU Make宏函数,这些函数必须要像“$(call)”这样调用。它们会返回文本类型的信息。

my-dir

返回最近一次包含的MakeFile的目录位置,通常这就是当前Android.mk文件所在的目录。它可以用来在Android.mk文件的开头定义LOCAL_PATH变量,如下:

代码语言:javascript
复制
LOCAL_PATH := $(call my-dir)  

特别要注意的是,该函数确切的说是返回最近一次包含的MakeFile文件的位置。请不要在包含了另外一个文件后调用my-dir宏函数。 例如,考虑以下这种情况:

代码语言:javascript
复制
LOCAL_PATH := $(call my-dir)  
  
... declare one module  
  
include $(LOCAL_PATH)/foo/`Android.mk`  
  
LOCAL_PATH := $(call my-dir)  
  
... declare another module  

这里存在的问题是,由于在第二次调用my-dir之前包含了另一个Android.mk文件,就会将LOCAL_PATH设置成PATH/foo,而不是PATH。 由于这个原因,如果要包含另外的文件的话,最好将其放在Android.mk文件的最后面,如:

代码语言:javascript
复制
LOCAL_PATH := $(call my-dir)  
  
... declare one module  
  
LOCAL_PATH := $(call my-dir)  
  
... declare another module  
  
# extra includes at the end of the `Android.mk`  
include $(LOCAL_PATH)/foo/`Android.mk`  

如果这样做不方便的话,在第一次调用my-dir之后,将其值保存在另外一个变量中,例如:

代码语言:javascript
复制
MY_LOCAL_PATH := $(call my-dir)  
  
LOCAL_PATH := $(MY_LOCAL_PATH)  
  
... declare one module  
  
include $(LOCAL_PATH)/foo/`Android.mk`  
  
LOCAL_PATH := $(MY_LOCAL_PATH)  
  
... declare another module  

all-subdir-makefiles

返回在当前‘my-dir’目录下,所有子目录中包含的Android.mk文件列表。例如,考虑在以下目录层级中:

代码语言:javascript
复制
sources/foo/Android.mk  
sources/foo/lib1/Android.mk  
sources/foo/lib2/Android.mk  

如果在sources/foo/Android.mk文件中包含下面这一行:

代码语言:javascript
复制
include $(call all-subdir-makefiles)  

那么,这一句将自动包含sources/foo/lib1/Android.mk和sources/foo/lib2/Android.mk文件。 该函数可以在多级嵌套的目录结构中,帮助编译系统罗列出里面所有包含的Android.mk文件。而在默认情况下,NDK只会寻找sources/*/Android.mk文件,再下面就不会去查找了。

this-makefile

返回当前MakeFile的路径(这个函数是在哪个MakeFile中调用的)

parent-makefile

返回父MakeFile的路径,也就是包含当前调用这个函数的MakeFile的那个MakeFile。

grand-parent-makefile

不解释了,我想大家应该从名字就能猜到其意思了。

import-module

该函数用于按指定的名字,查找另一个模块的Android.mk文件,并包含到当前的Android.mk中来。用法如下:

代码语言:javascript
复制
$(call import-module,<name>)  

上面的宏函数调用,会在 NDK_MODULE_PATH变量里所指定的目录列表的每一个目录中,寻找指定名字的模块,并且找到之后将其包含进来。

模块描述变量

下面介绍的这些变量,专门用来向编译系统描述你模块的一些特性。如果要使用的话,请确保将它们定义在“include (CLEAR_VARS)”和“include(BUILD_XXXXX)”之间。而除了一些下面说明的特例,“$(CLEAR_VARS)”会将这些变量都清理掉。

LOCAL_PATH

这个变量用来告诉编译系统当前编译路径是什么,必须要在Android.mk文件的一开头就定义,像这样:

代码语言:javascript
复制
LOCAL_PATH := $(call my-dir)  

这个变量不会被“$(CLEAR_VARS)”给清理掉,所以一般情况下每个Android.mk文件只要定义一次就可以了(除非你在一个Android.mk文件中定义了多个模块)。

LOCAL_MODULE

这个变量用来告诉编译系统当前编译模块的名字。这个名字必须唯一,不能和别的模块名相同,并且不能包含空格。必须在“$(BUILD_XXXXX)”之前定义。 默认情况下,编译生成的文件名是由模块的名字决定的。就是在模块名前面加上“lib”,再在后面加上“.so”(动态库)或者“.a”(静态库)。例如如果一个共享模块名是“foo”的话,那么生成的文件就是“libfoo.so”。 如果想自己制定生成的文件名字,而不用这种命名规则的话,可以通过修改变量LOCAL_MODULE_FILENAME的值来达到。

LOCAL_MODULE_FILENAME

这个变量是可选的,它允许你自己指定编译过后生成的文件的名字。默认情况下,编译系统使用LOCAL_MODULE变量的值来计算最后生成的文件名。 如果不想这样,而是自己制定,可以定义LOCAL_MODULE_FILENAME变量,例如:

代码语言:javascript
复制
LOCAL_MODULE := foo-version-1  
LOCAL_MODULE_FILENAME := libfoo  

这样的话,最终编译生成的文件就不是libfoo-version-1.so了,而是libfoo.so。 注意,定义这个变量的时候,请不要包含路径和文件扩展名。

LOCAL_SRC_FILES

这个变量用来指定,编译生成模块所需要的所有源码文件。请只包含源码文件,不要包含头文件,编译系统会自动替你计算依赖关系。 如果不指定路径的话,编译系统会默认在当前路径下(即变量LOCAL_PATH中指定的路径)搜索源码文件。如果源码文件不在当前路径下的话,请指定路径名,例如:

代码语言:javascript
复制
LOCAL_SRC_FILES := foo.c \  
                   toto/bar.c  

除了上面例子中这种相对路径外,也可以使用绝对路径:

代码语言:javascript
复制
LOCAL_SRC_FILES := /home/user/mysources/foo.c  

如必要,请尽量不要使用绝对路径,会导致兼容性问题,可移植性会变差。 请注意,即使是在Windows系统上编译,指定路径的时候也请使用正斜杠(“/”),而不要使用反斜杠(“\”)。

LOCAL_CPP_EXTENSION

这个变量是可选的,默认情况下,编译系统如果看到一个文件是以“.cpp”结尾的话,会认为其里面包含C++的代码;如果不是以“.cpp”结尾的话,则认为这不是一个包含C++代码的文件。如果你的源码确实是用C++编写,但是不是以“.cpp”结尾的文件保存的话,可以通过指定LOCAL_CPP_EXTENSION变量,让编译系统知道其也是用C++编写的。 例如,如果你的C++源码文件是以“.cxx”结尾的话,则可以这样定义:

代码语言:javascript
复制
LOCAL_CPP_EXTENSION := .cxx  

注意,指定扩展名的时候,要加上点(“.”)。 另外,从NDK r7开始,可以指定一串扩展文件列表给这个变量:

代码语言:javascript
复制
LOCAL_CPP_EXTENSION := .cxx .cpp .cc  

这样的话,编译系统就碰到以这些名字结尾的文件的话都会知道它们是用C++编写的。

LOCAL_CPP_FEATURES

这个变量是可选的,用来告诉编译系统,你的代码会依赖哪些C++专有的特性。主要有一下几个: 1)RTTI(RunTime Type Information,即动态类型识别) 如果想告诉编译系统,你的代码使用了C++中的动态类型识别特性,可以这样:

代码语言:javascript
复制
LOCAL_CPP_FEATURES := rtti  

2)C++ exceptions(C++异常) 如果想告诉编译系统,你的代码使用了C++的异常特性,可以这样:

代码语言:javascript
复制
LOCAL_CPP_FEATURES := exceptions  

也可以同时指定多个特性(顺序无所谓):

代码语言:javascript
复制
LOCAL_CPP_FEATURES := rtti exceptions  

通过设置这个变量,在编译的时候,可以传递相应的选项给编译器或链接器。 请不要在LOCAL_CPPFLAGS变量中定义-frtti或-fexceptions选项,而是在LOCAL_CPP_FEATURES变量中指定。

LOCAL_C_INCLUDES

这个变量是可选的,默认情况下,编译系统会在当前路径下(即在LOCAL_PATH变量中指定的路径)搜索相关的头文件,可以通过设置这个变量来增加搜索路径。 例如:

代码语言:javascript
复制
LOCAL_C_INCLUDES := sources/foo  

或者,也可以这样:

代码语言:javascript
复制
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo  

如果要在LOCAL_CFLAGS或者LOCAL_CPPFLAGS中使用任何包含选项的话,请确保要包含文件所在的目录已经在LOCAL_C_INCLUDES变量中定义过了。 如果使用ndk-gdb对程序进行调试的话,在变量LOCAL_C_INCLUDES中定义的目录也会自动被包含进来。

LOCAL_CFLAGS

这个变量定义了,当要编译C程序文件的时候,要传递给系统编译器的一组选项。 如果要通知编译器包含一个目录的话,也可以通过下面的变量定义实现:

代码语言:javascript
复制
LOCAL_CFLAGS += -I<path>  

但是,请尽量不要这么做,还是使用前面介绍的LOCAL_C_INCLUDES变量来指定特殊的包含路径,以保证后面用ndk-gdb对程序进行调试的时候,这些特殊路径能自动被包含进来。

LOCAL_CPPFLAGS / LOCAL_CXXFLAGS

这个变量定义了,当要编译C++程序文件的时候,要传递给系统编译器的一组选项。 一般情况下,它是接着LOCAL_CFLAGS变量后面定义的。 LOCAL_CXXFLAGS是LOCAL_CPPFLAGS的一个别名。请尽量使用LOCAL_CPPFLAGS,因为LOCAL_CXXFLAGS已经过时,以后的NDK中可能会不再提供支持。

LOCAL_STATIC_LIBRARIES

列出所有当前模块所要依赖的静态库。如果当前待编译模块是一个共享库或者一个可执行文件的话,这会强制让这些静态库链接进最终编译出来的二进制文件中。如果当前待编译模块也是一个静态库的话,则不会对编译出来的文件造成什么影响,只是如果以后有别的模块要包含当前这个静态库的时候,通过这个变量就知道它也要依赖这些静态库。

LOCAL_SHARED_LIBRARIES

列出了所有当前模块所要在运行时依赖的动态库。它的作用是在链接的时候,在最终生成的文件中包含相应的信息,并不会把这些动态库的二进制代码包含进来。

LOCAL_LDLIBS

这个变量用来告诉链接器,在将各个模块链接成最终的文件时,需要哪些特定的系统默认提供的库,这些库要以“-l”开头。 因为是给链接器用的,所以只在编译动态库或者可执行文件的时候,这个变量才有效。 例如:

代码语言:javascript
复制
LOCAL_LDLIBS := -lz  

这样的话,会告诉链接器,在生成最终的二进制文件中包含运行时将动态链接/system/lib/libz.so模块的信息。 和在LOCAL_SHARED_LIBRARIES中指定的动态库不同,LOCAL_LDLIBS是列出系统的动态库,而LOCAL_SHARED_LIBRARIES是列出自己编译出的动态库。 注意,在编译静态库的时候,这个变量即使设置了也会被忽略,并且在这种情况下ndk-build会打印出警告信息。

LOCAL_LDFLAGS

列出其它的一些(除去在LOCAL_LDLIBS中传给链接器的系统动态库的信息)要传给链接器的参数,同样只针对动态库和可执行文件有效。 在编译静态库的时候会被忽略,并且打出警告信息。

LOCAL_ALLOW_UNDEFINED_SYMBOLS

默认情况下,在编译动态库的时候,如果遇到了任何无法解析的符号,都会直接报“undefined symbol”的错误。 这样做可以极大的帮助开发者尽早发现程序中的错误。 但是,如果出于某些原因,你想在链接的时候忽略这些检查,可以将变量LOCAL_ALLOW_UNDEFINED_SYMBOLS设置成“true”。 但是,这样做的话,如果在加载的时候还是无法找到对应的符号,则程序还是会直接报错退出。 注意,这个选项对编译静态库的时候没有任何作用,如果编译系统在编译静态库时发现定义了这个变量,则会给出错误提示信息。

LOCAL_ARM_MODE

默认情况下,如果编译目标是ARM平台的话,编译系统会将程序编译成Thumb指令集(每条指令长16比特)的。可以通过将这个变量设置成“arm”来告诉编译系统,必须将代码编译成ARM指令集(每条指令32比特)的:

代码语言:javascript
复制
LOCAL_ARM_MODE := arm  

这样的话,这个整个这个模块都会编译成ARM指令集的。 而如果只想让模块中某几个源文件内的代码被编译成ARM指令集的,而剩下的代码任然用Thumb指令集编译的话,可以在源代码的文件名后面加上“.arm”后缀。 例如:

代码语言:javascript
复制
LOCAL_SRC_FILES := foo.c bar.c.arm  

这将告诉编译系统,将“bar.c”编译成ARM指令集的。而对于“foo.c”来说,任然用LOCAL_ARM_MODE变量指定的方式(如果未指定则默认用Thumb指令集)编译。

LOCAL_ARM_NEON

可以通过定义LOCAL_ARM_NEON变量,并将其值设置成“true”,打开GCC编译器对ARM中NEON指令集的支持。 注意,并不是所有ARM处理器都支持NEON指令集的。即使处理器支持比较新的ARMv7指令集,也不一定包含对NEON指令集的支持。所以,为了使得代码能够正确的执行,需要在运行时进行动态判断处理器是否支持NEON指令集。 通过设置LOCAL_ARM_NEON变量,编译器会将模块中所有的代码都编译成支持NEON指令集的形式。而如果你只想让模块中某几个源文件中的代码编译成支持NEON指令集,而其它的不要支持NEON指令集,可以将那些需要支持NEON指令集的源码文件的名字后面加上“.neon”后缀。 例如:

代码语言:javascript
复制
LOCAL_SRC_FILES = foo.c.neon bar.c zoo.c.arm.neon  

本例中,“foo.c”会被编译成Thumb指令集加上NEON指令集的形式(默认情况下,所有源代码会被编译成Thumb指令集的形式),“bar.c”会被编译成Thumb指令集的形式,而“zoo.c”会被编译成ARM指令集加上NEON指令集的形式。 还有一点要说明,如果“.arm”后缀和“.neon”后缀要同时使用的话,请保证“.arm”出现在“.neon”之前(对于前例中的“zoo.c.arm.neon”,如果改成“zoo.c.neon.arm”,则会出错)。

LOCAL_DISABLE_NO_EXECUTE

从Android NDK r4开始,加入了一个新的安全功能,叫做“NX bit”(No-eXecute)。就是将内存中的某些区域标记成不能执行的,如果PC寄存器移动到这些区域的话就会报错,从而让利用溢出漏洞变得更加困难。 默认的情况下,这个功能是被开启的,如果想关闭编译出来程序的“NX bit”功能的话,可以定义LOCAL_DISABLE_NO_EXECUTE变量,并将其值设置成“true”。 注意,这个功能只在支持ARMv6及以上的处理器上才能支持。但是编译出来的程序保持向下兼容性,即在ARMv5的处理器上也能运行,但保护功能失效。

LOCAL_DISABLE_RELRO

默认情况下,NDK编译出来的代码,会自带对重定位表和GOT表的保护功能。也就是告诉Android的动态链接器(linker),在程序加载进内存,并完成了重定位之后,将某些特定区域的内存标记为只读。这样,可以有效防止别的程序通过修改GOT表来达到hook程序中某些重要函数的目的,提高了程序的安全性(其实没什么用,只要在修改之前用mprotect()函数将GOT先改成可写的就可以了)。 如果处于某些特殊的目的,你想关闭这个功能的话,可以定义变量LOCAL_DISABLE_RELRO,并将其值设置成“true”。 注意,这个所谓的GOT保护功能,只在Android 4.3(Jelly Bean)及以后的版本中有效。而对于之前的Android版本,代码仍然能运行,但保护功能将不起作用。

LOCAL_EXPORT_CFLAGS

这个变量比较有意思,当要编译C语言编写的程序时,前面的LOCAL_CFLAGS是要编译本模块的时候传给编译器的参数列表,而这个LOCAL_EXPORT_CFLAGS是要传给别的依赖于当前模块的其它模块的编译器参数列表。 也就是说,如果一个模块自己用LOCAL_STATIC_LIBRARIES或者LOCAL_SHARED_LIBRARIES引用了一个别的静态或动态模块,而那个模块的Android.mk文件中定义了LOCAL_EXPORT_CFLAGS变量的话,则在编译自己模块的时候,传给编译器的选项还要包括那个引用模块中在LOCAL_EXPORT_CFLAGS变量里定义的选项。 好拗口,举个例子,假设有一个模块叫“foo”,它的定义如下:

代码语言:javascript
复制
include $(CLEAR_VARS)  
LOCAL_MODULE := foo  
LOCAL_SRC_FILES := foo/foo.c  
LOCAL_EXPORT_CFLAGS := -DFOO=1  
include $(BUILD_STATIC_LIBRARY)  

而同时还有另外一个模块,叫做“bar”,它依赖于这个“foo”模块,其定义如下:

代码语言:javascript
复制
include $(CLEAR_VARS)  
LOCAL_MODULE := bar  
LOCAL_SRC_FILES := bar.c  
LOCAL_CFLAGS := -DBAR=2  
LOCAL_STATIC_LIBRARIES := foo  
include $(BUILD_SHARED_LIBRARY)  

那么,对于“bar”来说,编译的时候传给编译器的选项就不光是“-DBAR=2”了。由于依赖的模块“foo”中定义了LOCAL_EXPORT_CFLAGS选项,所以编译选项要加上“-DFOO=1”。因此,最终传给编译器的选项是“-DFOO=1 -DBAR=2”。 是定义在自己模块的LOCAL_CFLAGS之前的。 同时,这个变量也是可传递的。假设另有一个模块“zoo”依赖于“bar”,而前面也看到了“bar”是依赖于“foo”的,那么在编译“zoo”模块时也会使用在“foo”模块中导出的参数。另外,这个变量对编译包含这个变量的自己模块是没有作用的。例如,前例中,在编译“foo.c”时,并不会将参数“-DFOO=1”传给编译器。

LOCAL_EXPORT_CPPFLAGS

功能和前面的LOCAL_EXPORT_CFLAGS一样,但只是对于编译C++的代码有效。 LOCAL_EXPORT_C_INCLUDES 这个变量也是导出到别的依赖模块的,但是导出的是包含路径。 还是用前面的例子,如果在“bar.c”中,要包含模块“foo”的头文件,有两种做法: 1)可以在“bar”模块中定义LOCAL_C_INCLUDES变量,将“foo”模块的路径赋值给它; 2)可以在“foo”自己模块中定义LOCAL_EXPORT_C_INCLUDES变量,将“foo”模块自己所在的路径赋值给它。

LOCAL_EXPORT_LDFLAGS

这个变量也是导出到别的依赖模块的,但是导出的是传给链接器的选项。

LOCAL_EXPORT_LDLIBS

这个变量也是导出到别的依赖模块的,但是导出的是用“-l”前缀来表示的,模块所需特定系统库的名字。

注意,这种通过其它模块的LOCAL_EXPORT_LDLIBS变量导入进来的链接器选项,会添加到在本模块LOCAL_LDLIBS变量中声明的选项之后。 通常这个变量用于这种场景,即有一个模块要编译成静态库,而这个模块又要依赖于一个系统提供的动态库。那么这时,就可以用LOCAL_EXPORT_LDLIBS变量把这个依赖关系导出出去,而不是在别的依赖这个静态库的模块中用LOCAL_LDLIBS变量来声明对这个系统库的依赖。因为,从逻辑上来说,这个依赖关系是静态库引入的,而不是依赖于这个静态库的模块。例如:

代码语言:javascript
复制
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参数,并且这个参数是在所有其它参数的最后。也就是表示“bar”模块是依赖于系统所提供的日志动态库的,因为“bar”模块依赖于“foo”模块,而“foo”模块依赖于系统的日志模块。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.12.27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 原文
    • 简单的例子
      • 自定义变量
        • CLEAR_VARS
        • BUILD_SHARED_LIBRARY
        • BUILD_STATIC_LIBRARY
        • PREBUILT_SHARED_LIBRARY
        • PREBUILD_STATIC_LIBRARY
        • TARGET_ARCH
        • TARGET_PLATFORM
        • TARGET_ARCH_ABI
        • TARGET_ABI
      • NDK提供的宏函数
        • my-dir
        • all-subdir-makefiles
        • this-makefile
        • parent-makefile
        • grand-parent-makefile
        • import-module
      • 模块描述变量
        • LOCAL_PATH
        • LOCAL_MODULE
        • LOCAL_MODULE_FILENAME
        • LOCAL_SRC_FILES
        • LOCAL_CPP_EXTENSION
        • LOCAL_CPP_FEATURES
        • LOCAL_C_INCLUDES
        • LOCAL_CFLAGS
        • LOCAL_CPPFLAGS / LOCAL_CXXFLAGS
        • LOCAL_STATIC_LIBRARIES
        • LOCAL_SHARED_LIBRARIES
        • LOCAL_LDLIBS
        • LOCAL_LDFLAGS
        • LOCAL_ALLOW_UNDEFINED_SYMBOLS
        • LOCAL_ARM_MODE
        • LOCAL_ARM_NEON
        • LOCAL_DISABLE_NO_EXECUTE
        • LOCAL_DISABLE_RELRO
        • LOCAL_EXPORT_CFLAGS
        • LOCAL_EXPORT_CPPFLAGS
        • LOCAL_EXPORT_LDFLAGS
        • LOCAL_EXPORT_LDLIBS
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档