• 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中的或者由其生成的程序集文件。

  • 相关阅读:
    阿里云高防回源IP的限制
    塑料袋厚度一般几丝
    h3c v5系列 和v7系列 端口上的qos限速命令
    ikuai8爱快sdwan的测试。
    写给小白的开源编译器
    GitHub 毕业年鉴「GitHub 热点速览 v.22.20」
    SpringBoot学习笔记(六)——分页、跨域、上传、定制banner、Lombok
    SpringBoot学习笔记(七)——综合示例BookStore图书管理系统
    Nginx配置分段下载
    数据安全KMS
  • 原文地址:https://www.cnblogs.com/Cqlismy/p/11801450.html
Copyright © 2020-2023  润新知