转载声明
原文地址 作者 发布时间 作者主页 Android 编译系统 (一)
Android 编译系统 (二)haicom 2012-03-13 22:02:38 haicom的博客 注:原作内容为2012年所发布,与最新版本的aosp有些区别
主要是没有一个完整的Android Build System
中文版,所以写了一个也可以以后作为参考。
Makefile & Android build system
在进行讲述Android编译系统之前,应该先了解一下编译时所使用的Makefile,或者说复习下这方面的知识,这样才能更好的了解Android build system
的原理。
Makefile
Makefile的规则
首先介绍Makefile的规则:
target ... : prerequisites ...
command
...
target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)。prerequisites就是要生成那个target所需要(依赖)的文件或是目标。
command也就是make需要执行的命令(任意的Shell命令)。这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。就是说,prerequisites
中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。
这就是Makefile
的规则。也就是Makefile
中最核心的内容,Android编译系统符合GNU make
的标准,当然这也是Android
编译系统最核心的内容。
编译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
)。
Android build ystem
Makefile
文件用来告诉make
命令需要怎么样的去编译和链接程序。在编译时,需要根据编译环境和编译目标选择编译工具,编译参数,以及选择编译安装哪些模块。同时Makefile指定了构建目标所需的依赖性以及生成规则。
在Android中,主要的Makefile文件存在于build/core/
目录下,它的表现形式为多个后缀为*.mk
的文件组成,也称为build system
。Android build system
主要有两大部分构成:配置部分和目标构建部分。
Build system
的主流程文件为build/core/main.mk
文件,有它以及所需要的其它*.mk
文件共同完成一次Build
的任务。
下面以表格的形式概要描述几个重要的*.mk
的文件的作用:
文件名 | 作用 |
---|---|
Android.mk |
module/package 的设置文件,每个module/package 的目录下都会有一个Android.mk |
AndroidProducts.mk |
即为Android build system 提供给厂商的接口文件,通过此文件即可定义所需编译和安装的packages,默认为generic |
base_rules.mk |
对一些Makefile的变量规则化 |
BoardConfig.mk |
是为product 主板做设定,例如driver 选择设定,选择CPU 架构等等 |
Binary.mk |
控制如何生成目标文件 |
buildspec.mk |
位于根目录下,可在此选择要产生的product 、平台、额外的module/package 等。build/buildspec.mk.default 是样板 |
Clear_vars.mk |
清除编译系统中用到的临时变量 |
Copy_headers.mk |
将头文件拷贝到指定目录 |
config.mk |
定义了编译目标程序所需的工具链及编译参数 |
definations.mk |
定义了很多编译系统中用到的宏,相当于函数库 |
envsetup.mk |
初始化编译环境,定义一些实用的shell 函数,方便编译 |
main.mk |
实际的主控Makefile ,例如找到TOP 目录下所有Android.mk 文件 |
Makefile |
辅助main.mk 主要控制生成 system.img ,ramdisk.img ,userdata.img 等 |
build/envsetup.sh |
提供了几个有用的命令,如:执行. build/envsetup.sh |
Combo/linux-arm.mk |
控制如何生成linux-arm 二进制文件,包括ARM 相关的编译器,编译参数等的设置 |
Android.mk
Android.mk是Android编译系统中最重要的一个文件,下面将详细介绍:
概述
Android.mk在编译中起着至关重要的作用,这其实就是Android编译环境中的makefile。Android.mk文件是为了向生成系统描述你的源代码。更明确的说:这个文件实际上是GNU Make文件的一小片段,它会被生成系统解析一次或多次。因此,你应该在Android.mk里尽量少地声明变量。
下面是在NDK中Android.mk网页中引用的一段:
An Android.mk file is written to describe your sources to thebuild system. More specifically:
The file is really a tiny GNU Makefile fragment that will be parsed one or more times by the build system. As such, you should try to minimize the variables you declare there and do not assume that anything is not defined during parsing.
The file syntax is designed to allow you to group your sources into 'modules'. A module is one of the following:
a static library
a shared library
Only shared libraries will be installed/copied to your application package. Static libraries can be used to generate shared libraries though.You can define one or more modules in each Android.mk file, and you can use the same source file in several modules.
- The build system handles many details for you. For example, you don't need to list header files or explicit dependencies between generated files in your Android.mk. The NDK build system will compute these automatically for you. This also means that, when updating to newer releases of the NDK, you should be able to benefit from new toolchain/platform support without having to touch your Android.mk files.
详细说明
首先,对这些变量的命名做一说明:
LOCAL_XXX
变量:在每个module中都要设置以LOCAL_
开头的变量。它们会被include $(CLEAR_VARS)
命令来清除,你会在你的很多module中使用这些LOCAL_开头的变量。
PRIVATE_XXX
变量:这些变量是make-target-specific
(编译具体目标)的变量。
INTERNAL_XXX
变量:这些变量是编译系统所使用的变量,所以你最好不要将你的变量用此来命名,而且最好也不要在你的makefile中见到这些变量。
HOST_XXX
变量和TARGET_XXX
变量:这些变量包含或者定义了一些特定的host
或者target
编译。所以不要在你的makefile中设置以开头HOST_
和TARGET_
的变量。
BUILD_XXX
变量和CLEAR_VARS
:这些变量都包含在那些清晰的makefile模板中了,比如CLEAR_VARS
和BUILD_HOST_PACKAGE
。
当然,你还可以在你的Android.mk文件中使用其它你所需要的命名变量。但是,要记住的是Android是一个非递归的编译系统,所以很有可能,你的变量可能会被其它的Android.mk改变,当你的module执行命令是,这些变量可能已经不同了。
说明:为保证可参考性,也将英语原文记录如下。
But first, a note on variable naming:
LOCAL_ These variables are set per-module. They are cleared by the include $(CLEAR_VARS) line, so you can rely on them being empty after including that file. Most of the variables you'll use in most modules are LOCAL_ variables.
PRIVATE_ These variables are make-target-specific variables. That means they're only usable within the commands for that module. It also means that they're unlikely to change behind your back from modules that are included after yours. This link to the make documentation describes more about target-specific variables. Please note that there are a couple of these laying around the tree that aren't prefixed with PRIVATE_. It is safe, and they will be fixed as they are discovered. Sorry for the confusion.
INTERNAL_ These variables are critical to functioning of the build system, so you shouldn't create variables named like this, and you probably shouldn't be messing with these variables in your makefiles.
HOST_ and TARGET_ These contain the directories and definitions that are specific to either the host or the target builds. Do not set variables that start with HOST_ or TARGET_ in your makefiles.
BUILD_ and CLEAR_VARS These contain the names of well-defined template makefiles to include. Some examples are CLEAR_VARS and BUILD_HOST_PACKAGE.
Any other name is fair-game for you to use in your Android.mk. However, remember that this is a non-recursive build system, so it is possible that your variable will be changed by another Android.mk included later, and be different when the commands for your rule / module are executed
下面对Android.mk文件中涉及到的变量做详细说明:
LOCAL_XXX
变量
LOCAL_XXX
变量用于向编译系统描述你的模块中所使用的变量,你应该在include $(CLEAR_VARS)
和include $(BUILD_XXXXX)
语句之间来定义你想要使用的变量。
下面来详细说明以LOCAL_
开头的变量:
-
LOCAL_ASSET_FILES
LOCAL_ASSET_FILES在Android.mk文件中编译应用程序(BUILD_PACKAGE)时设置此变量,表示引用资源文件,通常会定义成:LOCAL_ASSET_FILES += $(call find-subdir-assets)
说明:为保证可参考性,也将英语原文记录如下(下同,不再说明)。
In Android.mk files that include $(BUILD_PACKAGE) set this to the set of files you want built into your app. Usually:
LOCAL_ASSET_FILES += $(call find-subdir-assets)
This will probably change when we switch to ant for the apps' build system.
-
LOCAL_CC
如果你想在你的module中使用不同的C编译器,可以设置这个变量。如果LOCAL_CC是空的,它就使用默认的编译器。If you want to use a different C compiler for this module, set LOCAL_CC to the path to the compiler. If LOCAL_CC is blank, the appropriate default compiler is used.
-
LOCAL_CXX
如果你想在你的module中使用不同的C++编译器,可以设置这个变量。如果LOCAL_CXX是空的,它就使用默认的编译器。If you want to use a different C++ compiler for this module, set LOCAL_CXX to the path to the compiler. If LOCAL_CXX is blank, the appropriate default compiler is used.
-
LOCAL_CFLAGS
LOCAL_CFLAGS变量为C/C++编译器定义额外的标志,当编译C/C++源文件时传递一个可选的编译器标志,这对于指定额外的宏定义或编译选项很有用。例如:LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1
If you have additional flags to pass into the C or C++ compiler, add them here. For example:
LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1
重要提示:
尽量不要改变Android.mk
中的优化/调试级别,这个可以通过在Application.mk
中设置相应的信息来自动为你处理,并且会会让NDK生成在调试过程中使用的有用的数据文件。注意:在Android-ndk-1.5_r1
中,只使用于C源文件,而不适用于C++源文件。在匹配所有Android build system
的行为已经得到了纠正。(现在你可以为C++源文件使用LOCAL_CPPFLAGS
来指定标志)它可以用LOCAL_CFLAGS += -I
来指定额外的包含路径,然而,如果使用LOCAL_C_INCLUDES
会更好,因为用ndk-gdk
进行本地调试的时候,那些路径依然是需要使用的。 -
LOCAL_CPPFLAGS
LOCAL_CPPFLAGS
变量和LOCAL_CFLAGS
变量类似,如果你只想要增加一些标记(flag)在你的C++编译器中,使用LOCAL_CPPFLAGS
变量来增加它们,例如:LOCAL_CPPFLAGS += -ffriend-injection
LOCAL_CPPFLAGS必须在LOCAL_CFLAGS变量命令行的后面使用,所以你可以用它来重写你在LOCAL_CFLAGS定义的标记。
If you have additional flags to pass into only the C++ compiler, add them here. For example:
LOCAL_CPPFLAGS += -ffriend-injection
LOCAL_CPPFLAGS is guaranteed to be after LOCAL_CFLAGS on the compile line, so you can use it to override flags listed in LOCAL_CFLAGS.
注意:
在Android NDK-1.5_r1版本中,相应的标志可以应用于C或C++源文件上。在配合完整的Android build system的时候,这已经得到了纠正(你可以使用LOCAL_CFLAGS去指定C或C++源文件)。 -
LOCAL_CPP_EXTENSION
如果你的C++文件不是以cpp为文件后缀,通过LOCAL_CPP_EXTENSION指定C++文件后缀名例如:LOCAL_CPP_EXTENSION := .cc
需要注意的是在module中给出的所有的C++文件必须具有相同的扩展名,它是不允许混合使用不同扩展名的。
If your C++ files end in something other than ".cpp", you can specify the custom extension here. For example:
LOCAL_CPP_EXTENSION := .cc
Note that all C++ files for a given module must have the same extension; it is not currently possible to mix different extensions.
注意:统一模块中C++文件后缀必须保持一致。
-
LOCAL_C_INCLUDES
LOCAL_C_INCLUDES变量可以指定额外的目录来指引C/C++编译器来寻找头文件。这些路径必须是最顶端的目录路径,使用LOCAL_PATH来包含你的子目录路径下的文件,例如:LOCAL_C_INCLUDES += extlibs/zlib-1.2.3 LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
注意:
如果你在代码(main.c)中引用了一些头文件,而在编译时如果找不到这些头文件,就会报如下图的错误:
所以要确保你所包含的路径目录下,有你所需要的头文件,以避免编译时出现错误。Additional directories to instruct the C/C++ compilers to look for header files in. These paths are rooted at the top of the tree. Use LOCAL_PATH if you have subdirectories of your own that you want in the include paths. For example:
LOCAL_C_INCLUDES += extlibs/zlib-1.2.3 LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
You should not add subdirectories of include to LOCAL_C_INCLUDES, instead you should reference those files in the #include statement with their subdirectories. For example:
#include
not #include There are some components that are doing this wrong, and should be cleaned up.
补充1:
如果在你的头文件目录include下有以下*.h文件:/include/ utils/ KeyedVector.h log.h
你不应该在LOCAL_C_INCLUDES变量中包含子目录,相反,你应该在使用#include来声明这些文件的引用,以及它们的子目录路径。例如:
#include
而不是:
#include
如果你想引用log.h文件,那么你应该这么写:
#include
补充2:
如果你在编译JNI时,在你的JNI代码中要引用jni.h时,既当你的代码(helloneon.c)中写道:#include
如果你没有在该目录下的Android.mk中定义:
# Also need the JNI headers LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
那么就会在编译时报如下图所示的错误:
如果你能预先定义该变量的值,那么就不会出现上述的错误。
-
LOCAL_MODULE_TAGS
可以用空白的空格来分开一些标签,可以设置LOCAL_MODULE_TAGS
,如果这个标签列表是空的或者包含有droid,这个module就会当作droid来编译,否则,它只能用make
来编译和安装你的module,或者使用make all
Set
LOCAL_MODULE_TAGS
to any number of whitespace-separated tags. If the tag list is empty or contains droid, the module will get installed as part of a make droid. Otherwise, it will only get installed by runningmake
or with themake all
pseudotarget.补充:
LOCAL_MODULE_TAGS
:模块标记,一般的取值范围为debug
、eng
、test
、optional
,如果不定义则默认为optional
。对这几个模式的解释为:user
:指该模块只在user版本下才编译;eng
:指该模块只在eng
版本下才编译;tests
:指该模块只在tests
版本下才编译;optional
:指该模块在所有版本下都编译。 -
LOCAL_REQUIRED_MODULES
可以用空格来分开不同的module的名字,以用来设置LOCAL_REQUIRED_MODULES
,像libblah
或者Email
。如果安装了这个module,那么同时也会安装这个module所必需的模块。确保所需要的共享库和必须已经安装好,当所给的程序安装时。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_FORCE_STATIC_EXECUTABLE
如果编译的可执行程序要进行静态链接(执行时不依赖于任何动态库),则设置:LOCAL_FORCE_STATIC_EXECUTABLE:=true
目前只有libc有静态库形式,这个只有文件系统中/sbin目录下的应用程序会用到,这个目录下的应用程序在运行时通常文件系统的其它部分还没有加载,所以必须进行静态链接。
If your executable should be linked statically, set
LOCAL_FORCE_STATIC_EXECUTABLE:=true.
There is a very short list of libraries that we have in static form (currently only libc). This is really only used for executables in /sbin on the root filesystem.
-
LOCAL_JAVA_LIBRARIES
LOCAL_JAVA_LIBRARIES
编译java应用程序和库的时候指定包含的java类库,目前有core和framework两种情况下定义成:
注意:LOCAL_JAVA_LIBRARIES
不是必须的,而且编译APK时不允许定义(系统会自动添加)When linking Java apps and libraries,
LOCAL_JAVA_LIBRARIES
specifies which sets of java classes to include. Currently there are two of these: core and framework. In most cases, it will look like this:LOCAL_JAVA_LIBRARIES := core framework
Note that setting
LOCAL_JAVA_LIBRARIES
is not necessary (and is not allowed) when building an APK withinclude $(BUILD_PACKAGE)
. The appropriate libraries will be included automatically. -
LOCAL_LDFLAGS
你可以通过设置LOCAL_LDFLAGS
来增加额外的标记传递给连接器。不过要记住,这些参数对ld来说是非常重要的,所以你最好在所有的平台都测试下。You can pass additional flags to the linker by setting
LOCAL_LDFLAGS
. Keep in mind that the order of parameters is very important to ld, so test whatever you do on all platforms. -
LOCAL_LDLIBS
LOCAL_LDLIBS
允许你在你编译你的可执行程序或者库的时候,添加一些指定的额外的库。用lxxx的格式来指定你要引用的库,它们会被连接命令行直接解析。然而,要知道它不会为这些库生成附属。这在使用模拟器编译而又想使用库预编译在主机上时是非常有用的。例如:LOCAL_LDLIBS += -lcurses -lpthread LOCAL_LDLIBS += -Wl,-z,origin
LOCAL_LDLIBS allows you to specify additional libraries that are not part of the build for your executable or library. Specify the libraries you want in -lxxx format; they're passed directly to the link line. However, keep in mind that there will be no dependency generated for these libraries. It's most useful in simulator builds where you want to use a library preinstalled on the host. The linker (ld) is a particularly fussy beast, so it's sometimes necessary to pass other flags here if you're doing something sneaky. Some examples:
LOCAL_LDLIBS += -lcurses -lpthread LOCAL_LDLIBS += -Wl,-z,origin
补充1:
LOCAL_LDLIBS
:生成你的模块时用到的额外的连接器标记(linkerflags
)的名单,在传递有“-l”前缀的特殊系统库的名称时很有用。例如:LOCAL_LDLIBS := -labc
上面的语句会告诉连接器在load time时生成连接到/system/lib/目录下名字叫做libabc.so的动态库。
补充2:
如果在你的Android.mk代码中定义了LOCAL_LDLIBS变量,例如:LOCAL_LDLIBS := -lGLESv1_CM -ldl -llog
但是,如果在编译的过程中找不到这个库,或者说这个库并没有存在与你编译环境下,那么在编译的时候就会出现如下图所示的错误:
所以,在对
LOCAL_LDLIBS
变量赋值时,要确保其正确性以及存在性,避免出现上图的编译错误。 -
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 resetLOCAL_PATH
, so do your own stuff before you include them. This also means that if you try to write several include lines that referenceLOCAL_PATH
, it won't work, because those included makefiles might resetLOCAL_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.
下面就是该错误的截图:
接下来就是修改你的module的名字了,或者找到跟你重名的module把它干掉,但不建议你那么做,因为有可能会带来未知的错误(你修改了别人的module的名字,而别人不一定知道,当他再编译或者做其它时,就会出错)。
LOCAL_MODULE
is the name of what's supposed to be generated from yourAndroid.mk
. For exmample, for libkjs, theLOCAL_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
中定义你的库所需要使用的空间,如果不定义或者空间不够的话,在编译的时候就会报错。如下图所示:
当在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_EXPORT_CFLAGS
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_CPPFLAGS
定义这个变量用来记录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_C_INCLUDES
类似LOCAL_EXPORT_CFLAGS
,但适用于C++
标志。
具体请参考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。
BUILD_XXX
变量
-
BUILD_SHARED_LIBRARY
BUILD_SHARED_LIBRARY
:指明要编译生成动态共享库。指向一个生成脚本,这个脚本通过LOCAL_XXX
变量收集关于组件的信息,并决定如何根据你列出来的源文件生成目标共享库。
注意:在include
这个脚本文件之前你必须至少已经定义了LOCAL_MODULE
和LOCAL_SRC_FILES
。例如:include $(BUILD_SHARED_LIBRARY)
注意:这会生成一个名为
lib$(LOCAL_MODULE).so
的动态库。 -
BUILD_STATIC_LIBRARY
BUILD_STATIC_LIBRARY
与BUILD_SHARED_LIBRARY
类似,但用来生成目标静态库。静态库不会被拷贝至你的project/packages
文件夹下,但可用来生成共享库。
例如:include $(BUILD_STATIC_LIBRARY)
注意:这会生成一个静态库,名叫
lib$(LOCAL_MODULE).a
的静态库。 -
BUILD_PACKAGE
BUILD_PACKAGE
变量用于在最好编译时生成*.apk
,例如:include $(BUILD_STATIC_LIBRARY)
注意:这会生成一个
apk
安装包,名字就叫$(LOCAL_MODULE).apk
的安装包。
其它变量
CLEAR_VARS
CLEAR_VARS
变量是生成系统提供的,它指向一个特殊的GNU Makefile
,它将会为你自动清除许多名为LOCAL_XXX
的变量(比如:LOCAL_MODULE
、LOCAL_SRC_FILES
、LOCAL_STATIC_LIBRARIES
等),但LOCAL_PATH
是例外,它不会被清除。
注意:这些变量的清除是必须的,因为所有的控制文件是在单一的Makefile
,执行环境中解析的,在这里所有的变量都是全局的。TARGET_PLATFORM
TARGET_PLATFORM
:当解析该Android.mk
文件时用它来指定Andoid
目标平台的名称。例如:android-3
与Android 1.5
相对应。
NDK提供的宏函数
下面是GNU Make的宏函数,必须通过这样的形式调用:
$(call )
-
my-dir
my-dir
:返回放置当前Android.mk
的文件夹相对于NDK
生成系统根目录的路径。可用来在Android.mk
的开始处定义LOCAL_PATH
的值:LOCAL_PATH := $(call my-dir)
-
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
里寻找文件。 this-makefile
this-makefile
:返回当前Makefile
所在目录的路径。parent-makefile
parent-makefile
:返回父Makefile
所在目录makefile
的路径。-
import-module
一个允许你通过名字找到并包含另一个模块的的Android.mk
的功能,例如:$(call import-module,
) 这将会找到通过
NDK_MODULE_PATH
环境变量引用的模块<name
>的目录列表,并且将其自动包含到Android.mk
中。
Application.mk
作用
Application.mk
目的是描述在你的应用程序中所需要的模块(即静态库或动态库)。
Application.mk
文件通常被放置在$PROJECT/jni/Application.mk
下,$PROJECT
指的是您的项目。另一种方法是将其放在顶层的子目录下,既$NDK/apps
目录下,例如:
$NDK/apps//Application.mk
<myapp
>是一个简称,用于描述你的NDK
编译系统的应用程序(这个名字不会生成共享库或者最终的包),这个方法是Android NDK r4
以前的,现在仍然兼容。但是我们强烈建议你使用第一种方法,因为它更简单并且不用修改NDK
安装树的目录。
详细说明
下面是Application.mk中定义的几个变量:
APP_MODULES
APP_MODULES
变量是强制性的,并且会列出所有你所需要的模块。它不允许用一个空格来分隔其模块列表,这个模块名字被定义在Android.mk
文件中的LOCAL_MODULE
中。-
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
,这样编译系统才会找到我们要编译的库和源文件。 APP_CFLAGS
APP_CFLAGS
则是当要编译模块中有任何C
文件或者C++
文件的时候,C
编译器的信号就会被发出。这里可以在你的应用中需要这些模块时,进行编译的调整,这样就不许要直接更改Android.mk
为文件本身了。APP_OPTIM
这个变量是可选的,可以定义成两个值release
或者debug
,用于修改编译程序模块时的优化层级。release
模式是默认的,会产生高优化的文件,debug
模式会生成不优化的文件,使得调试更容易进行。
注意:调试release
和debug
文件都是可能的,但是release
版在调试节提高的信息很少,一些变量被优化输出,无法检查,代码被重排序,使得跟踪代码很困难,堆栈追踪也不可靠,等等。APP_CPPFLAGS
当编译的只有C++
源文件的时候,可以通过这个C++
编译器来设置。
注意:在Android NDK-1.5_r1
中,这个标志可以应用于C
和C++
源文件中。并且得到了纠正,以建立完整的与系统相匹配的Android编译系统。你先可也可以使用APP_CFLAGS
来应用于C
或者C++
源文件中。建议使用APP_CFLAGS
。
补充
两种不同级别的应用apk
目前我所理解是在Android
开发中我们会遇到两种不同级别的应用apk
:系统级应用apk
和普通级应用apk
。
下面分别描述两种apk
:
编译系统级应用apk
将应用程序的代码放到武当源代码目录路径下,然后进行编译。将编译生成的*.apk
通过adb
或者其它方式放到/system/app
目录下即可。编译普通级应用apk
应用程序的代码并没有放到平台的源代码目录下,然后通过编译生成的*.apk
通过adb install
的方式放到/data/app
目录下,就是普通级的apk
。
参考资料
Android ndk r7b for linux/ Android ndk r6b for windows
- Android NDK 概览:
$(NDK)/doc/OVERVIEW.html
- NDK使用方法:
$(NDK)/doc/HOWTO.html
- Android.mk 文件:
$(NDK)/doc/ANDROID-MK.html
- Application.mk 文件:
$(NDK)/doc/APPLICATION-MK.html
- Android Building System 总结
-
build-system.html
: Android 源码下platform/build/core/build-system.html