Android编译系统(Android.mk文件详解)

时间:2021-12-23 05:04:04
【Android-NDK(Native Development Kit)  docs文档】
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。
 
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’。 
  注:ABI (application binary interface,应用二进制接口, 缩写为 ABI. ABI不同于应用程序接口(API),API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译,然而ABI允许编译好的目标代码在使用兼容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)脚本之前定义它。模块的名字决定了生成文件的名字。例如,如果一个共享库模块的名字是xxx,那么生成文件的名字就是 libxxx.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的顶层目录),宏定义,或者编译选项。
  注意:不要在 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
  可查看 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)

和上面相似,BUILD_SHARED_LIBRARY 表示编译一个共享库。

以上三者的生成结果分别在如下目录中,generic 依具体 target 会变:
out/target/product/generic/obj/APPS
out/target/product/generic/obj/JAVA_LIBRARIES
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY

每个模块的目标文件夹分别为:

1)APK程序:XXX_intermediates
2)JAVA库程序:XXX_intermediates
 3)C\C++可执行程序:XXX_intermediates
 4)C\C++静态库: XXX_static_intermediates
 5)C\C++动态库: XXX_shared_intermediates
 
 

LOCAL_NO_MANIFEST

如果你的Package没有Manifest(AndroidManifest.xml),你可以设置

LOCAL_NO_MANIFEST:=true.

------分隔符,方便下次编辑修改------

If your package doesn't have a manifest (AndroidManifest.xml), then set

LOCAL_NO_MANIFEST:=true.

The common resources package does this.

LOCAL_PACKAGE_NAME

LOCAL_PACKAGE_NAME变量是一个App的名字,例如:Dialer、Contacts等等。它可能在我们使用ant编译系统编译App时会发生改变。

LOCAL_PACKAGE_NAME is the name of an app. For example, Dialer, Contacts, etc. This will probably change or go away when we switch to an ant-based build system for the apps.

LOCAL_PATH

LOCAL_PATH := $(call my-dir):每个Android.mk文件都必须以定义LOCAL_PATH变量开始,其目的是为了定位源文件的位置。例如:

LOCAL_PATH := $(my-dir)

my-dir宏函数使用的是MAKEFILE_LIST变量,你必须在include其它任何makefile之前来调用它。另外,考虑到当你include任何子目录时都要重新设置LOCAL_PATH,你必须在include它们之前设置它。

The directory your Android.mk file is in. You can set it by putting the following as the first line in your Android.mk:

LOCAL_PATH := $(my-dir)

The my-dir macro uses the MAKEFILE_LIST variable, so you must call it before you include any other makefiles. Also, consider that any subdirectories you inlcude might reset LOCAL_PATH, so do your own stuff before you include them. This also means that if you try to write several include lines that reference LOCAL_PATH, it won't work, because those included makefiles might reset LOCAL_PATH.

LOCAL_PREBUILT_EXECUTABLES

LOCAL_PREBUILT_EXECUTABLES预编译including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)时所用,指定需要复制的可执行文件。

When including $(BUILD_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to executables that you want copied. They're located automatically into the right bin directory.

LOCAL_PREBUILT_LIBS

LOCAL_PREBUILT_LIBS变量是在预编译including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)时所用, 指定需要复制的库.

