Android.mk文件是GNU Makefile的一小部分,它用來對Android程序進行編譯。
由於全部的編譯文件都在同一個 GNU MAKE 執行環境中進行執行,而Android.mk中全部的變量都是全局的。所以,您應儘可能少聲明變量,不要認爲某些變量在解析過程當中不會被定義。
一個Android.mk文件能夠編譯多個模塊,
每一個模塊屬下列類型之一
:
1)APK程序
通常的Android程序,編譯打包生成apk文件
2)JAVA庫
java類庫,編譯打包生成jar文件
3)C\C++應用程序
可執行的C\C++應用程序
4)C\C++靜態庫
編譯生成C\C++靜態庫,並打包成.a文件
5)C\C++共享庫
編譯生成共享庫(動態連接庫),並打包成.so文, 有且只有共享庫才能被安裝/複製到您的應用軟件(APK)包中。
能夠在每個Android.mk file 中定義一個或多個模塊,你也能夠在幾個模塊中使用同一個
源代碼文件。 編譯系統爲你處理許多細節問題。例如,你不須要在你的 Android.mk 中列出頭文件和依
賴文件。編譯系統將會爲你自動處理這些問題。這也意味着,在升級 NDK 後,你應該
獲得新的toolchain/platform支持,並且不須要改變你的 Android.mk 文件。
注意,NDK的Anroid.mk語法同公開發布的Android平臺開源代碼的Anroid.mk語法很接近,然而編譯系統實現他們的
方式倒是不一樣的,這是故意這樣設計的,可讓程序開發人員重用外部庫的源代碼更容易。
在描述語法細節以前,我們來看一個簡單的"hello world"的例子,好比,下面的文件:
sources/helloworld/helloworld.c
sources/helloworld/Android.mk
'helloworld.c'是一個 JNI
共享庫
,實現返回"hello world"字符串的原生方法。相應的
Android.mk 文件會象下面這樣:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= helloworld
LOCAL_SRC_FILES := helloworld.c
include $(BUILD_SHARED_LIBRARY)
解釋一下幾行代碼:
LOCAL_PATH := $(call my-dir)
一個Android.mk file首先必須定義好LOCAL_PATH變量。它表示是當前文件的路徑。
在這個例子中, 宏函數‘my-dir’, 由編譯系統提供, 用於返回當前路徑(即包含Android.mk file
文件的目錄)。
include $(CLEAR_VARS)
CLEAR_VARS 由編譯系統提供(能夠在 android 安裝目錄下的
/build/core/config.mk
文件看
到其定義,爲 CLEAR_VARS:=
$(BUILD_SYSTEM)/clear_vars.mk
),指定讓
GNU MAKEFILE
該腳本爲你清除許多 LOCAL_XXX 變量 ( 例如 LOCAL_MODULE , LOCAL_SRC_FILES ,
LOCAL_STATIC_LIBRARIES,等等…),除 LOCAL_PATH。這是必要的,由於全部的編譯文件都在同一個 GNU MAKE 執行環境中,全部的變量都是全局的。
因此咱們須要先清空這些變量(LOCAL_PATH除外)。又由於LOCAL_PATH老是要求在每一個模塊中都要進行設置,因此並須要清空它。
另外
注意
,該語句的意思就是把CLEAR_VARS變量所指向的腳本文件包含進來。
LOCAL_MODULE := helloworld
LOCAL_MODULE 變量必須定義,以標識你在 Android.mk 文件中描述的每一個模塊。名稱必須是惟一的,並且不包含任何空格。注意編譯系統會自動產生合適的前綴和後綴,換句話說,一個被命名爲'foo'的共享庫模塊,將會生成'libfoo.so'文件。
注意
:若是把庫命名爲‘libhelloworld’,編譯系統將不會添加任何的 lib 前綴,也會生成 libhelloworld.so。
LOCAL_SRC_FILES := helloworld.c
LOCAL_SRC_FILES 變量必須包含將要編譯打包進模塊中的 C 或 C++源代碼文件。不用
在這裏列出頭文件和包含文件,編譯系統將會自動找出依賴型的文件,固然對於包含文件,你包含時指定的路徑應該正確。
注意
,默認的 C++源碼文件的擴展名是‘.cpp’ 。指定一個不一樣的擴展名也是可能的,只要定義
LOCAL_DEFAULT_CPP_EXTENSION 變量,不要忘記開始的小圓點(也就是定義爲 ‘.cxx’,而不是‘cxx’)
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY 是編譯系統提供的變量,指向一個 GNU Makefile 腳本(應該
就是在 build/core 目錄下的 shared_library.mk) ,將根據LOCAL_XXX系列變量中的值,來編譯生成共享庫(動態連接庫)。
若是想生成靜態庫,則用BUILD_STATIC_LIBRARY
在NDK的sources/samples目錄下有更復雜一點的例子,寫有註釋的 Android.mk 文件。
2、自定義變量
如下是在 Android.mk中依賴或定義的變量列表, 能夠定義其餘變量爲本身使用,可是NDK編譯系統保留下列變量名:
-以 LOCAL_開頭的名字(例如 LOCAL_MODULE)
-以 PRIVATE_, NDK_ 或 APP_開頭的名字(內部使用)
-小寫名字(內部使用,例如‘my-dir’)
若是爲了方便在 Android.mk 中定義本身的變量,建議使用 MY_前綴,一個小例子:
MY_SOURCES := foo.c
ifneq ($(MY_CONFIG_BAR),)
MY_SOURCES += bar.c
endif
LOCAL_SRC_FILES += $(MY_SOURCES)
注意:‘:=’是賦值的意思;'+='是追加的意思;‘$’表示引用某變量的值。
3、GNU Make系統變量
這些 GNU Make變量在你的 Android.mk 文件解析以前,就由編譯系統定義好了。注意在
某些狀況下,NDK可能分析 Android.mk 幾回,每一次某些變量的定義會有不一樣。
(1)CLEAR_VARS:
指向一個編譯腳本,幾乎全部未定義的 LOCAL_XXX 變量都在"Module-description"節中列出。必須在開始一個新模塊以前包含這個腳本:include$(CLEAR_VARS),用於重置除LOCAL_PATH變量外的,全部LOCAL_XXX系列變量。
(2)BUILD_SHARED_LIBRARY:
指向編譯腳本,根據全部的在 LOCAL_XXX 變量把列出的源代碼文件編譯成一個共享庫。
注意,必須至少在包含這個文件以前定義 LOCAL_MODULE 和 LOCAL_SRC_FILES。
(3) BUILD_STATIC_LIBRARY:
一個 BUILD_SHARED_LIBRARY 變量用於編譯一個靜態庫。靜態庫不會複製到的APK包中,可是可以用於編譯共享庫。
示例
:include $(
BUILD_STATIC_LIBRARY)
注意,這將會生成一個名爲
lib$(LOCAL_MODULE).a 的文件
(4)TARGET_ARCH:
目標 CPU平臺的名字, 和 android 開放源碼中指定的那樣。若是是
arm,表示要生成 ARM 兼容的指令,與 CPU架構的修訂版無關。
(5)TARGET_PLATFORM:
Android.mk 解析的時候,目標 Android 平臺的名字.
詳情可參考/development/ndk/docs/stable- apis.txt.
android-3 -> Official Android 1.5 system images
android-4 -> Official Android 1.6 system images
android-5 -> Official Android 2.0 system images
(6)TARGET_ARCH_ABI:
暫時只支持兩個 value,armeabi 和 armeabi-v7a。在如今的版本中通常把這兩個值簡單的定義爲 arm, 經過 android 平臺內部對它重定義來得到更好的匹配。其餘的 ABI 將在之後的 NDK 版本中介紹,它們會有不一樣的名字。注意雖然全部基於
ARM的ABI都會把 'TARGET_ARCH'定義成‘arm’, 可是會有不一樣的‘TARGET_ARCH_ABI’。
( 7 ) TARGET_ABI:
目標平臺和 ABI 的組合,它事實上被定義成$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI) ,在想要在真實的設備中針對一個特別的目標系統進行測試時,會有用。在默認的狀況下,它會是'android-3-arm'。
5、模塊描述變量
下面的變量用於向編譯系統描述你的模塊。你應該定義在'include $(CLEAR_VARS)'和'include $(BUILD_XXXXX)'之間。正如前面描寫的那樣,$(CLEAR_VARS)是一個腳本,清除全部這些變量。
(1) LOCAL_PATH:
這個變量用於給出當前文件的路徑。必須在 Android.mk 的開頭定義,能夠這樣使用:LOCAL_PATH := $(call my-dir) 這個變量不會被$(CLEAR_VARS)清除,所以每一個 Android.mk 只須要定義一次(即便在一個文件中定義了幾個模塊的狀況下)。
(2)LOCAL_MODULE:
這是模塊的名字,它必須是惟一的,並且不能包含空格。必須在包含任一的$(BUILD_XXXX)腳本以前定義它。模塊的名字決定了生成文件的名字。例如,若是一個一個共享庫模塊的名字是,那麼生成文件的名字就是 lib.so。可是,在的 NDK 生成文
件中(或者 Android.mk 或者 Application.mk),應該只涉及(引用)有正常名字的其餘模塊。
(3)LOCAL_SRC_FILES:
這是要編譯的源代碼文件列表。只要列出要傳遞給編譯器的文件,由於編譯系統自動計算依賴。注意源代碼文件名稱都是相對於 LOCAL_PATH的,你可使用路徑部分,例如:
LOCAL_SRC_FILES := foo.c toto/bar.c\
Hello.c
文件之間能夠用空格或Tab鍵進行分割,換行請用"\".若是是追加源代碼文件的話,請用LOCAL_SRC_FILES +=
注意:
在生成文件中都要使用UNIX風格的斜槓(/).windows風格的反斜槓不會被正確的處理。
注意:
能夠LOCAL_SRC_FILES := $(call all-subdir-java-files)這種形式來包含local_path目錄下的全部java文件。
(4) LOCAL_CPP_EXTENSION:
這是一個可選變量, 用來指定C++代碼文件的擴展名,默認是'.cpp',可是能夠改變它,好比:
LOCAL_CPP_EXTENSION := .cxx
(5) LOCAL_C_INCLUDES:
可選變量,表示頭文件的搜索路徑。默認的頭文件的搜索路徑是LOCAL_PATH目錄。
示例:
LOCAL_C_INCLUDES := sources/foo或LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
LOCAL_C_INCLUDES須要在任何包含LOCAL_CFLAGS/LOCAL_CPPFLAGS標誌以前進行設置。
(6)LOCAL_CFLAGS:
可選的編譯器選項,在編譯 C 代碼文件的時候使用。這多是有
用的,指定一個附加的包含路徑(相對於NDK的頂層目錄),宏定義,或者編譯選項。
注意:
不要在 Android.mk 中改變 optimization/debugging 級別,只要在 Application.mk 中指定合適的信息,就會自動地爲你處理這個問題,在調試期間,會讓 NDK自動生成有用的數據文件。
(7)LOCAL_CXXFLAGS:
與 LOCAL_CFLAGS同理,針對 C++源文件。
(8)LOCAL_CPPFLAGS:
與 LOCAL_CFLAGS同理,可是對 C 和 C++ source files都適
用。
(9)LOCAL_STATIC_LIBRARIES:
表示該模塊須要使用哪些靜態庫,以便在編譯時進行連接。
(10)LOCAL_SHARED_LIBRARIES:
表示模塊在運行時要依賴的共享庫(動態庫),在連接時就須要,以便在生成文件時嵌入其相應的信息。注意:它不會附加列出的模塊到編譯圖,也就是仍然須要在Application.mk 中把它們添加到程序要求的模塊中。
(11)LOCAL_LDLIBS:
編譯模塊時要使用的附加的連接器選項。這對於使用‘
-l’
前綴傳遞指定庫的名字是有用的。
例如,
LOCAL_LDLIBS := -lz
表示
告訴連接器生成的模塊要在加載時刻連接到
/system/lib/libz.so
可查看 docs/STABLE-APIS.TXT 獲取使用 NDK發行版能連接到的開放的系統庫列表。
(12) LOCAL_ALLOW_UNDEFINED_SYMBOLS:
默認狀況下, 在試圖編譯一個共享庫時,任何未定義的引用將致使一個「未定義的符號」錯誤。這對於在源代碼文件中捕捉錯誤會有很大的幫助。然而,若是由於某些緣由,須要不啓動這項檢查,可把這個變量設爲‘true’。
注意相應的共享庫可能在運行時加載失敗。(這個通常儘可能不要去設爲 true)。
(13) LOCAL_ARM_MODE:
默認狀況下, arm目標二進制會以 thumb 的形式生成(16 位),你能夠經過設置這個變量爲 arm若是你但願你的 module 是以 32 位指令的形式。
'arm' (32-bit instructions) mode. E.g.:
LOCAL_ARM_MODE := arm
注意
:能夠在編譯的時候告訴系統針對某個源碼文件進行特定的類型的編譯
好比,LOCAL_SRC_FILES := foo.c bar.c.arm 這樣就告訴系統老是將 bar.c 以
arm的模式編譯。
(14)LOCAL_MODULE_PATH 和 LOCAL_UNSTRIPPED_PATH
在 Android.mk 文件中, 還能夠用
LOCAL_MODULE_PATH
和
LOCAL_UNSTRIPPED_PATH
指定最後的目標安裝路徑.
不一樣的文件系統路徑用如下的宏進行選擇:
TARGET_ROOT_OUT:表示根文件系統。
TARGET_OUT:表示 system文件系統。
TARGET_OUT_DATA:表示 data文件系統。
用法如:
LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT)
至於LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的區別,暫時還不清楚。
7、GNU Make‘功能’宏
GNU Make‘功能’宏,必須經過使用'$(call )'來調用,調用他們將返回文本化的信息。
(1)my-dir:
返回當前 Android.mk 所在的目錄的路徑,相對於 NDK 編譯系統的頂層。這是有用的,在 Android.mk 文件的開頭如此定義:
LOCAL_PATH := $(call my-dir)
(2)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 將會只搜索在 sources/*/Android.mk 中的文件。
(3)this-makefile:
返回當前Makefile 的路徑(即這個函數調用的地方)
(4)parent-makefile:
返回調用樹中父 Makefile 路徑。即包含當前Makefile的Makefile 路徑。
(5)grand-parent-makefile:
返回調用樹中父Makefile的父Makefile的路徑
8、 Android.mk 使用模板
在一個 Android.mk 中能夠生成多個APK應用程序,JAVA庫,C\C++可執行程序,C\C++動態庫和C\C++靜態庫。
(
1)編譯APK應用程序模板。
(2)編譯JAVA庫模板
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Any libraries that this library depends on
LOCAL_JAVA_LIBRARIES := android.test.runner
# The name of the jar file to create
LOCAL_MODULE := sample
# Build a static jar file.
include $(
BUILD_STATIC_JAVA_LIBRARY
)
注:LOCAL_JAVA_LIBRARIES := android.test.runner表示生成的JAVA庫的jar文件名
(3)編譯C/C++應用程序模板以下:
LOCAL_PATH := $(call my-dir)
#include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := test_exe
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(
BUILD_EXECUTABLE
)
注:‘:=’是賦值的意思,'+='是追加的意思,‘$’表示引用某變量的值
LOCAL_SRC_FILES中加入源文件路徑,LOCAL_C_INCLUDES中加入須要的頭文件搜索路徑
LOCAL_STATIC_LIBRARIES
加入所須要連接的
靜態庫(*.a)
的名稱,
LOCAL_SHARED_LIBRARIES
中加入所須要連接的
動態庫(*.so)
的名稱,
LOCAL_MODULE
表示模塊最終的名稱,
BUILD_EXECUTABLE
表示以一個可執行程序的方式進行編譯。
(4)編譯C\C++靜態庫
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
helloworld.c
LOCAL_MODULE:= libtest_static
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(
BUILD_STATIC_LIBRARY
)
和上面類似,BUILD_STATIC_LIBRARY 表示編譯一個靜態庫。
(5)編譯C\C++動態庫的模板
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := helloworld.c
LOCAL_MODULE := libtest_shared
TARGET_PRELINK_MODULES := false
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(
BUILD_SHARED_LIBRARY
)
和上面類似,BUILD_SHARED_LIBRARY 表示編譯一個共享庫。
以上三者的生成結果分別在以下目錄中,generic 依具體 target 會變:
out/target/product/generic/obj/APPS
out/target/product/generic/obj/JAVA_LIBRARIES
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY
每一個模塊的目標文件夾分別爲:
1)
APK程序
:XXX_intermediates
2)
JAVA庫程序
:XXX_intermediates
這裏的XXX
3)
C\C++可執行程序
:XXX_intermediates
4)
C\C++靜態庫
: XXX_static_intermediates
5)
C\C++動態庫
: XXX_shared_intermediates