Android 编译系统 (一)

主要是没有一个完整的Android Build System 中文版,所以写了一个也可以以后作为参考。
1.Makefile & Android build system

在进行讲述Android编译系统之前,应该先了解一下编译时所使用的Makefile,或者说复习下这方面的知识,这样才能更好的了解Android build system的原理。

1.1.Makefile

1.1.1.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编译系统最核心的内容。

1.1.2.编译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)。

1.2.Android build ystem

Makefile文件用来告诉make命令需要怎么样的去编译和链接程序。在编译时,需要根据编译环境和编译目标选择编译工具,编译参数,以及选择编译安装哪些模块。同时Makefile指定了构建目标所需的依赖性以及生成规则。

Android中,主要的Makefile文件存在于build/core/目录下,它的表现形式为多个后缀为*.mk的文件组成,也称为build systemAndroid 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相关的编译器,编译参数等的设置

2.Android.mk

Android.mk是Android编译系统中最重要的一个文件,下面将详细介绍:

2.1.概述

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:

1. 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.

2. 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.

3. 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.

2.2.详细说明

首先,对这些变量的命名做一说明:

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文件中涉及到的变量做详细说明:

2.2.1.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)中引用了一些头文件,而在编译时如果找不到这些头文件,就会报如下图的错误:

Android 编译系统 (一)_第1张图片

所以要确保你所包含的路径目录下,有你所需要的头文件,以避免编译时出现错误。

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< utils/KeyedVector.h>

not #include< KeyedVector.h>

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< utils/KeyedVector.h>

而不是:

#include< KeyedVector.h>

如果你想引用log.h文件,那么你应该这么写:

#include< log.h>

补充2:

如果你在编译JNI时,在你的JNI代码中要引用jni.h时,既当你的代码(helloneon.c)中写道:

#include< jni.h>

如果你没有在该目录下的Android.mk中定义:

# Also need the JNI headers

LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)

那么就会在编译时报如下图所示的错误:

Android 编译系统 (一)_第2张图片

如果你能预先定义该变量的值,那么就不会出现上述的错误。

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 running make< your-module> or with the make 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 := 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 with "include $(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

但是,如果在编译的过程中找不到这个库,或者说这个库并没有存在与你编译环境下,那么在编译的时候就会出现如下图所示的错误:


Android 编译系统 (一)_第3张图片

上图的错误表示在编译到SHARED_LIBRARIES这一步(既要生成libsanangeles.so共享库时),在链接的过程中(既在LINKED这一步时)遇到了错误。

所以,在对LOCAL_LDLIBS变量赋值时,要确保其正确性以及存在性,避免出现上图的编译错误。

你可能感兴趣的:(Android进阶)