Android编译系统(Android.mk文件详解)
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE:= helloworld LOCAL_SRC_FILES := helloworld.c include $(BUILD_SHARED_LIBRARY)
MY_SOURCES := foo.c ifneq ($(MY_CONFIG_BAR),) MY_SOURCES += bar.c endif LOCAL_SRC_FILES += $(MY_SOURCES)
注意:‘:=’是赋值的意思;'+='是追加的意思;‘$’表示引用某变量的值。
LOCAL_SRC_FILES := foo.c toto/bar.c\
Hello.c
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文件名
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_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 表示编译一个静态库。
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 表示编译一个共享库。
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
每个模块的目标文件夹分别为:
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后缀。例如:
|
------分隔符,方便下次编辑修改------
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下的可执行程序和主机上的可执行程序我们要使用静态库。例如:
|
------分隔符,方便下次编辑修改------
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_MODULE
LOCAL_MODULE变量必须定义,用来标识在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。如果有其它moudle中已经定义了该名称,那么你在编译时就会报类似这样的错误:
libgl2jni already defined by frameworks/base/opengl/tests/gl2_jni/jni. Stop. |
下面就是该错误的截图:
接下来就是修改你的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,例如:
|
------分隔符,方便下次编辑修改------
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_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中定义你的库所需要使用的空间,如果不定义或者空间不够的话,在编译的时候就会报错。如下图所示:
当在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库预编译成功了:
注意:
在给我们的应用库分配地址空间时,最好以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 |
那么,在编译的时候就会出现下图的错误:
这说明在编译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 |