When including $(BUILD_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to libraries that you want copied. They're located automatically into the right lib directory.

LOCAL_SHARED_LIBRARIES

LOCAL_SHARED_LIBRARIES变量用来列出模块所需的共享库的列表,不需要加上.so后缀。例如:

LOCAL_SHARED_LIBRARIES := /

libutils /

libui /

libaudio /

libexpat /

libsgl

------分隔符,方便下次编辑修改------

These are the libraries you directly link against. You don't need to pass transitively included libraries. Specify the name without the suffix:

LOCAL_SHARED_LIBRARIES := \

libutils \

libui \

libaudio \

libexpat \

libsgl

LOCAL_SRC_FILES

LOCAL_SRC_FILES变量必须包含一系列将被构建和组合成模块的C/C++源文件。

注意:不需要列出头文件或include文件,因为生成系统会为你自动计算出源文件的依赖关系。默认的C++源文件的扩展名是.cpp,但你可以通过定义LOCAL_DEFAULT_EXTENSION来指定一个扩展名。

The build system looks at LOCAL_SRC_FILES to know what source files to compile -- .cpp .c .y .l .java. For lex and yacc files, it knows how to correctly do the intermediate .h and .c/.cpp files automatically. If the files are in a subdirectory of the one containing the Android.mk, prefix them with the directory name:

LOCAL_SRC_FILES := \

file1.cpp \

dir/file2.cpp

LOCAL_STATIC_LIBRARIES

LOCAL_STATIC_LIBRARIES变量和LOCAL_SHARED_LIBRARIES类似,用来列出你的模块中所需的静态库的列表,你可以在你的module中包含一些想使用的静态库,通常我们使用共享库,但是有些地方,像在sbin下的可执行程序和主机上的可执行程序我们要使用静态库。例如:

LOCAL_STATIC_LIBRARIES := /

libutils /

libtinyxml

------分隔符,方便下次编辑修改------

These are the static libraries that you want to include in your module. Mostly, we use shared libraries, but there are a couple of places, like executables in sbin and host executables where we use static libraries instead.

LOCAL_STATIC_LIBRARIES := \

libutils \

libtinyxml

LOCAL_MODULE

LOCAL_MODULE变量必须定义,用来标识在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。如果有其它moudle中已经定义了该名称,那么你在编译时就会报类似这样的错误:

libgl2jni already defined by frameworks/base/opengl/tests/gl2_jni/jni. Stop.

下面就是该错误的截图:

Android编译系统(Android.mk文件详解)

接下来就是修改你的module的名字了,或者找到跟你重名的module把它干掉,但不建议你那么做,因为有可能会带来未知的错误(你修改了别人的module的名字,而别人不一定知道,当他再编译或者做其它时,就会出错)。

LOCAL_MODULE is the name of what's supposed to be generated from your Android.mk. For exmample, for libkjs, the LOCAL_MODULE is "libkjs" (the build system adds the appropriate suffix -- .so .dylib .dll).

注意:编译系统会自动产生合适的前缀和后缀,例如:

LOCAL_MODULE := screenshot

一个被命名为“screenshot”的共享库模块,将会生成“libscreenshot.so”文件。

补充1:变量命名的规范性

如果LOCAL_MODULE变量定义的值可能会被其它module调用时,就要考虑为其变量命名的规范性了。特别是在使用JNI时,既在LOCAL_JNI_SHARED_LIBRARIES变量中定义的值,最好要和LOCAL_MODULE变量定义的值保存一致(具体请参考LOCAL_JNI_SHARED_LIBRARIES变量的使用说明)。

这时的LOCAL_MODULE变量的命名最好以lib开头,既“libxxx”,例如:

LOCAL_MODULE := libscreenshot

LOCAL_MODULE_PATH

通知编译系统将module放到其它地方而不是它通常的类型。如果你重写这个变量,确保你还要再设置LOCAL_UNSTRIPPED_PATH变量的值。如果你忘了设置LOCAL_UNSTRIPPED_PATH变量的值的话,就会报错。

Instructs the build system to put the module somewhere other than what's normal for its type. If you override this, make sure you also set LOCAL_UNSTRIPPED_PATH if it's an executable or a shared library so the unstripped binary has somewhere to go. An error will occur if you forget to.

LOCAL_WHOLE_STATIC_LIBRARIES

LOCAL_WHOLE_STATIC_LIBRARIES 指定模块所需要载入的完整静态库(这些静态库在链接是不允许链接器删除其中无用的代码)。通常这在你想往共享库中增加一个静态库时是非常有用的,共享库就会接受到静态库暴露出的content,例如:

LOCAL_WHOLE_STATIC_LIBRARIES := /

libsqlite3_android

------分隔符,方便下次编辑修改------

These are the static libraries that you want to include in your module without allowing the linker to remove dead code from them. This is mostly useful if you want to add a static library to a shared library and have the static library's content exposed from the shared library.

LOCAL_WHOLE_STATIC_LIBRARIES := \

libsqlite3_android

LOCAL_REQUIRED_MODULES

LOCAL_REQUIRED_MODULES 指定模块运行所依赖的模块(模块安装时将会同步安装它所依赖的模块)

Set LOCAL_REQUIRED_MODULES to any number of whitespace-separated module names, like "libblah" or "Email". If this module is installed, all of the modules that it requires will be installed as well. This can be used to, e.g., ensure that necessary shared libraries or providers are installed when a given app is installed.

LOCAL_PRELINK_MODULE

LOCAL_PRELINK_MODULE变量用来规定是否需要预连接处理(默认需要,用来做动态库优化)。LOCAL_PRELINK_MODULE只有在编译.so的时候才会有的选项,主要是通过预链接的方式来加快程序启动和执行的速度,如果在你的代码(/jni/Android.mk)中有下面一条语句:

LOCAL_PRELINK_MODULE := true

那么你要在build/core/prelink-linux-arm.map中定义你的库所需要使用的空间,如果不定义或者空间不够的话,在编译的时候就会报错。如下图所示:

Android编译系统(Android.mk文件详解)

当在build/core/prelink-linux-arm.map中定义了我们这里使用的libhello-jni.so库的空间之后,既在该文件中加入一条语句:

libhello-jni.so                   0x99E00000

注意:在prelink-linux-arm.map文件的开头部分有明确的规定,指定的内存取值范围分配给不同的部分使用,而我们的App的库也给指定了一个范围:

0x90000000 - 0x9FFFFFFF Prelinked App Libraries

重新编译,就不会再报错了,下面的截图中很清晰地看到已经将libhello-jni.so库预编译成功了:

Android编译系统(Android.mk文件详解)

注意:

在给我们的应用库分配地址空间时,最好以1M为边界,地址空间大小按照由大到小的降序进行排序。

下面是对于Prelink的说明:

Prelink利用事先链接代替运行时链接的方法来加速共享库的加载,它不仅可以加快起动速度,还可以减少部分内存开销。程序运行时的动态链接尤其是重定位(relocation)的开销对于大型系统来说是很大的。动态链接和加载的过程开销很大,并且在大多数的系统上,函数库并不会常常被更动,每次程序被执行时所进行的链接动作都是完全相同的,对于嵌入式系统来说尤其如此。因此,这一过程可以改在运行时之前就可以预先处理好,即花一些时间利用Prelink工具对动态共享库和可执行文件进行处理,修改这些二进制文件并加入相应的重定位等信息,节约了本来在程序启动时的比较耗时的查询函数地址等工作,这样可以减少程序启动的时间,同时也减少了内存的耗用。

Prelink的这种做法当然也有代价的,每次更新动态共享库时,相关的可执行文件都需要重新执行一遍Prelink才能保证有效,因为新的共享库中的符号信息、地址等很可能与原来的已经不同了,这就是为什么android framework代码一改动,这时候就会导致相关的应用程序重新被编译。

LOCAL_JNI_SHARED_LIBRARIES

LOCAL_JNI_SHARED_LIBRARIES变量主要是用在JNI的编译中,如果你要在你的Java代码中引用JNI中的共享库*.so,此变量就是共享库的名字。

那么你要注意的一点是:在你的Project根目录下的Android.mk中要定义此变量用来引用你要使用的JNI中的共享库*.so。例如:

$(Project)/Android.mk

LOCAL_JNI_SHARED_LIBRARIES := libsanangeles

而在你的jni目录下的Android.mk中则要定义LOCAL_MODULE变量的值,一定要让这两个变量的值相同。假如你没有这么做,而是像这样:

$(Project)/jni/Android.mk

LOCAL_MODULE := sanangeles

那么,在编译的时候就会出现下图的错误:

Android编译系统(Android.mk文件详解)

这说明在编译libsanangeles.so找不到其规则,因为在上面的代码中定义的是sanangeles。重新修改LOCAL_MODULE变量的值:

$(Project)/jni/Android.mk

LOCAL_MODULE := libsanangeles

即可正常编译。

LOCAL_EXPORT_CFLAGS

定义这个变量用来记录C/C++编译器标志集合,并且会被添加到其他任何以LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES的模块的LOCAL_CFLAGS定义中。例如:这样定义"foo"模块:

foo/Android.mk

include $(CLEAR_VARS)

LOCAL_MODULE :=foo

LOCAL_SRC_FILES :=foo/foo.c

LOCAL_EXPORT_CFLAGS :=-DFOO=1

include $(BUILD_STATIC_LIBRARY)

另一个模块,叫做"bar",并且依赖于上面的模块:

bar/Android.mk

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",“bar”依赖"foo",那么"zoo"也将继承"foo"输出的所有标志。

最后,当编译模块输出标志的时候,这些标志并不会被使用。在上面的例子中,当编译foo/foo.c时,-DFOO=1将不会被传递给编译器。

LOCAL_EXPORT_CPPFLAGS

类似LOCAL_EXPORT_CFLAGS,但适用于C++标志。

具体请参考LOCAL_EXPORT_CFLAGS条目。

LOCAL_EXPORT_C_INCLUDES

类似LOCAL_EXPORT_CFLAGS,但是只有C能包含路径,如果"bar.c"想包含一些由"foo"模块提供的头文件的时候这会很有用。

具体请参考LOCAL_EXPORT_CFLAGS条目。

LOCAL_EXPORT_LDLIBS

类似于LOCAL_EXPORT_CFLAGS,但是只用于链接标志。注意,引入的链接标志将会被追加到模块的LOCAL_LDLIBS,这是由UNIX连接器的工作方式决定的。

当模块foo是一个静态库的时候并且代码依赖于系统库时会很有用的。LOCAL_EXPORT_LDLIBS可以用于输出依赖,例如:

#Frist build the static library libfoo.a

include $(CLEAR_VARS)

LOCAL_MODULE := foo

LOCAL_SRC_FILES := foo/foo.c

LOCAL_EXPORT_LDLIBS := -llog

include $(BUILD_STATIC_LIBRARY)

#Then build the shared library libbar.so

include $(CLEAR_VARS)

LOCAL_MODULE := bar

LOCAL_SRC_FILES := bar.c

LOCAL_STATIC_LIBRARIES := foo

include $(BUILD_SHARED_LIBRARY)

这里,在连接器命令最后,libbar.so将以”-llog”参数进行编译来表明它依赖于系统日志库,因为它依赖于foo。

LOCAL_ALLOW_UNDEFINED_SYMBOLS

默认情况下,当试图编译一个共享库的时候遇到任何未定义的引用都可能导致"未定义符号"(undefined symbol)的错误。这在你的源代码中捕获bug会很有用。

然而,但是由于某些原因,你需要禁用此检查的话,设置变量为"true"即可。需要注意的是,相应的共享库在运行时可能加载失败。

LOCAL_ARM_MODE

LOCAL_ARM_MODE变量主要是应用与嵌入式产品的编译系统中,可以指定为arm模式。例如:

LOCAL_ARM_MODE := arm

注意:你需要执行编译系统为在ARM模式下通过文件的名字增加后缀的方式编译指定的源文件。例如:

LOCAL_SRC_FILES :=foo.c bar.c.arm

这会告诉编译系统一直以ARM模式编译"bar.c",并且通过LOCAL_ARM_MODE的值编译foo.c。

2.2.2.BUILD_XXX变量

2.2.2.1.BUILD_SHARED_LIBRARY

BUILD_SHARED_LIBRARY:指明要编译生成动态共享库。指向一个生成脚本,这个脚本通过LOCAL_XXX变量收集关于组件的信息,并决定如何根据你列出来的源文件生成目标共享库。

注意:在include这个脚本文件之前你必须至少已经定义了LOCAL_MODULE和LOCAL_SRC_FILES。例如:

include $(BUILD_SHARED_LIBRARY)

注意:这会生成一个名为lib$(LOCAL_MODULE).so的动态库。

2.2.2.2.BUILD_STATIC_LIBRARY

BUILD_STATIC_LIBRARY与BUILD_SHARED_LIBRARY类似,但用来生成目标静态库。静态库不会被拷贝至你的project/packages文件夹下,但可用来生成共享库。

例如:

include $(BUILD_STATIC_LIBRARY)

注意:这会生成一个静态库,名叫lib$(LOCAL_MODULE).a的静态库。

2.2.2.3.BUILD_PACKAGE

BUILD_PACKAGE变量用于在最好编译时生成*.apk,例如:

include $(BUILD_STATIC_LIBRARY)

注意:这会生成一个apk安装包,名字就叫$(LOCAL_MODULE).apk的安装包。

2.2.3.其它变量

2.2.3.1.CLEAR_VARS

CLEAR_VARS变量是生成系统提供的,它指向一个特殊的GNU Makefile,它将会为你自动清除许多名为LOCAL_XXX的变量(比如:LOCAL_MODULE、LOCAL_SRC_FILES、LOCAL_STATIC_LIBRARIES等),但LOCAL_PATH是例外,它不会被清除。

注意:这些变量的清除是必须的,因为所有的控制文件是在单一的Makefile,执行环境中解析的,在这里所有的变量都是全局的。

2.2.3.2.TARGET_PLATFORM

TARGET_PLATFORM:当解析该Android.mk文件时用它来指定Andoid目标平台的名称。例如:android-3与Android 1.5相对应。

2.2.4.NDK提供的宏函数

下面是GNU Make的宏函数,必须通过这样的形式调用:

$(call <function>)

2.2.4.1.my-dir

my-dir:返回放置当前Android.mk的文件夹相对于NDK生成系统根目录的路径。可用来在Android.mk的开始处定义LOCAL_PATH的值:

LOCAL_PATH := $(call my-dir)

2.2.4.2.all-subdir-makefiles

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仅在source/*/Android.mk里寻找文件。

2.2.4.3.this-makefile

this-makefile:返回当前Makefile所在目录的路径。

2.2.4.4.parent-makefile

parent-makefile:返回父Makefile所在目录makefile的路径。

2.2.4.5.import-module

一个允许你通过名字找到并包含另一个模块的的Android.mk的功能,例如:

$(call import-module,<name>)

这将会找到通过NDK_MODULE_PATH环境变量引用的模块<name>的目录列表,并且将其自动包含到Android.mk中。

3.Application.mk

3.1.作用

Application.mk目的是描述在你的应用程序中所需要的模块(即静态库或动态库)。

Application.mk文件通常被放置在$PROJECT/jni/Application.mk下,$PROJECT指的是您的项目。另一种方法是将其放在顶层的子目录下,既$NDK/apps目录下,例如:

$NDK/apps/<myapp>/Application.mk

<myapp>是一个简称,用于描述你的NDK编译系统的应用程序(这个名字不会生成共享库或者最终的包),这个方法是Android NDK r4以前的,现在仍然兼容。但是我们强烈建议你使用第一种方法,因为它更简单并且不用修改NDK安装树的目录。

3.2.详细说明

下面是Application.mk中定义的几个变量:

3.2.1.APP_MODULES

APP_MODULES 变量是强制性的,并且会列出所有你所需要的模块。它不允许用一个空格来分隔其模块列表,这个模块名字被定义在Android.mk文件中的LOCAL_MODULE中。

3.2.2.APP_PROJECT_PATH

APP_PROJECT_PATH变量也是强制性的,并且会给出应用程序工程的根目录一个绝对路径。这是用来复制或者安装一个没有任何版本限制的JNI库,从而给 APK 生成工具一个详细的路径。例如:

\HelloNDK\Application.mk

APP_PROJECT_PATH := $(call my-dir)/project

APP_MODULES := HelloNdk

这里定义了工程路径为$(call my-dir)/project,而要编译的模块则是HelloNdk,这样编译系统才会找到我们要编译的库和源文件。

3.2.3.APP_CFLAGS

APP_CFLAGS则是当要编译模块中有任何C文件或者C++文件的时候,C编译器的信号就会被发出。这里可以在你的应用中需要这些模块时,进行编译的调整,这样就不许要直接更改Android.mk为文件本身了。

3.2.4.APP_OPTIM

这个变量是可选的,可以定义成两个值release或者debug,用于修改编译程序模块时的优化层级。release模式是默认的,会产生高优化的文件,debug模式会生成不优化的文件,使得调试更容易进行。

注意:调试release和debug文件都是可能的,但是release版在调试节提高的信息很少,一些变量被优化输出,无法检查,代码被重排序,使得跟踪代码很困难,堆栈追踪也不可靠,等等。

3.2.5.APP_CPPFLAGS

当编译的只有C++源文件的时候,可以通过这个C++编译器来设置。

注意:在Android NDK-1.5_r1中,这个标志可以应用于C和C++源文件中。并且得到了纠正,以建立完整的与系统相匹配的Android编译系统。你先可也可以使用APP_CFLAGS来应用于C或者C++源文件中。建议使用APP_CFLAGS。

4.补充

4.1.两种不同级别的应用apk

目前我所理解是在Android开发中我们会遇到两种不同级别的应用apk:系统级应用apk和普通级应用apk。

下面分别描述两种apk:

4.1.1.编译系统级应用apk

将应用程序的代码放到源代码目录路径下,然后进行编译。将编译生成的*.apk通过adb或者其它方式放到/system/app目录下即可。

4.1.2.编译普通级应用apk

应用程序的代码并没有放到平台的源代码目录下,然后通过编译生成的*.apk通过adb install的方式放到/data/app目录下,就是普通级的apk。

5.参考资料

1.

Android ndk r7b for linux/ Android ndk r6b for windows

2.

Android NDK 概览($(NDK)/doc/OVERVIEW.html)

http://hualang.iteye.com/blog/1135105

3.

NDK使用方法($(NDK)/doc/HOWTO.html)

http://hualang.iteye.com/blog/1136209

4.

Android.mk 文件($(NDK)/doc/ANDROID-MK.html)

http://hualang.iteye.com/blog/1140414

5.

Application.mk 文件($(NDK)/doc/APPLICATION-MK.html)

http://hualang.iteye.com/blog/1149359

6.

Android Building System 总结

http://blog.csdn.net/yili_xie/article/details/5004205

7.

build-system.html

Android 源码下:platform/build/core/build-system.html

转自:http://blog.sina.com.cn/s/blog_6bdbc71601010v67.html