轉(zhuǎn)載聲明
原文地址 作者 發(fā)布時間 作者主頁 Android 編譯系統(tǒng) (一)
Android 編譯系統(tǒng) (二)haicom 2012-03-13 22:02:38 haicom的博客 注:原作內(nèi)容為2012年所發(fā)布,與最新版本的aosp有些區(qū)別
主要是沒有一個完整的Android Build System
中文版帖族,所以寫了一個也可以以后作為參考态兴。
Makefile & Android build system
在進行講述Android編譯系統(tǒng)之前辫樱,應(yīng)該先了解一下編譯時所使用的Makefile簇宽,或者說復(fù)習下這方面的知識弧满,這樣才能更好的了解Android build system
的原理静暂。
Makefile
Makefile的規(guī)則
首先介紹Makefile的規(guī)則:
target ... : prerequisites ...
command
...
target也就是一個目標文件,可以是Object File谱秽,也可以是執(zhí)行文件洽蛀。還可以是一個標簽(Label)摹迷。prerequisites就是要生成那個target所需要(依賴)的文件或是目標。
command也就是make需要執(zhí)行的命令(任意的Shell命令)郊供。這是一個文件的依賴關(guān)系峡碉,也就是說,target這一個或多個的目標文件依賴于prerequisites中的文件驮审,其生成規(guī)則定義在command中鲫寄。就是說,prerequisites
中如果有一個以上的文件比target文件要新的話疯淫,command所定義的命令就會被執(zhí)行地来。
這就是Makefile
的規(guī)則。也就是Makefile
中最核心的內(nèi)容熙掺,Android編譯系統(tǒng)符合GNU make
的標準未斑,當然這也是Android
編譯系統(tǒng)最核心的內(nèi)容。
編譯helloworld
下面用一個最簡單的例子來說明這個規(guī)則币绩,當然就是我們最喜歡寫的helloworld蜡秽,創(chuàng)建一個簡單的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
執(zhí)行make就可以編譯helloworld.c
了缆镣,執(zhí)行make clean
就可以清除編譯結(jié)果了(其實就是刪除helloworld
helloworl.o
)芽突。
Android build ystem
Makefile
文件用來告訴make
命令需要怎么樣的去編譯和鏈接程序。在編譯時董瞻,需要根據(jù)編譯環(huán)境和編譯目標選擇編譯工具寞蚌,編譯參數(shù),以及選擇編譯安裝哪些模塊钠糊。同時Makefile指定了構(gòu)建目標所需的依賴性以及生成規(guī)則睬澡。
在Android中,主要的Makefile文件存在于build/core/
目錄下眠蚂,它的表現(xiàn)形式為多個后綴為*.mk
的文件組成煞聪,也稱為build system
。Android build system
主要有兩大部分構(gòu)成:配置部分和目標構(gòu)建部分逝慧。
Build system
的主流程文件為build/core/main.mk
文件昔脯,有它以及所需要的其它*.mk
文件共同完成一次Build
的任務(wù)。
下面以表格的形式概要描述幾個重要的*.mk
的文件的作用:
文件名 | 作用 |
---|---|
Android.mk |
module/package 的設(shè)置文件笛臣,每個module/package 的目錄下都會有一個Android.mk
|
AndroidProducts.mk |
即為Android build system 提供給廠商的接口文件云稚,通過此文件即可定義所需編譯和安裝的packages,默認為generic
|
base_rules.mk |
對一些Makefile的變量規(guī)則化 |
BoardConfig.mk |
是為product 主板做設(shè)定沈堡,例如driver 選擇設(shè)定静陈,選擇CPU 架構(gòu)等等 |
Binary.mk |
控制如何生成目標文件 |
buildspec.mk |
位于根目錄下,可在此選擇要產(chǎn)生的product 、平臺鲸拥、額外的module/package 等拐格。build/buildspec.mk.default 是樣板 |
Clear_vars.mk |
清除編譯系統(tǒng)中用到的臨時變量 |
Copy_headers.mk |
將頭文件拷貝到指定目錄 |
config.mk |
定義了編譯目標程序所需的工具鏈及編譯參數(shù) |
definations.mk |
定義了很多編譯系統(tǒng)中用到的宏,相當于函數(shù)庫 |
envsetup.mk |
初始化編譯環(huán)境刑赶,定義一些實用的shell 函數(shù)捏浊,方便編譯 |
main.mk |
實際的主控Makefile ,例如找到TOP 目錄下所有Android.mk 文件 |
Makefile |
輔助main.mk 主要控制生成 system.img ,ramdisk.img ,userdata.img 等 |
build/envsetup.sh |
提供了幾個有用的命令撞叨,如:執(zhí)行. build/envsetup.sh
|
Combo/linux-arm.mk |
控制如何生成linux-arm 二進制文件金踪,包括ARM 相關(guān)的編譯器,編譯參數(shù)等的設(shè)置 |
Android.mk
Android.mk是Android編譯系統(tǒng)中最重要的一個文件牵敷,下面將詳細介紹:
概述
Android.mk在編譯中起著至關(guān)重要的作用胡岔,這其實就是Android編譯環(huán)境中的makefile。Android.mk文件是為了向生成系統(tǒng)描述你的源代碼枷餐。更明確的說:這個文件實際上是GNU Make文件的一小片段靶瘸,它會被生成系統(tǒng)解析一次或多次。因此尖淘,你應(yīng)該在Android.mk里盡量少地聲明變量。
下面是在NDK中Android.mk網(wǎng)頁中引用的一段:
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中都要設(shè)置以LOCAL_
開頭的變量村生。它們會被include $(CLEAR_VARS)
命令來清除,你會在你的很多module中使用這些LOCAL_開頭的變量饼丘。
PRIVATE_XXX
變量:這些變量是make-target-specific
(編譯具體目標)的變量趁桃。
INTERNAL_XXX
變量:這些變量是編譯系統(tǒng)所使用的變量,所以你最好不要將你的變量用此來命名肄鸽,而且最好也不要在你的makefile中見到這些變量卫病。
HOST_XXX
變量和TARGET_XXX
變量:這些變量包含或者定義了一些特定的host
或者target
編譯。所以不要在你的makefile中設(shè)置以開頭HOST_
和TARGET_
的變量典徘。
BUILD_XXX
變量和CLEAR_VARS
:這些變量都包含在那些清晰的makefile模板中了蟀苛,比如CLEAR_VARS
和BUILD_HOST_PACKAGE
。
當然逮诲,你還可以在你的Android.mk文件中使用其它你所需要的命名變量帜平。但是,要記住的是Android是一個非遞歸的編譯系統(tǒng)梅鹦,所以很有可能裆甩,你的變量可能會被其它的Android.mk改變,當你的module執(zhí)行命令是齐唆,這些變量可能已經(jīng)不同了嗤栓。
說明:為保證可參考性,也將英語原文記錄如下箍邮。
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
變量用于向編譯系統(tǒng)描述你的模塊中所使用的變量,你應(yīng)該在include $(CLEAR_VARS)
和include $(BUILD_XXXXX)
語句之間來定義你想要使用的變量雹仿。
下面來詳細說明以LOCAL_
開頭的變量:
-
LOCAL_ASSET_FILES
LOCAL_ASSET_FILES在Android.mk文件中編譯應(yīng)用程序(BUILD_PACKAGE)時設(shè)置此變量私恬,表示引用資源文件,通常會定義成: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編譯器马昙,可以設(shè)置這個變量。如果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++編譯器,可以設(shè)置這個變量土匀。如果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
中的優(yōu)化/調(diào)試級別妒御,這個可以通過在Application.mk
中設(shè)置相應(yīng)的信息來自動為你處理解愤,并且會會讓NDK生成在調(diào)試過程中使用的有用的數(shù)據(jù)文件。注意:在Android-ndk-1.5_r1
中乎莉,只使用于C源文件送讲,而不適用于C++源文件。在匹配所有Android build system
的行為已經(jīng)得到了糾正惋啃。(現(xiàn)在你可以為C++源文件使用LOCAL_CPPFLAGS
來指定標志)它可以用LOCAL_CFLAGS += -I<path>
來指定額外的包含路徑哼鬓,然而,如果使用LOCAL_C_INCLUDES
會更好边灭,因為用ndk-gdk
進行本地調(diào)試的時候异希,那些路徑依然是需要使用的。 -
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版本中,相應(yīng)的標志可以應(yīng)用于C或C++源文件上善茎。在配合完整的Android build system的時候券册,這已經(jīng)得到了糾正(你可以使用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.
注意:統(tǒng)一模塊中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)中引用了一些頭文件匾七,而在編譯時如果找不到這些頭文件划址,就會報如下圖的錯誤:
所以要確保你所包含的路徑目錄下,有你所需要的頭文件署穗,以避免編譯時出現(xiàn)錯誤。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
你不應(yīng)該在LOCAL_C_INCLUDES變量中包含子目錄糕档,相反,你應(yīng)該在使用#include來聲明這些文件的引用茴晋,以及它們的子目錄路徑舷丹。例如:
#include <utils/KeyedVector.h>
而不是:
#include <KeyedVector.h>
如果你想引用log.h文件抒钱,那么你應(yīng)該這么寫:
#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)
那么就會在編譯時報如下圖所示的錯誤:
如果你能預(yù)先定義該變量的值谋币,那么就不會出現(xiàn)上述的錯誤。
-
LOCAL_MODULE_TAGS
可以用空白的空格來分開一些標簽症概,可以設(shè)置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 runningmake <your-module>
or with themake all
pseudotarget.補充:
LOCAL_MODULE_TAGS
:模塊標記,一般的取值范圍為debug
秤涩、eng
帜乞、test
、optional
筐眷,如果不定義則默認為optional
黎烈。對這幾個模式的解釋為:user
:指該模塊只在user版本下才編譯;eng
:指該模塊只在eng
版本下才編譯匀谣;tests
:指該模塊只在tests
版本下才編譯照棋;optional
:指該模塊在所有版本下都編譯。 -
LOCAL_REQUIRED_MODULES
可以用空格來分開不同的module的名字武翎,以用來設(shè)置LOCAL_REQUIRED_MODULES
烈炭,像libblah
或者Email
。如果安裝了這個module宝恶,那么同時也會安裝這個module所必需的模塊符隙。確保所需要的共享庫和必須已經(jīng)安裝好,當所給的程序安裝時垫毙。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
如果編譯的可執(zhí)行程序要進行靜態(tài)鏈接(執(zhí)行時不依賴于任何動態(tài)庫)霹疫,則設(shè)置:LOCAL_FORCE_STATIC_EXECUTABLE:=true
目前只有l(wèi)ibc有靜態(tài)庫形式,這個只有文件系統(tǒng)中/sbin目錄下的應(yīng)用程序會用到综芥,這個目錄下的應(yīng)用程序在運行時通常文件系統(tǒng)的其它部分還沒有加載丽蝎,所以必須進行靜態(tài)鏈接。
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應(yīng)用程序和庫的時候指定包含的java類庫膀藐,目前有core和framework兩種情況下定義成:
注意:LOCAL_JAVA_LIBRARIES
不是必須的屠阻,而且編譯APK時不允許定義(系統(tǒng)會自動添加)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
你可以通過設(shè)置LOCAL_LDFLAGS
來增加額外的標記傳遞給連接器。不過要記住额各,這些參數(shù)對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
允許你在你編譯你的可執(zhí)行程序或者庫的時候臊泰,添加一些指定的額外的庫蛉加。用lxxx的格式來指定你要引用的庫蚜枢,它們會被連接命令行直接解析缸逃。然而,要知道它不會為這些庫生成附屬厂抽。這在使用模擬器編譯而又想使用庫預(yù)編譯在主機上時是非常有用的需频。例如: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”前綴的特殊系統(tǒng)庫的名稱時很有用筷凤。例如:LOCAL_LDLIBS := -labc
上面的語句會告訴連接器在load time時生成連接到/system/lib/目錄下名字叫做libabc.so的動態(tài)庫昭殉。
補充2:
如果在你的Android.mk代碼中定義了LOCAL_LDLIBS變量苞七,例如:LOCAL_LDLIBS := -lGLESv1_CM -ldl -llog
但是,如果在編譯的過程中找不到這個庫挪丢,或者說這個庫并沒有存在與你編譯環(huán)境下蹂风,那么在編譯的時候就會出現(xiàn)如下圖所示的錯誤:
所以,在對
LOCAL_LDLIBS
變量賦值時乾蓬,要確保其正確性以及存在性惠啄,避免出現(xiàn)上圖的編譯錯誤。 -
LOCAL_NO_MANIFEST
如果你的Package沒有Manifest(AndroidManifest.xml)任内,你可以設(shè)置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編譯系統(tǒng)編譯App時會發(fā)生改變。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宏函數(shù)使用的是
MAKEFILE_LIST
變量,你必須在include其它任何makefile之前來調(diào)用它摘盆。另外铜跑,考慮到當你include任何子目錄時都要重新設(shè)置LOCAL_PATH
,你必須在include它們之前設(shè)置它骡澈。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
預(yù)編譯including $(BUILD_PREBUILT)
或者$(BUILD_HOST_PREBUILT)
時所用,指定需要復(fù)制的可執(zhí)行文件锅纺。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
變量是在預(yù)編譯including $(BUILD_PREBUILT)
或者$(BUILD_HOST_PREBUILT)
時所用, 指定需要復(fù)制的庫.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
變量必須包含一系列將被構(gòu)建和組合成模塊的C/C++源文件囤锉。
注意:不需要列出頭文件或include文件,因為生成系統(tǒng)會為你自動計算出源文件的依賴關(guān)系护锤。默認的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
類似烙懦,用來列出你的模塊中所需的靜態(tài)庫的列表驱入,你可以在你的module中包含一些想使用的靜態(tài)庫,通常我們使用共享庫氯析,但是有些地方亏较,像在sbin下的可執(zhí)行程序和主機上的可執(zhí)行程序我們要使用靜態(tài)庫。例如: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中已經(jīng)定義了該名稱巡通,那么你在編譯時就會報類似這樣的錯誤: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
).注意:編譯系統(tǒng)會自動產(chǎn)生合適的前綴和后綴,例如:
LOCAL_MODULE := screenshot
一個被命名為“screenshot”的共享庫模塊叉讥,將會生成“l(fā)ibscreenshot.so”文件窘行。
補充1:變量命名的規(guī)范性
如果LOCAL_MODULE
變量定義的值可能會被其它module調(diào)用時,就要考慮為其變量命名的規(guī)范性了图仓。特別是在使用JNI時罐盔,既在LOCAL_JNI_SHARED_LIBRARIES
變量中定義的值,最好要和LOCAL_MODULE
變量定義的值保存一致(具體請參考LOCAL_JNI_SHARED_LIBRARIES
變量的使用說明)救崔。
這時的LOCAL_MODULE
變量的命名最好以lib開頭惶看,既libxxx
,例如:LOCAL_MODULE := libscreenshot
-
LOCAL_MODULE_PATH
通知編譯系統(tǒng)將module放到其它地方而不是它通常的類型六孵。如果你重寫這個變量纬黎,確保你還要再設(shè)置LOCAL_UNSTRIPPED_PATH
變量的值。如果你忘了設(shè)置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
指定模塊所需要載入的完整靜態(tài)庫(這些靜態(tài)庫在鏈接是不允許鏈接器刪除其中無用的代碼)。通常這在你想往共享庫中增加一個靜態(tài)庫時是非常有用的主巍,共享庫就會接受到靜態(tài)庫暴露出的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
變量用來規(guī)定是否需要預(yù)連接處理(默認需要,用來做動態(tài)庫優(yōu)化)孕索。LOCAL_PRELINK_MODULE
只有在編譯.so
的時候才會有的選項逛艰,主要是通過預(yù)鏈接的方式來加快程序啟動和執(zhí)行的速度,如果在你的代碼(/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
文件的開頭部分有明確的規(guī)定镇眷,指定的內(nèi)存取值范圍分配給不同的部分使用,而我們的App的庫也給指定了一個范圍:0x90000000 - 0x9FFFFFFF Prelinked App Libraries
重新編譯恳啥,就不會再報錯了偏灿,下面的截圖中很清晰地看到已經(jīng)將libhello-jni.so庫預(yù)編譯成功了:
注意:
在給我們的應(yīng)用庫分配地址空間時丹诀,最好以1M為邊界钝的,地址空間大小按照由大到小的降序進行排序翁垂。下面是對于Prelink的說明:
Prelink
利用事先鏈接代替運行時鏈接的方法來加速共享庫的加載,它不僅可以加快起動速度硝桩,還可以減少部分內(nèi)存開銷沿猜。程序運行時的動態(tài)鏈接尤其是重定位(relocation
)的開銷對于大型系統(tǒng)來說是很大的。動態(tài)鏈接和加載的過程開銷很大碗脊,并且在大多數(shù)的系統(tǒng)上啼肩,函數(shù)庫并不會常常被更動,每次程序被執(zhí)行時所進行的鏈接動作都是完全相同的衙伶,對于嵌入式系統(tǒng)來說尤其如此祈坠。因此,這一過程可以改在運行時之前就可以預(yù)先處理好矢劲,即花一些時間利用Prelink工具對動態(tài)共享庫和可執(zhí)行文件進行處理赦拘,修改這些二進制文件并加入相應(yīng)的重定位等信息,節(jié)約了本來在程序啟動時的比較耗時的查詢函數(shù)地址等工作芬沉,這樣可以減少程序啟動的時間躺同,同時也減少了內(nèi)存的耗用。Prelink
的這種做法當然也有代價的丸逸,每次更新動態(tài)共享庫時蹋艺,相關(guān)的可執(zhí)行文件都需要重新執(zhí)行一遍Prelink
才能保證有效,因為新的共享庫中的符號信息黄刚、地址等很可能與原來的已經(jīng)不同了捎谨,這就是為什么android framework
代碼一改動,這時候就會導(dǎo)致相關(guān)的應(yīng)用程序重新被編譯憔维。 -
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
那么,在編譯的時候就會出現(xiàn)下圖的錯誤:
這說明在編譯libsanangeles.so
找不到其規(guī)則虱肄,因為在上面的代碼中定義的是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
連接器的工作方式?jīng)Q定的底瓣。
當模塊foo
是一個靜態(tài)庫的時候并且代碼依賴于系統(tǒng)庫時會很有用的谢揪。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
”參數(shù)進行編譯來表明它依賴于系統(tǒng)日志庫,因為它依賴于foo
茁肠。 LOCAL_ALLOW_UNDEFINED_SYMBOLS
默認情況下患民,當試圖編譯一個共享庫的時候遇到任何未定義的引用都可能導(dǎo)致"未定義符號"(undefined symbol
)的錯誤。這在你的源代碼中捕獲bug
會很有用垦梆。
然而匹颤,但是由于某些原因,你需要禁用此檢查的話托猩,設(shè)置變量為"true
"即可印蓖。需要注意的是,相應(yīng)的共享庫在運行時可能加載失敗京腥。-
LOCAL_ARM_MODE
LOCAL_ARM_MODE
變量主要是應(yīng)用與嵌入式產(chǎn)品的編譯系統(tǒng)中赦肃,可以指定為arm
模式。例如:LOCAL_ARM_MODE := arm
注意:你需要執(zhí)行編譯系統(tǒng)為在ARM模式下通過文件的名字增加后綴的方式編譯指定的源文件。例如:
LOCAL_SRC_FILES :=foo.c bar.c.arm
這會告訴編譯系統(tǒng)一直以ARM模式編譯"
bar.c
"他宛,并且通過LOCAL_ARM_MODE
的值編譯foo.c船侧。
BUILD_XXX
變量
-
BUILD_SHARED_LIBRARY
BUILD_SHARED_LIBRARY
:指明要編譯生成動態(tài)共享庫。指向一個生成腳本堕汞,這個腳本通過LOCAL_XXX
變量收集關(guān)于組件的信息勺爱,并決定如何根據(jù)你列出來的源文件生成目標共享庫晃琳。
注意:在include
這個腳本文件之前你必須至少已經(jīng)定義了LOCAL_MODULE
和LOCAL_SRC_FILES
讯检。例如:include $(BUILD_SHARED_LIBRARY)
注意:這會生成一個名為
lib$(LOCAL_MODULE).so
的動態(tài)庫。 -
BUILD_STATIC_LIBRARY
BUILD_STATIC_LIBRARY
與BUILD_SHARED_LIBRARY
類似卫旱,但用來生成目標靜態(tài)庫人灼。靜態(tài)庫不會被拷貝至你的project/packages
文件夾下,但可用來生成共享庫顾翼。
例如:include $(BUILD_STATIC_LIBRARY)
注意:這會生成一個靜態(tài)庫投放,名叫
lib$(LOCAL_MODULE).a
的靜態(tài)庫。 -
BUILD_PACKAGE
BUILD_PACKAGE
變量用于在最好編譯時生成*.apk
适贸,例如:include $(BUILD_STATIC_LIBRARY)
注意:這會生成一個
apk
安裝包灸芳,名字就叫$(LOCAL_MODULE).apk
的安裝包。
其它變量
CLEAR_VARS
CLEAR_VARS
變量是生成系統(tǒng)提供的拜姿,它指向一個特殊的GNU Makefile
烙样,它將會為你自動清除許多名為LOCAL_XXX
的變量(比如:LOCAL_MODULE
、LOCAL_SRC_FILES
蕊肥、LOCAL_STATIC_LIBRARIES
等)谒获,但LOCAL_PATH
是例外,它不會被清除壁却。
注意:這些變量的清除是必須的批狱,因為所有的控制文件是在單一的Makefile
,執(zhí)行環(huán)境中解析的展东,在這里所有的變量都是全局的赔硫。TARGET_PLATFORM
TARGET_PLATFORM
:當解析該Android.mk
文件時用它來指定Andoid
目標平臺的名稱。例如:android-3
與Android 1.5
相對應(yīng)盐肃。
NDK提供的宏函數(shù)
下面是GNU Make的宏函數(shù)卦停,必須通過這樣的形式調(diào)用:
$(call <function>)
-
my-dir
my-dir
:返回放置當前Android.mk
的文件夾相對于NDK
生成系統(tǒng)根目錄的路徑∧张睿可用來在Android.mk
的開始處定義LOCAL_PATH
的值:LOCAL_PATH := $(call my-dir)
-
all-subdir-makefiles
all-subdir-makefiles
:返回my-dir
子目錄下的所有Android.mk
惊完。例如:
代碼的結(jié)構(gòu)如下: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
处硬。這個函數(shù)能將深層嵌套的代碼文件夾提供給生成系統(tǒng)小槐。
注意:默認情況下,NDK
僅在source/*/Android.mk
里尋找文件。 this-makefile
this-makefile
:返回當前Makefile
所在目錄的路徑凿跳。parent-makefile
parent-makefile
:返回父Makefile
所在目錄makefile
的路徑件豌。-
import-module
一個允許你通過名字找到并包含另一個模塊的的Android.mk
的功能,例如:$(call import-module,<name>)
這將會找到通過
NDK_MODULE_PATH
環(huán)境變量引用的模塊<name
>的目錄列表控嗜,并且將其自動包含到Android.mk
中茧彤。
Application.mk
作用
Application.mk
目的是描述在你的應(yīng)用程序中所需要的模塊(即靜態(tài)庫或動態(tài)庫)。
Application.mk
文件通常被放置在$PROJECT/jni/Application.mk
下疆栏,$PROJECT
指的是您的項目曾掂。另一種方法是將其放在頂層的子目錄下,既$NDK/apps
目錄下壁顶,例如:
$NDK/apps/<myapp>/Application.mk
<myapp
>是一個簡稱珠洗,用于描述你的NDK
編譯系統(tǒng)的應(yīng)用程序(這個名字不會生成共享庫或者最終的包),這個方法是Android NDK r4
以前的若专,現(xiàn)在仍然兼容许蓖。但是我們強烈建議你使用第一種方法,因為它更簡單并且不用修改NDK
安裝樹的目錄调衰。
詳細說明
下面是Application.mk中定義的幾個變量:
APP_MODULES
APP_MODULES
變量是強制性的膊爪,并且會列出所有你所需要的模塊。它不允許用一個空格來分隔其模塊列表嚎莉,這個模塊名字被定義在Android.mk
文件中的LOCAL_MODULE
中米酬。-
APP_PROJECT_PATH
APP_PROJECT_PATH
變量也是強制性的,并且會給出應(yīng)用程序工程的根目錄一個絕對路徑萝喘。這是用來復(fù)制或者安裝一個沒有任何版本限制的JNI庫淮逻,從而給 APK 生成工具一個詳細的路徑。例如:# \HelloNDK\Application.mk APP_PROJECT_PATH := $(call my-dir)/project APP_MODULES := HelloNdk
這里定義了工程路徑為
$(call my-dir)/project
阁簸,而要編譯的模塊則是HelloNdk
爬早,這樣編譯系統(tǒng)才會找到我們要編譯的庫和源文件。 APP_CFLAGS
APP_CFLAGS
則是當要編譯模塊中有任何C
文件或者C++
文件的時候启妹,C
編譯器的信號就會被發(fā)出筛严。這里可以在你的應(yīng)用中需要這些模塊時,進行編譯的調(diào)整饶米,這樣就不許要直接更改Android.mk
為文件本身了桨啃。APP_OPTIM
這個變量是可選的,可以定義成兩個值release
或者debug
檬输,用于修改編譯程序模塊時的優(yōu)化層級照瘾。release
模式是默認的,會產(chǎn)生高優(yōu)化的文件丧慈,debug
模式會生成不優(yōu)化的文件析命,使得調(diào)試更容易進行主卫。
注意:調(diào)試release
和debug
文件都是可能的,但是release
版在調(diào)試節(jié)提高的信息很少鹃愤,一些變量被優(yōu)化輸出簇搅,無法檢查,代碼被重排序软吐,使得跟蹤代碼很困難瘩将,堆棧追蹤也不可靠,等等凹耙。APP_CPPFLAGS
當編譯的只有C++
源文件的時候姿现,可以通過這個C++
編譯器來設(shè)置。
注意:在Android NDK-1.5_r1
中使兔,這個標志可以應(yīng)用于C
和C++
源文件中建钥。并且得到了糾正藤韵,以建立完整的與系統(tǒng)相匹配的Android編譯系統(tǒng)虐沥。你先可也可以使用APP_CFLAGS
來應(yīng)用于C
或者C++
源文件中。建議使用APP_CFLAGS
泽艘。
補充
兩種不同級別的應(yīng)用apk
目前我所理解是在Android
開發(fā)中我們會遇到兩種不同級別的應(yīng)用apk
:系統(tǒng)級應(yīng)用apk
和普通級應(yīng)用apk
欲险。
下面分別描述兩種apk
:
編譯系統(tǒng)級應(yīng)用apk
將應(yīng)用程序的代碼放到武當源代碼目錄路徑下,然后進行編譯匹涮。將編譯生成的*.apk
通過adb
或者其它方式放到/system/app
目錄下即可天试。編譯普通級應(yīng)用apk
應(yīng)用程序的代碼并沒有放到平臺的源代碼目錄下,然后通過編譯生成的*.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 總結(jié)
-
build-system.html
: Android 源碼下platform/build/core/build-system.html