android.mk_android.mk详解 - CSDN
  • android.mk 详解

    千次阅读 2016-08-26 15:06:14
    LOCAL_PATH必须位于Android.mk文件的最开始。它是用来定位源文件的位置,$(call my-dir)的作用就是返回当前目录的路径。Android.mk 定义 属性 方法 Android.mk文件是GNU Makefile的一小部分,它用来对Android程序...
    LOCAL_PATH
    必须位于Android.mk文件的最开始。它是用来定位源文件的位置,$(call my-dir)的作用就是返回当前目录的路径。
    Android.mk 定义 属性 方法
     
    Android.mk文件是GNU Makefile的一小部分,它用来对Android程序进行编译。
    因为所有的编译文件都在同一个 GNU MAKE 执行环境中进行执行,而Android.mk中所有的变量都是全局的。因此,您应尽量少声明变量,不要认为某些变量在解析过程中不会被定义。
    一个Android.mk文件可以编译多个模块,每个模块属下列类型之一:
      1)APK程序
      一般的Android程序,编译打包生成apk文件
      2)JAVA库
      java类库,编译打包生成jar文件
      3)C\C++应用程序
     可执行的C\C++应用程序
      4)C\C++静态库
    编译生成C\C++静态库,并打包成.a文件
      5)C\C++共享库
    编译生成共享库(动态链接库),并打包成.so文, 有且只有共享库才能被安装/复制到您的应用软件(APK)包中。
      可以在每一个Android.mk file 中定义一个或多个模块,你也可以在几个模块中使用同一个
    源代码文件。  编译系统为你处理许多细节问题。例如,你不需要在你的 Android.mk 中列出头文件和依
    赖文件。编译系统将会为你自动处理这些问题。这也意味着,在升级 NDK 后,你应该
    得到新的toolchain/platform支持,而且不需要改变你的 Android.mk 文件。
      注意,NDK的Anroid.mk语法同公开发布的Android平台开源代码的Anroid.mk语法很接近,然而编译系统实现他们的
    方式却是不同的,这是故意这样设计的,可以让程序开发人员重用外部库的源代码更容易。
      在描述语法细节之前,咱们来看一个简单的"hello world"的例子,比如,下面的文件:
     sources/helloworld/helloworld.c
     sources/helloworld/Android.mk
     'helloworld.c'是一个 JNI 共享库,实现返回"hello world"字符串的原生方法。相应的
    Android.mk 文件会象下面这样:
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE:= helloworld
    LOCAL_SRC_FILES := helloworld.c
    include $(BUILD_SHARED_LIBRARY)
      解释一下几行代码:
      LOCAL_PATH := $(call my-dir)
      一个Android.mk file首先必须定义好LOCAL_PATH变量。它表示是当前文件的路径。
    在这个例子中, 宏函数‘my-dir’,  由编译系统提供, 用于返回当前路径(即包含Android.mk file
    文件的目录)。
      include $(CLEAR_VARS)
      CLEAR_VARS 由编译系统提供(可以在 android 安装目录下的/build/core/config.mk 文件看到其定义,为 CLEAR_VARS:=$(BUILD_SYSTEM)/clear_vars.mk),指定让GNU MAKEFILE该脚本为你清除许多 LOCAL_XXX 变量 ( 例如 LOCAL_MODULE , LOCAL_SRC_FILES ,LOCAL_STATIC_LIBRARIES,等等…),除 LOCAL_PATH。这是必要的,因为所有的编译文件都在同一个 GNU MAKE 执行环境中,所有的变量都是全局的。所以我们需要先清空这些变量(LOCAL_PATH除外)。又因为LOCAL_PATH总是要求在每个模块中都要进行设置,所以并需要清空它。
    另外注意,该语句的意思就是把CLEAR_VARS变量所指向的脚本文件包含进来。
      LOCAL_MODULE := helloworld
      LOCAL_MODULE 变量必须定义,以标识你在 Android.mk 文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'foo'的共享库模块,将会生成'libfoo.so'文件。
    注意:如果把库命名为‘libhelloworld’,编译系统将不会添加任何的 lib 前缀,也会生成 libhelloworld.so。
      LOCAL_SRC_FILES := helloworld.c
      LOCAL_SRC_FILES 变量必须包含将要编译打包进模块中的 C 或 C++源代码文件。不用
    在这里列出头文件和包含文件,编译系统将会自动找出依赖型的文件,当然对于包含文件,你包含时指定的路径应该正确。
    注意,默认的 C++源码文件的扩展名是‘.cpp’ 。指定一个不同的扩展名也是可能的,只要定义LOCAL_DEFAULT_CPP_EXTENSION 变量,不要忘记开始的小圆点(也就是定义为  ‘.cxx’,而不是‘cxx’)
      include $(BUILD_SHARED_LIBRARY)
      BUILD_SHARED_LIBRARY 是编译系统提供的变量,指向一个 GNU Makefile 脚本(应该
    就是在 build/core  目录下的 shared_library.mk) ,将根据LOCAL_XXX系列变量中的值,来编译生成共享库(动态链接库)。
    如果想生成静态库,则用BUILD_STATIC_LIBRARY
      在NDK的sources/samples目录下有更复杂一点的例子,写有注释的 Android.mk 文件。
    二、自定义变量
     以下是在 Android.mk中依赖或定义的变量列表, 可以定义其他变量为自己使用,但是NDK编译系统保留下列变量名:
     -以 LOCAL_开头的名字(例如 LOCAL_MODULE)
     -以 PRIVATE_, NDK_ 或 APP_开头的名字(内部使用)
     -小写名字(内部使用,例如‘my-dir’)
      如果为了方便在 Android.mk 中定义自己的变量,建议使用 MY_前缀,一个小例子:
    MY_SOURCES := foo.c
    ifneq ($(MY_CONFIG_BAR),)
     MY_SOURCES += bar.c
    endif
    LOCAL_SRC_FILES += $(MY_SOURCES)
    注意:‘:=’是赋值的意思;'+='是追加的意思;‘$’表示引用某变量的值。
    三、GNU Make系统变量
      这些 GNU Make变量在你的 Android.mk 文件解析之前,就由编译系统定义好了。注意在
    某些情况下,NDK可能分析 Android.mk 几次,每一次某些变量的定义会有不同。
      (1)CLEAR_VARS:  指向一个编译脚本,几乎所有未定义的 LOCAL_XXX 变量都在"Module-description"节中列出。必须在开始一个新模块之前包含这个脚本:include$(CLEAR_VARS),用于重置除LOCAL_PATH变量外的,所有LOCAL_XXX系列变量。
      (2)BUILD_SHARED_LIBRARY:  指向编译脚本,根据所有的在 LOCAL_XXX 变量把列出的源代码文件编译成一个共享库。
      注意,必须至少在包含这个文件之前定义 LOCAL_MODULE 和 LOCAL_SRC_FILES。
    (3) BUILD_STATIC_LIBRARY:  一个 BUILD_SHARED_LIBRARY 变量用于编译一个静态库。静态库不会复制到的APK包中,但是能够用于编译共享库。
    示例:include $(BUILD_STATIC_LIBRARY)
    注意,这将会生成一个名为 lib$(LOCAL_MODULE).a 的文件
      (4)TARGET_ARCH: 目标 CPU平台的名字,  和 android 开放源码中指定的那样。如果是
    arm,表示要生成 ARM 兼容的指令,与 CPU架构的修订版无关。
      (5)TARGET_PLATFORM: Android.mk 解析的时候,目标 Android 平台的名字.详情可参考/development/ndk/docs/stable- apis.txt.
     android-3 -> Official Android 1.5 system images
     android-4 -> Official Android 1.6 system images
     android-5 -> Official Android 2.0 system images
      (6)TARGET_ARCH_ABI:  暂时只支持两个 value,armeabi 和 armeabi-v7a。在现在的版本中一般把这两个值简单的定义为 arm, 通过 android  平台内部对它重定义来获得更好的匹配。其他的 ABI 将在以后的 NDK 版本中介绍,它们会有不同的名字。注意虽然所有基于
    ARM的ABI都会把 'TARGET_ARCH'定义成‘arm’, 但是会有不同的‘TARGET_ARCH_ABI’。
    ( 7 ) TARGET_ABI:  目标平台和 ABI 的组合,它事实上被定义成$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)  ,在想要在真实的设备中针对一个特别的目标系统进行测试时,会有用。在默认的情况下,它会是'android-3-arm'。
    五、模块描述变量
      下面的变量用于向编译系统描述你的模块。你应该定义在'include  $(CLEAR_VARS)'和'include $(BUILD_XXXXX)'之间。正如前面描写的那样,$(CLEAR_VARS)是一个脚本,清除所有这些变量。
      (1) LOCAL_PATH:  这个变量用于给出当前文件的路径。必须在 Android.mk 的开头定义,可以这样使用:LOCAL_PATH := $(call my-dir)  这个变量不会被$(CLEAR_VARS)清除,因此每个 Android.mk 只需要定义一次(即使在一个文件中定义了几个模块的情况下)。
      (2)LOCAL_MODULE: 这是模块的名字,它必须是唯一的,而且不能包含空格。必须在包含任一的$(BUILD_XXXX)脚本之前定义它。模块的名字决定了生成文件的名字。例如,如果一个一个共享库模块的名字是,那么生成文件的名字就是 lib.so。但是,在的 NDK 生成文
    件中(或者 Android.mk 或者 Application.mk),应该只涉及(引用)有正常名字的其他模块。
      (3)LOCAL_SRC_FILES:  这是要编译的源代码文件列表。只要列出要传递给编译器的文件,因为编译系统自动计算依赖。注意源代码文件名称都是相对于 LOCAL_PATH的,你可以使用路径部分,例如:
    LOCAL_SRC_FILES := foo.c toto/bar.c\
    Hello.c
     文件之间可以用空格或Tab键进行分割,换行请用"\".如果是追加源代码文件的话,请用LOCAL_SRC_FILES +=
    注意:在生成文件中都要使用UNIX风格的斜杠(/).windows风格的反斜杠不会被正确的处理。
    注意:可以LOCAL_SRC_FILES := $(call all-subdir-java-files)这种形式来包含local_path目录下的所有java文件。
      (4) LOCAL_CPP_EXTENSION:  这是一个可选变量, 用来指定C++代码文件的扩展名,默认是'.cpp',但是可以改变它,比如:
    LOCAL_CPP_EXTENSION := .cxx
      (5) LOCAL_C_INCLUDES:  可选变量,表示头文件的搜索路径。默认的头文件的搜索路径是LOCAL_PATH目录。
      示例:LOCAL_C_INCLUDES := sources/foo或LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
      LOCAL_C_INCLUDES需要在任何包含LOCAL_CFLAGS/LOCAL_CPPFLAGS标志之前进行设置。

      (6)LOCAL_CFLAGS:  可选的编译器选项,在编译 C 代码文件的时候使用。这可能是有用的,指定一个附加的包含路径(相对于NDK的顶层目录),宏定义,或者编译选项。

    例如:LOCAL_CFLAGS := -D__STDINT_LIMITS -DINDEPENDENT_NODE -pipe -fstrict-aliasing -pthread -fexceptions -fpermissive -fno-inline  -frtti -Wdeprecated-declarations

      注意:不要在 Android.mk 中改变 optimization/debugging 级别,只要在 Application.mk 中指定合适的信息,就会自动地为你处理这个问题,在调试期间,会让 NDK自动生成有用的数据文件。
      (7)LOCAL_CXXFLAGS:  与 LOCAL_CFLAGS同理,针对 C++源文件。
      (8)LOCAL_CPPFLAGS:  与 LOCAL_CFLAGS同理,但是对 C 和 C++ source files都适用。
      (9)LOCAL_STATIC_LIBRARIES: 表示该模块需要使用哪些静态库,以便在编译时进行链接。
      (10)LOCAL_SHARED_LIBRARIES:  表示模块在运行时要依赖的共享库(动态库),在链接时就需要,以便在生成文件时嵌入其相应的信息。注意:它不会附加列出的模块到编译图,也就是仍然需要在Application.mk 中把它们添加到程序要求的模块中。

      (11)LOCAL_LDLIBS:  编译模块时要使用的附加的链接器选项。这对于使用‘-l’前缀传递指定库的名字是有用的。

    例如,LOCAL_LDLIBS := -lz表示告诉链接器生成的模块要在加载时刻链接到/system/lib/libz.so,还有比如-llog -lz -landroid等。
      可查看 docs/STABLE-APIS.TXT 获取使用 NDK发行版能链接到的开放的系统库列表。
      (12) LOCAL_ALLOW_UNDEFINED_SYMBOLS:  默认情况下, 在试图编译一个共享库时,任何未定义的引用将导致一个“未定义的符号”错误。这对于在源代码文件中捕捉错误会有很大的帮助。然而,如果因为某些原因,需要不启动这项检查,可把这个变量设为‘true’。
    注意相应的共享库可能在运行时加载失败。(这个一般尽量不要去设为 true)。
      (13) LOCAL_ARM_MODE: 默认情况下, arm目标二进制会以 thumb 的形式生成(16 位),你可以通过设置这个变量为 arm如果你希望你的 module 是以 32 位指令的形式。
    'arm' (32-bit instructions) mode. E.g.:
    LOCAL_ARM_MODE := arm
    注意:可以在编译的时候告诉系统针对某个源码文件进行特定的类型的编译
    比如,LOCAL_SRC_FILES := foo.c bar.c.arm  这样就告诉系统总是将 bar.c 以arm的模式编译。
    (14)LOCAL_MODULE_PATH 和 LOCAL_UNSTRIPPED_PATH
    在 Android.mk 文件中, 还可以用LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH指定最后的目标安装路径.
    不同的文件系统路径用以下的宏进行选择:
      TARGET_ROOT_OUT:表示根文件系统。
       TARGET_OUT:表示 system文件系统。
       TARGET_OUT_DATA:表示 data文件系统。
    用法如:LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT)
    至于LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的区别,暂时还不清楚。
    七、GNU Make‘功能’宏
    GNU Make‘功能’宏,必须通过使用'$(call  )'来调用,调用他们将返回文本化的信息。
    (1)my-dir:返回当前 Android.mk 所在的目录的路径,相对于 NDK 编译系统的顶层。这是有用的,在 Android.mk 文件的开头如此定义:
    LOCAL_PATH := $(call my-dir)
    (2)all-subdir-makefiles: 返回一个位于当前'my-dir'路径的子目录中的所有Android.mk的列表。
    例如,看下面的目录层次:
    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 中的文件。
    (3)this-makefile:  返回当前Makefile 的路径(即这个函数调用的地方)
    (4)parent-makefile:  返回调用树中父 Makefile 路径。即包含当前Makefile的Makefile 路径。
    (5)grand-parent-makefile:返回调用树中父Makefile的父Makefile的路径
    八、 Android.mk 使用模板
      在一个 Android.mk 中可以生成多个APK应用程序,JAVA库,C\C++可执行程序,C\C++动态库和C\C++静态库。
    (1)编译APK应用程序模板。
    关于编译APK应用程序的模板请参照《Android.mk编译APK范例》
    (2)编译JAVA库模板
      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      # Build all java files in the java subdirectory
      LOCAL_SRC_FILES := $(call all-subdir-java-files)
      # Any libraries that this library depends on
      LOCAL_JAVA_LIBRARIES := android.test.runner
      # The name of the jar file to create
      LOCAL_MODULE := sample
      # Build a static jar file.
      include $(BUILD_STATIC_JAVA_LIBRARY)
      注:LOCAL_JAVA_LIBRARIES := android.test.runner表示生成的JAVA库的jar文件名
    (3)编译C/C++应用程序模板如下:
    LOCAL_PATH := $(call my-dir)
    #include $(CLEAR_VARS)
    LOCAL_SRC_FILES := main.c
    LOCAL_MODULE := test_exe
    #LOCAL_C_INCLUDES :=
    #LOCAL_STATIC_LIBRARIES :=
    #LOCAL_SHARED_LIBRARIES :=
    include $(BUILD_EXECUTABLE)
    注:‘:=’是赋值的意思,'+='是追加的意思,‘$’表示引用某变量的值
    LOCAL_SRC_FILES中加入源文件路径,LOCAL_C_INCLUDES中加入需要的头文件搜索路径
    LOCAL_STATIC_LIBRARIES 加入所需要链接的静态库(*.a)的名称,
    LOCAL_SHARED_LIBRARIES 中加入所需要链接的动态库(*.so)的名称,
    LOCAL_MODULE表示模块最终的名称,BUILD_EXECUTABLE 表示以一个可执行程序的方式进行编译。
    (4)编译C\C++静态库
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_SRC_FILES := \
     helloworld.c
    LOCAL_MODULE:= libtest_static
     #LOCAL_C_INCLUDES :=
    #LOCAL_STATIC_LIBRARIES :=
    #LOCAL_SHARED_LIBRARIES :=
    include $(BUILD_STATIC_LIBRARY)
    和上面相似,BUILD_STATIC_LIBRARY 表示编译一个静态库。
    (5)编译C\C++动态库的模板
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_SRC_FILES := helloworld.c
    LOCAL_MODULE := libtest_shared
    TARGET_PRELINK_MODULES := false
    #LOCAL_C_INCLUDES :=
    #LOCAL_STATIC_LIBRARIES :=
    #LOCAL_SHARED_LIBRARIES :=
    include $(BUILD_SHARED_LIBRARY)

    展开全文
  • Android.mk超全详细用法

    千次阅读 2018-12-28 13:40:52
    Android.mk 是是Android提供的一种makefile文件,用来指定诸如编译生成so库名、可执行bin文件以及jar包以及apk   最简单的Android.mk     LOCAL_PATH := $(call my-dir) #定义了当前模块的相对路径 ...

    Android.mk 是Android提供的一种makefile文件,用来指定诸如编译生成so库名、可执行bin文件以及jar包以及apk

     

    最简单的Android.mk

     

    
     
    LOCAL_PATH := $(call my-dir) #定义了当前模块的相对路径 include $(CLEAR_VARS)#清空当前环境变量 LOCAL_MODULE:= test1 #编译生成的目标名称 LOCAL_SRC_FILES := test1.c #编译该模块需要的源文件 include $(BUILD_EXECUTABLE) #编译所生成的目标文件格式

     

    
     


    下面介绍几种常见用法

     

     

    1.1、多源码编译

    方法一:直接引用多个.c文件

     

    LOCAL_SRC_FILES := test1.c\
                       test2.c\
                       test3.c\

    方法二:使用系统提供的函数处理
     

     

    1.2、编译多个目标文件

     

    
     
    LOCAL_PATH := $(call my-dir) #定义了当前模块的相对路径 include $(CLEAR_VARS)#清空当前环境变量 LOCAL_MODULE:= test1 #编译生成的目标名称 LOCAL_SRC_FILES := test1.c #编译该模块需要的源文件 include $(BUILD_EXECUTABLE) #编译所生成的目标文件格式
    include $(CLEAR_VARS)#清空当前环境变量
    
    LOCAL_MODULE:= test2    #编译生成的目标名称
    
    LOCAL_SRC_FILES := test2.c    #编译该模块需要的源文件
    
    include $(BUILD_EXECUTABLE) #编译所生成的目标文件格式

     

    
     

     

     

     

    1.3、编译动态库、静态库

     

    将编译动态库:
    	编译类型修改为 BUILD_SHARED_LIBRARY
    编译静态库:
    	编译类型修改为 BUILD_STATIC_LIBRARY

     

    1.4、编译jar包,APK

    编译apk

      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_SRC_FILES := $(call all-subdir-java-files)
      LOCAL_PACKAGE_NAME := LocalPackage
      include $(BUILD_PACKAGE)

     

    编译jar包

     

      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_SRC_FILES := $(call all-subdir-java-files)
      LOCAL_MODULE := com.test.myjar
      include $(BUILD_STATIC_JAVA_LIBRARY):编译成静态jar包
      include $(BUILD_JAVA_LIBRARY):编译生成共享jar包
    
    


    1.5、Android.mk中的判断语句

     

    ifeq($(VALUE), x)	#ifneq
      do_yes
    else
      do_no
    endif
    
    ifeq/ifneq:根据判断条件执行相关编译
    

     

     

     

    展开全文
  • Android.mk 用法汇总

    千次阅读 2013-03-21 10:52:50
    (1)Android.mk文件首先需要指定LOCAL_PATH变 量,用于查找源文件。由于一般情况下  Android.mk和需要编译的源文件在同一目录下,所以定义成如下形式: LOCAL_PATH:=$(call my-dir) 上面的语句的意思是将LOCAL_...


    (1)Android.mk文件首先需要指定LOCAL_PATH变 量,用于查找源文件。由于一般情况下
     
    Android.mk和需要编译的源文件在同一目录下,所以定义成如下形式:
    LOCAL_PATH:=$(call my-dir)
    上面的语句的意思是将LOCAL_PATH变量定义成本文件所在目录路径。

    (2)Android.mk中可以定义多个编译模块,每个编译模块都 是以include $(CLEAR_VARS)开始 
    以include $(BUILD_XXX)结束 。
    include $(CLEAR_VARS)
    CLEAR_VARS由编译系统提供,指定 让GNU MAKEFILE为你清除除LOCAL_PATH以外的所有LOCAL_XXX变量,
    如 LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_SHARED_LIBRARIES,LOCAL_STATIC_LIBRARIES 等。
    include $(BUILD_STATIC_LIBRARY)表示编译成静态库
    include $(BUILD_SHARED_LIBRARY)表示编译成动态库。
    include $(BUILD_EXECUTABLE)表示编译成可执行程序



    个Android.mk file用来向编译系统描述你的源代码。具体来说:该文件是GNU Makefile的一小部分,会被编译系统解析一次或多次。你可以在每一个Android.mk file中定义一个或多个模块,你也可以在几个模块中使用同一个源代码文件。编译系统为你处理许多细节问题。例如,你不需要在你的Android.mk中列出头文件和依赖文件。NDK编译系统将会为你自动处理这些问题。这也意味着,在升级NDK后,你应该得到新的toolchain/platform支持,而且不需要改变你的Android.mk文件。

          先看一个简单的例子:一个简单的"hello world",比如下面的文件:
    sources/helloworld/helloworld.c 
    sources/helloworld/Android.mk
    相应的Android.mk文件会象下面这样:
    ---------- cut here ------------------
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE
    := helloworld
    LOCAL_SRC_FILES := helloworld.c
    include $(BUILD_SHARED_LIBRARY)
    ---------- cut here ------------------
          我们来解释一下这几行代码:
    LOCAL_PATH := $(call my-dir) 
    一个Android.mk file首先必须定义好LOCAL_PATH变量。它用于在开发树中查找源文件。在这个例子中,宏函数’my-dir’, 由编译系统提供,用于返回当前路径(即包含Android.mk file文件的目录)。
    include $( CLEAR_VARS)
    CLEAR_VARS由编译系统提供,指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
    LOCAL_MODULE := helloworld
    LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'foo'的共享库模块,将会生成'libfoo.so'文件。
    LOCAL_SRC_FILES := helloworld.c 
    LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意,你不用在这里列出头文件和包含文件,因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文件就好。

     

          在Android中增加本地程序或者库,这些程序和库与其所载路径没有任何关系,只和它们的Android.mk文件有关系。Android.mk和普通的Makefile有所不同,它具有统一的写法,主要包含一些系统公共的宏。
         在一个Android.mk中可以生成多个可执行程序、动态库和静态库。
    1,编译应用程序的模板:
         #Test Exe
         LOCAL_PATH := $(call my-dir)
         #include $(CLEAR_VARS)
         LOCAL_SRC_FILES:= main.c
         LOCAL_MODULE:= test_exe
         #LOCAL_C_INCLUDES :=
         #LOCAL_STATIC_LIBRARIES :=
         #LOCAL_SHARED_LIBRARIES :=
         include $(BUILD_EXECUTABLE)
    (菜鸟级别解释::=是赋值的意思,$是引用某变量的值)LOCAL_SRC_FILES中加入源文件路径,LOCAL_C_INCLUDES 中加入所需要包含的头文件路径,LOCAL_STATIC_LIBRARIES加入所需要链接的静态库(*.a)的名称,LOCAL_SHARED_LIBRARIES中加入所需要链接的动态库(*.so)的名称,LOCAL_MODULE表示模块最终的名称,BUILD_EXECUTABLE表示以一个可执行程序的方式进行编译。
    2,编译静态库的模板:
         #Test Static Lib
         LOCAL_PATH := $(call my-dir)
         include $(CLEAR_VARS)
         LOCAL_SRC_FILES:= /
                   helloworld.c
         LOCAL_MODULE:= libtest_static
         #LOCAL_C_INCLUDES :=
         #LOCAL_STATIC_LIBRARIES :=
         #LOCAL_SHARED_LIBRARIES :=
         include $(BUILD_STATIC_LIBRARY)
    一般的和上面相似,BUILD_STATIC_LIBRARY表示编译一个静态库。
    3,编译动态库的模板:
         #Test Shared Lib
         LOCAL_PATH := $(call my-dir)
         include $(CLEAR_VARS)
         LOCAL_SRC_FILES:= /
                   helloworld.c
         LOCAL_MODULE:= libtest_shared
         TARGET_PRELINK_MODULES := false
         #LOCAL_C_INCLUDES :=
         #LOCAL_STATIC_LIBRARIES :=
         #LOCAL_SHARED_LIBRARIES :=
          include $(BUILD_SHARED_LIBRARY)
    一般的和上面相似,BUILD_SHARED_LIBRARY表示编译一个静态库。
          以上三者的生成结果分别在如下,generic依具体target会变:
    out/target/product/generic/obj/EXECUTABLE
    out/target/product/generic/obj/STATIC_LIBRARY
    out/target/product/generic/obj/SHARED_LIBRARY
          每个模块的目标文件夹分别为:
    可执行程序:XXX_intermediates
    静态库:      XXX_static_intermediates
    动态库:      XXX_shared_intermediates
          另外,在Android.mk文件中,还可以指定最后的目标安装路径,用LOCAL_MODULE_PATH和LOCAL_UNSTRIPPED_PATH来指定。不同的文件系统路径用以下的宏进行选择:
    TARGET_ROOT_OUT:表示根文件系统。
    TARGET_OUT:表示system文件系统。
    TARGET_OUT_DATA:表示data文件系统。
    用法如:
     CAL_MODULE_PATH:=$(TARGET_ROOT_OUT)


    From:

    http://blog.csdn.net/zwj0403/article/details/6089338


    android是什么就不用说了,android自从开源以来,就受到很多人的追捧。当然,一部人追捧它是因为它是Google开发的。 对一个程序员来说,一个系统值不值得追捧得要拿代码来说话。我这里并不打算分析android的代码,而是android的makefile,我想通过分 析andorid的makefile来告诉大家如何写makefile。

    对于一个程序新手而言,好的IDE是他们追捧的对象。但当他接触的代码多了之后,就会逐渐发现IDE不够用了,因为有好多东西用IDE是不好做的, 例如自动编译,测试,版本控制,编译定制等。这跟政治课上的一句话有点像:资本主义开始的时候是促进生产力发展的,但到了后来又成了阻碍生产力发展的因素 了。如果一个程序不能摆脱IDE的限制(不是不用,而是要有选择的用),那么他就很难提高。要知道,IDE和makefile代表了两种不同的思 想:IDE根据强调的是简化计算机与用户的交互;而makefile体现的是自动化。

    对于一个一开始就接触linux的人来说,makefile可能是比较容易学的(熟能生巧),对于一个一开始就接触Windows的人来 说,makefile就不太好学,这主要是应该很多时候会不自觉地去用Visual Studio(Visual Studio是个好东西,特别是它的调试)。不知道大叫有没有这个的感觉:一个人如果先接触c,再接触java会比较容易点;如果一个人先接触java, 再接触c,就会比较反感c。

    这个先引用一下百度百科对makefile的一些描述:

    一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件 需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。

    makefile带来的好处就是——“自动化编译” ,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make 。可见,makefile都成为了一种在工程方面的编译方法。

    Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。 而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。makefile 文件是许多编译器--包括 Windows NT 下的编译器--维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。

    对于android而言,android使用的是GNU的make,因此它的makefile格式也是GNU的makefile格式。现在网络上关 于makefile最好的文档就是陈皓的《跟我一起写makefile》,这份文档对makefile进行了详细的介绍,因此推荐大家先看这份文档(电子 版可以到http://pipi.googlecode.com/files/How%20to%20Write%20makefile.pdf 下载,陈皓的blog在http://blog.csdn.net/haoel )。

    android最顶层的目录结构如下:


    |-- Makefile        (全局的Makefile) 
    |-- bionic          (Bionic含义为仿生,这里面是一些基础的库的源代码) 
    |-- bootloader      (引导加载器) 
    |-- build           (build目录中的内容不是目标所用的代码,而是编译和配置所需要的脚本和工具) 
    |-- dalvik          (JAVA虚拟机) 
    |-- development     (程序开发所需要的模板和工具) 
    |-- external        (目标机器使用的一些库) 
    |-- frameworks      (应用程序的框架层) 
    |-- hardware        (与硬件相关的库) 
    |-- kernel          (Linux2.6的源代码) 
    |-- packages        (Android的各种应用程序) 
    |-- prebuilt        (Android在各种平台下编译的预置脚本) 
    |-- recovery        (与目标的恢复功能相关) 
    `-- system          (Android的底层的一些库)

    本文将要分析的是build目录下的makefile和shell文件,android的代码是1.5的版本。

    主要的目录结构如下:

    1.makefile入门

        1.1 makefile helloworld

        1.2 用makefile构建交叉编译环境

        1.3 makefile里面的一些技巧

    2.android makefile分析

        2.1 android shell分析

        2.2 android build下的各个makefile分析

    3. android其他目录的android.mk分析

    由于最近研究生要毕业了,得找工作了,所以可能分析有时候会间断一两天,望大家能够谅解。

    作为序的最后,大家先通过网络的一些文章来了解一下andoroid的makefile。

    1.Android build system

    2.Android Building System 分析

    3.Android Build System(介绍使用)

    4. http://source.android.com/porting/build_cookbook.html


    android makefile(android.mk)分析(1)


    1.1 makefile helloworld

    Makefile 的规则如下:

    target ... : prerequisites ...

    command ... ...

    target可以是一个目标文件,也可以是Object File(例如helloworld.obj),也可以是执行文件和标签。

    prerequisites就是生成target所需要的文件或是目标。

    command 也就是要达到target这个目标所需要执行的命令。这里没有说“使用生成target所需要执行的命令”,是因为target可能是标签。需要注意的是 command前面必须是TAB键,而不是空格,因此喜欢在编辑器里面将TAB键用空格替换的人需要特别小心了。

    我们写程序一般喜欢写helloworld,当我们写了一个c的helloworld之后,我们该如何写helloworld来编译helloworld.c呢?

    下面就是编译helloworld的makefile。

    helloworld : helloworld.o

        cc -o helloworld helloworld .o

    helloworld.o : helloworld.c

        cc -c main.c

    clean:

        rm helloworld helloworl.o

    之后我们执行make就可以编译helloworld.c了,执行make clean就可以清除编译结果了(其实就是删除helloworld helloworl.o)。

    可能有人问为什么执行make就会生成helloworld呢?这得从make的默认处理说起:make将makefile的第一个target作为作为最终的

    target,凡是这个规则依赖的规则都将被执行,否则就不会执行。所以在执行make的时候,clean这个规则就没有被执行。

    上 面的是最简单的makefile,复杂点makefile就开始使用高级点的技巧了,例如使用变量,使用隐式规则,执行负责点shell命令(常见的是字 符串处理和文件处理等),这里不打算介绍这些规则,后面在分析android的makefile时会结合具体代码进行具体分析,大家可以先看看陈皓的《跟 我一起写makefile》来了解了解。

    makefile的大体的结构是程序树形的,如下:

                                                         image

    这样写起makefile也简单,我们将要达到的目标作为第一个规则,然后将目标分解成子目标,然后一个个写规则,依次类推,直到最下面的规则很容易实现为止。这其实和算法里面的分治法很像,将一个复杂的问题分而治之。

    说 到树,我想到了编译原理里面的语法分析,语法分析里面有自顶而下的分析方法和自底而下的分析方法。当然makefile并不是要做语法分析,而是要做与语 法分析分析相反的事。(语法分析要做的是一个句子是不是根据语法可以推出来,而makefile要做的是根据规则生成一个command 执行队列。)不过makefile的规则和词法分析还是很像的。下面出一道编译原理上面的一个例子,大家可以理解一下makefile和词法分析的不同点 和相同点:

    <标识符>  -> <字母><字母数字串> 
        <字母数字串>  -> <字母><字母数字串>|<数字><字母数字串>|<下划线><字母数字串>|ε 
       <无符号整数> -> <数字><数字串> 
       <数字串> -> <数字><数字串>|ε 
      <加法运算符>  -> + 
       <减法运算符> -> - 
      <大于关系运算符> -> > 
       <大于等于关系运算符> -> >=

     

    最后,介绍一下autoconfautomake,使用这两个工具可以自动生成makefile。

    autoconf

    从 上面的图可以看出,通过autoscan,我们可以根据代码生成一个叫做configure.scan的文件,然后我们编辑这个文件,参数一个 configure.in的文件。接着我们写一个makefile.am的文件,然后就可以用automake生成makefile.in,最后,根据 makefile.in和configure就可以生成makefile了。在很多开源的工程里面,我们都可以看到 makefile.am,configure.in,makefine.in,configure文件,还有可能看到一个十分复杂的makefile文 件,许多人学习makefile的时候想通过看这个文件来学习,最终却发现太复杂了。如果我们知道这个文件是自动生成的,就理解这个makefile文件 为什么这个复杂了。

    欲更加详细的理解automake等工具,可以参考http://www.ibm.com/developerworks/cn/linux/l-makefile/ 。


    1.2 用makefile构建交叉编译环境

    这节的内容请参考http://blog.csdn.net/absurd/category/228434.aspx 里面的交叉编译场景分析,我只是说一下我做的步骤:

    1.下载交叉编译环境(http://www.codesourcery.com/downloads/public/gnu_toolchain/arm-none-linux-gnueabi)并安装,一般解压就可以了,然后将里面的bin目录加到环境变量的PATH里面,我的做法是在~/.bashrc最下面加一行:export PATH=$PATH:~/arm-2009q1/bin。

    2.在用户的home目录(cd ~)建一个目录cross-compile

    3.在cross-compile创建一个文件cross.env,内容如下:

    export WORK_DIR=~/cross-compile 
    export ROOTFS_DIR=$WORK_DIR/rootfs 
    export ARCH=arm 
    export PKG_CONFIG_PATH=$ROOTFS_DIR/usr/local/lib/pkgconfig:$ROOTFS_DIR/usr/lib/pkgconfig:$ROOTFS_DIR/usr/X11R6/lib/pkgconfig
    if [ ! -e "$ROOTFS_DIR/usr/local/include" ]; then mkdir -p $ROOTFS_DIR/usr/local/include;fi; 
    if [ ! -e "$ROOTFS_DIR/usr/local/lib" ]; then mkdir -p $ROOTFS_DIR/usr/local/lib; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/local/etc" ]; then mkdir -p $ROOTFS_DIR/usr/local/etc; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/local/bin" ]; then mkdir -p $ROOTFS_DIR/usr/local/bin; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/local/share" ]; then mkdir -p $ROOTFS_DIR/usr/local/share; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/local/man" ]; then mkdir -p $ROOTFS_DIR/usr/local/man; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/include" ]; then mkdir -p $ROOTFS_DIR/usr/include; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/lib" ]; then mkdir -p $ROOTFS_DIR/usr/lib; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/etc" ]; then mkdir -p $ROOTFS_DIR/usr/etc; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/bin" ]; then mkdir -p $ROOTFS_DIR/usr/bin; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/share" ]; then mkdir -p $ROOTFS_DIR/usr/share; fi; 
    if [ ! -e "$ROOTFS_DIR/usr/man" ]; then mkdir -p $ROOTFS_DIR/usr/man; fi;

    4.开启命令行,进入cross-compile目录下,执行. cross.env

    5.将编译linux时生产的头文件,so等拷贝到cross-compile目录下rootfs/usr对应的目录(头文件一般可以拷pc的,so一定要拷arm版的)。

    5.下载要编译的源代码,并放在cross-compile目录下

    6.按照http://blog.csdn.net/absurd/category/228434.aspx 里面的方法写makefile文件,放在cross-compile目录下

    7.在命令行执行make -f libxml2.mk(libxml2.mk为上一步写的makefile)。

    展开全文
  • Android mk详解

    千次阅读 2019-07-20 13:28:21
    Android.mk文件用于定义Application.mk、编译系统和环境变量所未定义的项目范围设置。它还可替换特定模块的项目范围设置。 Android.mk的语法支持将源文件分组为模块。模块是静态库、共享库或...

    概览

    Android.mk 文件位于项目 jni/ 目录的子目录中,用于向编译系统描述源文件和共享库。它实际上是编译系统解析一次或多次的微小 GNU makefile 片段。Android.mk 文件用于定义 Application.mk、编译系统和环境变量所未定义的项目范围设置。它还可替换特定模块的项目范围设置。

    Android.mk 的语法支持将源文件分组为模块。模块是静态库、共享库或独立的可执行文件。您可在每个 Android.mk 文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。编译系统只将共享库放入您的应用软件包。此外,静态库可生成共享库。

    除了封装库之外,编译系统还可为您处理各种其他事项。例如,您无需在 Android.mk 文件中列出头文件或生成的文件之间的显式依赖关系。NDK 编译系统会自动计算这些关系。因此,您应该能够享受到未来 NDK 版本中新工具链/平台支持带来的益处,而无需处理 Android.mk 文件。

    此文件的语法与随整个 Android 开源项目分发的 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_MODULELOCAL_SRC_FILES 和 LOCAL_STATIC_LIBRARIES。请注意,GNU Makefile 不会清除 LOCAL_PATH。此变量必须保留其值,因为系统在单一 GNU Make 执行环境(其中的所有变量都是全局变量)中解析所有编译控制文件。在描述每个模块之前,必须声明(重新声明)此变量。

    接下来,LOCAL_MODULE 变量存储您要编译的模块的名称。请在应用的每个模块中使用一次此变量。

    LOCAL_MODULE := hello-jni
        

     

    每个模块名称必须唯一,且不含任何空格。编译系统在生成最终共享库文件时,会对您分配给 LOCAL_MODULE 的名称自动添加正确的前缀和后缀。例如,上述示例会生成名为 libhello-jni.so的库。

    注意:如果模块名称的开头已经是 lib,则编译系统不会附加额外的 lib 前缀;而是按原样采用模块名称,并添加 .so 扩展名。因此,比如原来名为 libfoo.c 的源文件仍会生成名为 libfoo.so 的共享对象文件。此行为是为了支持 Android 平台源文件根据 Android.mk 文件生成的库;所有这些库的名称都以 lib 开头。

    下一行会列举源文件,以空格分隔多个文件:

    LOCAL_SRC_FILES := hello-jni.c
        

     

    LOCAL_SRC_FILES 变量必须包含要编译到模块中的 C 和/或 C++ 源文件列表。

    最后一行帮助系统将所有内容连接到一起:

    include $(BUILD_SHARED_LIBRARY)
        

     

    BUILD_SHARED_LIBRARY 变量指向一个 GNU Makefile 脚本,该脚本会收集您自最近 include 以来在 LOCAL_XXX 变量中定义的所有信息。此脚本确定要编译的内容以及编译方式。

    示例目录中有更为复杂的示例,包括带有注释的 Android.mk 文件供您参考。此外,示例:native-activity 详细介绍了该示例的 Android.mk 文件。最后,变量和宏提供了关于本部分中变量的更多信息。

    变量和宏

    编译系统提供了许多可在 Android.mk 文件中使用的变量。其中许多变量已预先赋值。另一些变量由您赋值。

    除了这些变量之外,您还可以自己定义任意变量。在定义变量时请注意,NDK 编译系统保留了下列变量名称:

    • 以 LOCAL_ 开头的名称,例如 LOCAL_MODULE
    • 以 PRIVATE_NDK_ 或 APP 开头的名称。编译系统在内部使用这些变量名。
    • 小写名称,例如 my-dir。编译系统也是在内部使用这些变量名。

    如果您为了方便而需要在 Android.mk 文件中定义自己的变量,建议在名称前附加 MY_

    NDK 定义的 include 变量

    本部分探讨了编译系统在解析 Android.mk 文件前定义的 GNU Make 变量。在某些情况下,NDK 可能会多次解析 Android.mk 文件,每次使用其中某些变量的不同定义。

    CLEAR_VARS

    此变量指向的编译脚本用于取消定义下文“开发者定义的变量”部分中列出的几乎所有 LOCAL_XXX 变量。在描述新模块之前,请使用此变量来包含此脚本。使用它的语法为:

    include $(CLEAR_VARS)
        

     

    BUILD_SHARED_LIBRARY

    此变量指向的编译脚本用于收集您在 LOCAL_XXX 变量中提供的模块的所有相关信息,以及确定如何根据您列出的源文件编译目标共享库。请注意,使用此脚本要求您至少已经为 LOCAL_MODULE 和 LOCAL_SRC_FILES 赋值(要详细了解这些变量,请参阅模块描述变量)。

    使用此变量的语法为:

    include $(BUILD_SHARED_LIBRARY)
        

     

    共享库变量会导致编译系统生成扩展名为 .so 的库文件。

    BUILD_STATIC_LIBRARY

    用于编译静态库的 BUILD_SHARED_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 相同,但用于预编译静态库。要详细了解如何使用预编译库,请参阅使用预编译库

    目标信息变量

    编译系统会根据 APP_ABI 变量所指定的每个 ABI 解析 Android.mk 一次,该变量通常在 Application.mk 文件中定义。如果 APP_ABI 为 all,则编译系统会根据 NDK 支持的每个 ABI 解析 Android.mk 一次。本部分介绍了编译系统每次解析 Android.mk 时定义的变量。

    TARGET_ARCH

    编译系统解析此 Android.mk 文件时面向的 CPU 系列。此变量是 armarm64x86 或 x86_64之一。

    TARGET_PLATFORM

    编译系统解析此 Android.mk 文件时面向的 Android API 级别编号。例如,Android 5.1 系统映像对应于 Android API 级别 22:android-22。如需平台名称和对应 Android 系统映像的完整列表,请参阅 Android NDK 原生 API。以下示例演示了使用此变量的语法:

    ifeq ($(TARGET_PLATFORM),android-22)
            # ... do something ...
        endif
        

     

    TARGET_ARCH_ABI

    编译系统解析此 Android.mk 文件时面向的 ABI。表 1 显示用于每个受支持 CPU 和架构的 ABI 设置。

    表 1. 不同 CPU 和架构的 ABI 设置。

    CPU 和架构 设置
    ARMv7 armeabi-v7a
    ARMv8 AArch64 arm64-v8a
    i686 x86
    x86-64 x86_64

    以下示例演示了如何检查 ARMv8 AArch64 是否为目标 CPU 与 ABI 的组合:

    ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
          # ... do something ...
        endif
        

     

    要详细了解架构 ABI 和相关兼容性问题,请参阅 ABI 管理

    未来的新目标 ABI 将使用不同的值。

    TARGET_ABI

    目标 Android API 级别与 ABI 的连接,特别适用于要针对实际设备测试特定目标系统映像的情况。例如,要检查搭载 Android API 级别 22 的 64 位 ARM 设备:

    ifeq ($(TARGET_ABI),android-22-arm64-v8a)
          # ... do something ...
        endif
        

     

    模块描述变量

    本部分中的变量会向编译系统描述您的模块。每个模块描述都应遵守以下基本流程:

    1. 使用 CLEAR_VARS 变量初始化或取消定义与模块相关的变量。
    2. 为用于描述模块的变量赋值。
    3. 使用 BUILD_XXX 变量设置 NDK 编译系统,使其将适当的编译脚本用于该模块。

    LOCAL_PATH

    此变量用于指定当前文件的路径。必须在 Android.mk 文件开头定义此变量。以下示例演示了如何定义此变量:

    LOCAL_PATH := $(call my-dir)
        

     

    CLEAR_VARS 所指向的脚本不会清除此变量。因此,即使 Android.mk 文件描述了多个模块,您也只需定义它一次。

    LOCAL_MODULE

    此变量用于存储模块名称。指定的名称必须唯一,并且不得包含任何空格。必须在包含任何脚本(CLEAR_VARS 的脚本除外)之前定义此变量。无需添加 lib 前缀或者 .so 或 .a 文件扩展名;编译系统会自动进行这些修改。在整个 Android.mk 和 Application.mk 文件中,请通过未经修改的名称引用模块。例如,以下行会导致生成名为 libfoo.so 的共享库模块:

    LOCAL_MODULE := "foo"
        

     

    如果希望生成的模块使用除“lib + LOCAL_MODULE 的值”以外的名称,您可使用 LOCAL_MODULE_FILENAME 变量为生成的模块指定自己选择的名称。

    LOCAL_MODULE_FILENAME

    此可选变量使您能够替换编译系统为其生成的文件默认使用的名称。例如,如果 LOCAL_MODULE 的名称为 foo,您可以强制系统将它生成的文件命名为 libnewfoo。以下示例演示了如何完成此操作:

    LOCAL_MODULE := foo
        LOCAL_MODULE_FILENAME := libnewfoo
        

     

    对于共享库模块,此示例将生成一个名为 libnewfoo.so 的文件。

    注意:您无法替换文件路径或文件扩展名。

    LOCAL_SRC_FILES

    此变量包含编译系统生成模块时所用的源文件列表。只列出编译系统实际传递到编译器的文件,因为编译系统会自动计算所有相关的依赖关系。请注意,您可以使用相对(相对于 LOCAL_PATH)和绝对文件路径。

    建议避免使用绝对文件路径;相对路径可以提高 Android.mk 文件的移植性。

    注意:务必在编译文件中使用 Unix 样式的正斜杠 (/)。编译系统无法正确处理 Windows 样式的反斜杠 (\)。

    LOCAL_CPP_EXTENSION

    可以使用此可选变量为 C++ 源文件指明 .cpp 以外的文件扩展名。例如,以下行将扩展名更改为 .cxx。(设置必须包含点。)

    LOCAL_CPP_EXTENSION := .cxx
        

     

    您可以使用此变量指定多个扩展名。例如:

    LOCAL_CPP_EXTENSION := .cxx .cpp .cc
        

     

    LOCAL_CPP_FEATURES

    您可使用此可选变量指明您的代码依赖于特定 C++ 功能。它会在编译过程中启用正确的编译器标记和链接器标记。对于预编译二进制文件,此变量还会声明二进制文件依赖于哪些功能,从而确保最终链接正常运行。建议您使用此变量,而不要直接在 LOCAL_CPPFLAGS 定义中启用 -frtti 和 -fexceptions

    使用此变量可让编译系统对每个模块使用适当的标记。使用 LOCAL_CPPFLAGS 会导致编译器将所有指定的标记用于所有模块,而不管实际需求如何。

    例如,要指明您的代码使用 RTTI(运行时类型信息),请输入:

    LOCAL_CPP_FEATURES := rtti
        

     

    要指明您的代码使用 C++ 异常,请输入:

    LOCAL_CPP_FEATURES := exceptions
        

     

    您还可以为此变量指定多个值。例如:

    LOCAL_CPP_FEATURES := rtti features
        

     

    描述值的顺序无关紧要。

    LOCAL_C_INCLUDES

    可以使用此可选变量指定相对于 NDK root 目录的路径列表,以便在编译所有源文件(C、C++ 和 Assembly)时添加到 include 搜索路径。例如:

    LOCAL_C_INCLUDES := sources/foo
        

     

    或者甚至:

    LOCAL_C_INCLUDES := $(LOCAL_PATH)/<subdirectory>/foo
        

     

    请在通过 LOCAL_CFLAGS 或 LOCAL_CPPFLAGS 设置任何对应的包含标记前定义此变量。

    在使用 ndk-gdb 启动原生调试时,编译系统也会自动使用 LOCAL_C_INCLUDES 路径。

    LOCAL_CFLAGS

    此可选变量用于设置在编译 C 和 C++ 源文件时编译系统要传递的编译器标记。这样,您就可以指定额外的宏定义或编译选项。可以使用 LOCAL_CPPFLAGS 仅为 C++ 指定标记。

    请勿尝试在 Android.mk 文件中更改优化/调试级别。编译系统可以使用 [pplication.mk] 文件中的相关信息自动处理此设置。这样,编译系统就可以生成供调试期间使用的有用数据文件。

    您可通过输入以下代码指定额外的 include 路径:

    LOCAL_CFLAGS += -I<path>,
        

     

    但是,最好使用 LOCAL_C_INCLUDES,因为这样也可以使用可用于 ndk-gdb 原生调试的路径。

    LOCAL_CPPFLAGS

    只编译 C++ 源文件时将传递的一组可选编译器标记。它们将出现在编译器命令行中的 LOCAL_CFLAGS后面。使用 LOCAL_CFLAGS 为 C 和 C++ 指定标记。

    LOCAL_STATIC_LIBRARIES

    此变量用于存储当前模块依赖的静态库模块列表。

    如果当前模块是共享库或可执行文件,此变量将强制这些库链接到生成的二进制文件。

    如果当前模块是静态库,此变量只是指出依赖于当前模块的其他模块也会依赖于列出的库。

    LOCAL_SHARED_LIBRARIES

    此变量会列出此模块在运行时依赖的共享库模块。此信息是链接时必需的信息,用于将相应的信息嵌入到生成的文件中。

    LOCAL_WHOLE_STATIC_LIBRARIES

    此变量是 LOCAL_STATIC_LIBRARIES 的变体,表示链接器应将相关的库模块视为完整归档。要详细了解完整归档,请参阅 GNU ld 文档的 --whole-archive 标记部分。

    多个静态库之间存在循环依赖关系时,此变量很有用。使用此变量编译共享库时,它将强制编译系统将静态库中的所有对象文件添加到最终二进制文件。但是,生成可执行文件时不会发生这种情况。

    LOCAL_LDLIBS

    此变量列出了在编译共享库或可执行文件时使用的额外链接器标记。利用此变量,您可使用 -l 前缀传递特定系统库的名称。例如,以下示例指示链接器生成在加载时链接到 /system/lib/libz.so 的模块:

    LOCAL_LDLIBS := -lz
        

     

    如需了解此 NDK 版本中可以链接的公开系统库列表,请参阅 Android NDK 原生 API

    注意:如果为静态库定义此变量,编译系统会忽略此变量,并且 ndk-build 会显示一则警告。

    LOCAL_LDFLAGS

    此变量列出了编译系统在编译共享库或可执行文件时使用的其他链接器标记。例如,要在 ARM/X86 上使用 ld.bfd 链接器:

    LOCAL_LDFLAGS += -fuse-ld=bfd
        

     

    注意:如果为静态库定义此变量,编译系统会忽略此变量,并且 ndk-build 会显示一则警告。

    LOCAL_ALLOW_UNDEFINED_SYMBOLS

    默认情况下,如果编译系统在尝试编译共享库时遇到未定义的引用,将会抛出“未定义的符号”错误。此错误可帮助您捕获源代码中的错误。

    要停用此检查,请将此变量设置为 true。请注意,此设置可能会导致共享库在运行时加载。

    注意:如果为静态库定义此变量,编译系统会忽略此变量,并且 ndk-build 会显示一则警告。

    LOCAL_ARM_MODE

    默认情况下,编译系统会在 thumb 模式下生成 ARM 目标二进制文件,其中每条指令都是 16 位宽,并与 thumb/ 目录中的 STL 库关联。将此变量定义为 arm 会强制编译系统在 32 位 arm 模式下生成模块的对象文件。以下示例演示了如何执行此操作:

    LOCAL_ARM_MODE := arm
        

     

    您也可以对源文件名附加 .arm 后缀,指示编译系统在 arm 模式下仅编译特定的源文件。例如,以下示例指示编译系统在 ARM 模式下始终编译 bar.c,但根据 LOCAL_ARM_MODE 的值编译 foo.c

    LOCAL_SRC_FILES := foo.c bar.c.arm
        

     

    注意:您也可以在 Application.mk 文件中将 APP_OPTIM 设置为 debug,以强制编译系统生成 ARM 二进制文件。指定 debug 会强制执行 ARM 编译,因为工具链调试程序无法正确处理 Thumb 代码。

    LOCAL_ARM_NEON

    此变量仅在以 armeabi-v7a ABI 为目标时才有意义。它允许在 C 和 C++ 源代码中使用 ARM Advanced SIMD (NEON) 编译器内建函数,以及在 Assembly 文件中使用 NEON 指令。

    请注意,并非所有基于 ARMv7 的 CPU 都支持 NEON 扩展指令集。因此,必须执行运行时检测,以便在运行时安全地使用此代码。详情请参阅 NEON 支持和 cpufeatures 库。

    此外,您也可以使用 .neon 后缀,指定编译系统仅以 NEON 支持编译特定源文件。在以下示例中,编译系统以 Thumb 和 NEON 支持编译 foo.c,以 Thumb 支持编译 bar.c,并以 ARM 和 NEON 支持编译 zoo.c

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

     

    如果同时使用这两个后缀,则 .arm 必须在 .neon 前面。

    LOCAL_DISABLE_FORMAT_STRING_CHECKS

    默认情况下,编译系统会在编译代码时保护格式字符串。这样的话,如果 printf 样式的函数中使用了非常量格式的字符串,就会强制引发编译器错误。此保护默认启用,但您也可通过将此变量的值设置为 true 将其停用。如果没有必要的原因,我们不建议停用。

    LOCAL_EXPORT_CFLAGS

    此变量用于记录一组 C/C++ 编译器标记,这些标记将添加到通过 LOCAL_STATIC_LIBRARIES 或 LOCAL_SHARED_LIBRARIES 变量使用此模块的任何其他模块的 LOCAL_CFLAGS 定义中。

    例如,假设有下面这一对模块:foo 和 bar,bar 依赖于 foo

    include $(CLEAR_VARS)
        LOCAL_MODULE := foo
        LOCAL_SRC_FILES := foo/foo.c
        LOCAL_EXPORT_CFLAGS := -DFOO=1
        include $(BUILD_STATIC_LIBRARY)
    
        include $(CLEAR_VARS)
        LOCAL_MODULE := bar
        LOCAL_SRC_FILES := bar.c
        LOCAL_CFLAGS := -DBAR=2
        LOCAL_STATIC_LIBRARIES := foo
        include $(BUILD_SHARED_LIBRARY)
        

     

    在此例中,编译系统在编译 bar.c 时会向编译器传递 -DFOO=1 和 -DBAR=2 标记。它还会在模块的 LOCAL_CFLAGS 前面加上导出的标记,以便您轻松进行替换。

    此外,模块之间的关系也具有传递性:如果 zoo 依赖于 bar,而后者依赖于 foo,那么 zoo 也会继承从 foo 导出的所有标记。

    最后,编译系统在执行局部编译时(即,编译要导出标记的模块时),不使用导出的标记。因此,在上面的示例中,编译 foo/foo.c 时不会将 -DFOO=1 传递到编译器。要执行局部编译,请改用 LOCAL_CFLAGS

    LOCAL_EXPORT_CPPFLAGS

    此变量与 LOCAL_EXPORT_CFLAGS 相同,但仅适用于 C++ 标记。

    LOCAL_EXPORT_C_INCLUDES

    此变量与 LOCAL_EXPORT_CFLAGS 相同,但适用于 C include 路径。例如,当 bar.c 需要包含模块 foo 的头文件时,此变量很有用。

    LOCAL_EXPORT_LDFLAGS

    此变量与 LOCAL_EXPORT_CFLAGS 相同,但适用于链接器标记。

    LOCAL_EXPORT_LDLIBS

    此变量与 LOCAL_EXPORT_CFLAGS 相同,告诉编译系统将特定系统库的名称传递给编译器。请在您指定的每个库名称前附加 -l

    请注意,编译系统会将导入的链接器标记附加到模块的 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。这样就会告知链接器,由于 libbar.so 依赖于 foo,因此它也依赖于系统日志记录库。

    LOCAL_SHORT_COMMANDS

    当模块有很多源文件和/或依赖的静态或共享库时,请将此变量设置为 true。这样会强制编译系统将 @ 语法用于包含中间对象文件或链接库的归档。

    此功能在 Windows 上可能很有用,在 Windows 上,命令行最多只接受 8191 个字符,这对于复杂的项目来说可能太少。它还会影响个别源文件的编译,而且将几乎所有编译器标记都放在列表文件内。

    请注意,true 以外的任何值都将恢复为默认行为。您也可以在 Application.mk 文件中定义 APP_SHORT_COMMANDS,以对项目中的所有模块强制实施此行为。

    不建议默认启用此功能,因为它会减慢编译速度。

    LOCAL_THIN_ARCHIVE

    编译静态库时,请将此变量设置为 true。这样会生成一个瘦归档,即一个库文件,其中不含对象文件,而只包含它通常包含的实际对象的文件路径。

    这对于减小编译输出的大小非常有用。但缺点是,这样的库无法移至其他位置(其中的所有路径都是相对路径)。

    有效值为 truefalse 或空白。您可在 Application.mk 文件中通过 APP_THIN_ARCHIVE 变量设置默认值。

    注意:在非静态库模块或预编译的静态库模块中,将会忽略此变量。

    LOCAL_FILTER_ASM

    请将此变量定义为一个 shell 命令,供编译系统用于过滤根据您为 LOCAL_SRC_FILES 指定的文件提取或生成的汇编文件。定义此变量会导致发生以下情况:

    1. 编译系统从任何 C 或 C++ 源文件生成临时汇编文件,而不是将它们编译到对象文件中。
    2. 编译系统在任何临时汇编文件以及 LOCAL_SRC_FILES 中所列任何汇编文件的 LOCAL_FILTER_ASM 中执行 shell 命令,因此会生成另一个临时汇编文件。
    3. 编译系统将这些过滤的汇编文件编译到对象文件中。

    例如:

    LOCAL_SRC_FILES  := foo.c bar.S
        LOCAL_FILTER_ASM :=
    
        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
        

     

    NDK 提供的函数宏

    本部分介绍了 NDK 提供的 GNU Make 函数宏。使用 $(call <function>) 可以对这些宏进行求值;它们将返回文本信息。

    my-dir

    这个宏返回最后包含的 makefile 的路径,通常是当前 Android.mk 的目录。my-dir 可用于在 Android.mk 文件的开头定义 LOCAL_PATH。例如:

    LOCAL_PATH := $(call my-dir)
        

     

    由于 GNU Make 的工作方式,这个宏实际返回的是编译系统解析编译脚本时包含的最后一个 makefile 的路径。因此,包含其他文件后就不应调用 my-dir

    例如:

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

     

    该示例的问题在于,对 my-dir 的第二次调用将 LOCAL_PATH 定义为 $PATH/foo,而不是 $PATH,因为这是其最近的 include 所指向的位置。

    在 Android.mk 文件中的所有其他内容后添加额外的 include 可避免此问题。例如:

    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 file
        include $(LOCAL_PATH)/foo/Android.mk
        

     

    如果以这种方式构造文件不可行,请将第一个 my-dir 调用的值保存到另一个变量中。例如:

    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 文件列表。

    利用此函数,您可以为编译系统提供深度嵌套的源目录层次结构。默认情况下,NDK 只在 Android.mk 文件所在的目录中查找文件。

    this-makefile

    返回当前 makefile(编译系统从中调用函数)的路径。

    parent-makefile

    返回包含树中父 makefile 的路径(包含当前 makefile 的 makefile 的路径)。

    grand-parent-makefile

    返回包含树中祖父 makefile 的路径(包含当前父 makefile 的 makefile 的路径)。

    import-module

    此函数用于按模块名称查找和包含模块的 Android.mk 文件。典型的示例如下所示:

    $(call import-module,<name>)
        

     

    在此示例中,编译系统在 NDK_MODULE_PATH 环境变量所引用的目录列表中查找具有 <name> 标记的模块,并且自动包含其 Android.mk 文件。

     

     

    https://developer.android.google.cn/ndk/guides/android_mk

    展开全文
  • 解读Android.mk文件

    千次阅读 2018-08-23 14:19:39
    Android.mk文件会将我们的 C 和 C++ 文件描述为 Android NDK 二、概述 Android.mk文件是描述源文件在构建系统的作用,更具体来说: 这个Android.mk是一个微小版的在构建过程中解析一次或多次的Makefile,...
  • Android.mk的用法和基础 && m、mm、mmm编译命令

    万次阅读 多人点赞 2017-12-05 14:20:19
    一个Android.mk file用来向编译系统描述你的源代码。具体来说:该文件是GNU Makefile的一小部分,会被编译系统解析一次或多次。你可以在每一个Android.mk file中定义一个或多个模块,你也可以在几个模块中使用同一个...
  • android.mk

    2020-09-19 07:51:04
    Android.mk的基础知识 一.基础知识 1.Android.mk文件可以将源码打包成模块,模块可以是apk,jar包,c/c++应用程序,静态库和动态库。动态库可以被以到应用程序包apk,静态库可以被连接入动态库。 2.Android.mk中...
  • android android.mk中:= ?= +=之间的区别

    千次阅读 2015-12-24 16:16:51
    在Makefile中我们经常看到 = := ?= +=这几个赋值运算符,那么他们有什么区别呢?我们来做个简单的实验 新建一个Makefile,内容为: ifdef DEFINE_VRE  VRE = “Hello World!” else ...endif
  • Android.mk入门

    千次阅读 2017-01-12 11:13:59
    Androidmk文件是有很强的套路的,下面我在我的<android源码路径>/packsges/app/文件夹下建立一个名字叫做MakefileDemo的工程,里面的目录结构如图所示:![Alt text](./2017-01-11 14:34:49的屏幕截图.png) libs...
  • Android.mk用法详解(一)

    万次阅读 2018-07-24 16:21:59
    Android.mkAndroid提供的一个makefile文件,可以将源文件分组为模块。用来引用的头文件目录、需要编译的*.c/*.cpp文件、jni源文件、指定编译生成*.so共享库文件或者*.a静态库文件,可以定义一个或多个模块,也可以...
  • Android.mk语法

    万次阅读 2019-03-20 11:36:42
    NDK Android NDK 是将C或C++(原生代码)嵌入到 Android 应用中的工具。... Android.mk,必须在jni文件夹内创建的配置文件,其中定义了模块及其名称、编译的源文件、版本标志以及需要链接的库。ndk-build...
  • Android.mk的用法和基础

    2011-07-06 15:24:41
    Android.mk的用法和基础 2011-05-19 23:17:31| 分类: Android | 标签: |字号 订阅
  • 1、Android.mk文件概述 Android.mk文件用来告诉NDK编译系统,应该如何编译这些源码。更确切地说,该文件其实就是一个小型的Makefile。该文件会被NDK的编译工具解析多次,所以要注意不要过多使用环境变量,以免第...
  • Android.mk语法解释

    千次阅读 2015-06-02 14:37:18
    大家在编写Android的Native代码时,经常会接触到一个叫做Android.mk的文件。 虽然编译的时候都用到的是make,但是这个Android.mk文件里的语法还跟一般的make文件语法不太一样。 本质上,Android.mk只是GNU ...
  • Android Studio 中使用 Android.mk 配置动态库库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三方 动态库 ( Android.mk ) V . 链接动态库 ( 设置动态库依赖 ) VI . Java 代码...
  • androidandroid.mk,和application.mk文件编写(总结版)1.简介android.mk是用来描述要编译某个具体的模块,所需要的一些资源,包括要编译的源码、要链接的库等等application.mk:描述你的程序所需要的模块,即静态...
  • 最近项目上,程序编译是使用 android.mk 进行管理和编译的,之前一直使用 makfile,最目前这个机制不太了解; 经过这几天了解,现将自己的理解总结如下,有新理解后续再更新。。。。。。 Android.mk 自我理解 1. ...
  • Android.mk 详解

    千次阅读 2018-04-25 01:10:01
    阅读目录1.编译程序:1.1 可执行程序模板:1.2 编译一个静态库1.3 编译一...Android.mk与普通的makefile略有不同,Android.mk具有统一的写法,主要包含一些系统的公共的宏:Android.mk中选项参考以下文件路径:buil...
  • Android.mk文件 一个Android.mk file用来向编译系统描述你的源代码。具体来说:该文件是GNU Makefile的一小部分,会被编译系统解析一次或多次。你可以在每一个Android.mk file中定义一个或多个模块,你也可以在几...
  • android.mk转换为android.bp

    万次阅读 2018-05-09 20:05:42
    android.mk大家都很熟悉了,就是android下编译模块的配置文件,可以理解为android makefile。从android N之后,我们发现好多模块下面没有了android.mk文件,多了一个android.bp文件。这个是google在android N之后新...
1 2 3 4 5 ... 20
收藏数 50,313
精华内容 20,125
关键字:

android.mk