1. 程式人生 > >Android 編譯系統

Android 編譯系統

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<path>來指定額外的包含路徑,然而,如果使用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 <wbr>編譯系統 <wbr>(一)

所以要確保你所包含的路徑目錄下,有你所需要的標頭檔案,以避免編譯時出現錯誤。

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 <wbr>編譯系統 <wbr>(一)

如果你能預先定義該變數的值,那麼就不會出現上述的錯誤。

LOCAL_MODULE_TAGS

可以用空白的空格來分開一些標籤,可以設定LOCAL_MODULE_TAGS,如果這個標籤列表是空的或者包含有droid,這個module就會當作droid來編譯,否則,它只能用make<your-module>來編譯和安裝你的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 <wbr>編譯系統 <wbr>(一)

上圖的錯誤表示在編譯到SHARED_LIBRARIES這一步(既要生成libsanangeles.so共享庫時),在連結的過程中(既在LINKED這一步時)遇到了錯誤。

所以,在對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 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 <wbr>編譯系統 <wbr>(二)

接下來就是修改你的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 <wbr>編譯系統 <wbr>(二)

當在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 <wbr>編譯系統 <wbr>(二)

注意:

在給我們的應用庫分配地址空間時,最好以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 <wbr>編譯系統 <wbr>(二)

這說明在編譯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編譯器的訊號就會被髮出。這裡可以在你的應用中需要這些模組時,進行編譯的