下面是我個人對android-ndk-r4版本中Android.mk文件做的翻譯,由於自己英語水平和專業知識的限制,有些地方可能翻譯的不是很準確,敬請指正,本文僅希望對做android NDK開發的同仁們能提供一點點的幫助而已。
Android.mk文件是用來描述你想要編譯進系統的資源的。
這個文件的語法允許你把你的資源打包進“modules”。Module應該是下面module中的一種:
- a static library
- a shared library
只有shared library會被打包到你的應用程序中,儘管static library也能用來生產shared library。
你可以在一個Android.mk中定義多個module,你也可以在幾個module中使用相同的資源文件。
編譯系統會爲你處理一些細節。例如,你不需要在你的Android.mk文件中列出你的頭文件或是你的文件之間明確的依賴關係。NDK編譯系統會自動幫你處理。
這同時意味着,當升級到新版本的NDK時,你不需要動你的Android.mk文件就可以使用心得toolchain/platform支持。
注意,用在Android.mk文件中的語法和整個的android平臺的開源資源語法是不同的。編譯系統實現了對他們不同的使用,這是故意這樣設計的,用來允許應用開發人員更容易地重用外部庫資源代碼。
一個簡單的例子:
在語法詳細說明之前,讓我們先來看一個簡單的“hello-jni”例子,文件在$NDK/samples/hello-jni下:
在這我們可以看到:
-src目錄下包含這個簡單的android工程的一個java源文件
-jni目錄下包含了這個例子的本地源文件
這個源文件實現了一個簡單的共享庫,這個共享庫實現了一個想vm應用程序返回一個字符串的本地方法。
-Android.mk文件向NDK編譯系統描述了一個共享庫。它的內容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY
現在讓我們來解釋這些語法:
LOCAL_PATH := $(call my-dir)
一個Android.mk文件必須以定義一個LOCAL_PATH 變量開始。它用來在你的工程樹形目錄下定位你的資源文件的位置。在這個例子中,那個編譯系統提供的“my-dir”宏函數用來返回當前目錄的路徑(Android.mk文件所在的目錄)。
include $(CLEAR_VARS)
編譯系統提供的一個指向特殊的GNU Makefile文件的CLEAR_VARS變量將爲你清理很多LOCAL_PATH中的LOCAL_XXX變量(例如: LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等)。這是有必要的,因爲所有的編譯控制文件都會在一個單獨的GNU Make可執行上下文中被解析,在這裏所有的變量都是全局的。
LOCAL_MODULE := hello-jni
用來標識分佈在Android.mk文件中的每一個module的LOCAL_MODULE變量必須定義。名字必須是唯一的而且不能有空格。
注意,編譯系統會給相應的生成文件自動地添加適當的前綴和後綴。換句話說,一個命名爲“foo”的共享庫module將生成名爲“libfoo.so”的文件。
重點注意:
如果你的module命名爲“libfoo”,編譯系統不會爲你添加另外一個“lib”前綴,還是會生成一個“libfoo.so”的文件而已。這個爲了支持你需要使用的android平臺資源的Android.mk文件。
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SRC_FILES變量必須包含一個將要被編譯和彙編進module的一個C/C++資源文件列表。注意你不需要列出頭文件和包含文件,因爲編譯系統會自動幫你計算依賴關係;僅僅需要列出直接傳遞給編譯器的資源文件就可以了。
注意:默認的C++資源文件的擴展名是“.cpp”。然而可以通過定義LOCAL_DEFAULT_CPP_EXTENSION變量來指定一個不同的擴展名。不要忘記最開始的dot(例如:.cxx是可以的,但是cxx是不行的)。
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY是編譯系統提供的一個指向GNU Makefile腳本的變量,它負責收集從最近的include $(CLEAR_VARS)' 變量開始的所有內定義的 LOCAL_XXX變量的信息,然後決定哪些需要編譯以及怎樣編譯。BUILD_STATIC_LIBRARY變量可以生成一個static library。
在samples目錄下有幾個複雜的例子,在他們的Android.mk文件中你可以看到一些註釋。
Reference:
下面是你可以在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)
好,我們接着看:
NDK提供的變量:
在你的Android.mk文件解析之前,編譯系統定義了這些GNU Make變量。注意,在某種情況下NDK可能會多次解析你的Android.mk文件,每一次解析某些變量的定義可能不同。
CLEAR_VARS
指向一個幾乎所有在"Module-description"部分列出的LOCAL_XXX變量都沒有定義的編譯腳本(我的理解就是在編譯前清空所有變量,然後在這個變量的下面再重新定義)。在開始一個新的module之前你必須包含這個腳本,例如:
include $(CLEAR_VARS)
BUILD_SHARED_LIBRARY
指向一個收集所有你提供的關於module的LOCAL_XXX變量的信息的編譯腳本,然後決定如何根據你列出的資源編譯一個目標shared library。注意,你在包含這個變量之前必須至少OCAL_MODULE 和LOCAL_SRC_FILES 變量。可以這樣使用:
include $(BUILD_SHARED_LIBRARY)
注意它將生成一個以lib$(LOCAL_MODULE).so命名的文件。
BUILD_STATIC_LIBRARY
BUILD_STATIC_LIBRARY變量的不同之處在於它是用來生成一個目標static ibrary 。static ibrary 不會被打包進你的工程包,但是可以用來生成shared library(看下面的LOCAL_STATIC_LIBRARIES 和LOCAL_STATIC_WHOLE_LIBRARIES說明)。可以這樣使用:
include $(BUILD_STATIC_LIBRARY)
注意,它將生成一個以lib$(LOCAL_MODULE).a命名的文件。
TARGET_ARCH
指明一個full Android open-source build明確說明的目標CPU架構。如果是“arm”用來所有的兼容ARM的編譯,不依賴與CPU架構版本。
TARGET_PLATFORM
當Android.mk文件解析時,指明一個android目標平臺。例如"android-3”對應"android1.5"系統鏡像。完整的平臺名字和android系統鏡像的對應列表可以閱讀docs/目錄下STABLE-APIS.TXT文檔。
TARGET_ARCH_ABI
當Android.mk文件解析時,指明一個目標CPU+ABI。有兩個值可供使用:
armeabi
對應Armv5TE
armeabi-v7a
注意:Android NDK 1.6_r1之上的版本,這個變量被定義爲'arm'。然而,這個變量的重定義能夠被Android platform內部更好的使用。
對於更多的ABI架構和對應的兼容性問題的細節,可以閱讀docs目錄下的CPU-ARCH-ABIS.TXT文檔。
其他的ABI架構將會在以後的NDK版本中引入,並且有不同的名字。注意,所有基於ARM的ABIs都會被'TARGET_ARCH' 定義爲 'arm',但是可能有不同的'TARGET_ARCH_ABI'。
TARGET_ABI
目標平臺和abi的連結,它被定義爲$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI),當你用真機測試一個指定的目標平臺鏡像的時候會有用處。
默認情況下,它將是'android-3-armeabi'
(Android NDK 1.6_r1以上的版本,'android-3-arm'是默認的)
NDK提供的宏函數
下面是GNU Make宏函數,通過使用'$(call <function>)'來獲得他的值。它們返回文本信息。
my-dir
返回相對於上層的NDK編譯系統的當前Android.mk文件目錄的路徑。這對於以下面這種方式在你的Android.mk文件的開始定義LOCAL_PATH變量很有用:
LOCAL_PATH := $(call my-dir)
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目錄下尋找文件。
this-makefile
返回當前Makefile文件的路徑(例如,函數在哪裏被調用)。
parent-makefile
放回父Makefile文件在包含樹中的路徑。例如包含當前Makefile文件的Makefile文件的路徑。
Module描述變量
下面的變量是用來向系統描述你的module的。你應該在一個'include $(CLEAR_VARS)'和'一個include $(BUILD_XXXXX)'變量之間定義它們中的某些變量。前面的部分已經介紹過了,$(CLEAR_VARS)變量是取消/清除所有這些變量的定義,除非在他們的描述說明確地註明。
LOCAL_PATH
這個變量用來給出當前文件的路徑。你必須在你的Android.mk文件的開始定義它,比如下面這樣定義是可以的:
LOCAL_PATH := $(call my-dir)
這個變量不會被$(CLEAR_VARS)清除掉,所以在每一個Android.mk文件中僅需要定義一次(在你的一個文件中定義了幾個module的情況下)。
LOCAL_MODULE
你的module的名字。它必須在所有的module名字中是唯一的,而且不能有空格。你必須在任何一個$(BUILD_XXXX)腳本之前定義它。
module名字決定了生成文件的名字,例如:一個名字爲lib<foo>.so的shared library的module名字爲<foo>。可是,你應該在你的NDK編譯文件中(無論是Android.mk還是 Application.mk)進引用其它module的通用的名字(例如<foo>)。
LOCAL_SRC_FILES
這是一個將用於你的module編譯的資源文件列表。只用在列表中的文件纔會傳遞給編譯器,因爲編譯系統自動幫你計算依賴關係。
注意,所有的資源文件名字都是相對於LOCAL_PATH的,你能夠使用路徑分隔符,例如:
LOCAL_SRC_FILES := foo.c \
toto/bar.c
注意:只有使用Unix風格的正斜線(/)在編譯文件中纔可以,windows風格的反斜線(\)不能被正確處理。
LOCAL_CPP_EXTENSION
這是一個可選擇的變量,它能用來定義c++源文件的文件後綴名。默認的是'.cpp',但是你可以改變它。例如:
LOCAL_CPP_EXTENSION := .cxx
LOCAL_C_INCLUDES
一個可選的編譯所有源文件(C,C++,彙編)時會添加到include搜索路徑相對於NDK *root*文件夾的路徑的列表。例如:
LOCAL_C_INCLUDES := sources/foo
或者
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
他們會在LOCAL_CFLAGS / LOCAL_CPPFLAGS中的任何inclusion flag之前被替換。
LOCAL_CFLAGS
當編譯 C 和 C++源文件時傳遞的一組可選的編譯器標識。
它可以用來指定額外的宏定義或編譯選項。
重要信息:不要在你的Android.mk文件中嘗試去改變優化/調試等級,這個可以通過在你的Android.mk文件中指明適當的信息來讓系統自動處理,將會使NDK生成在調試中可以使用的有用的數據文件。
注意:在android-ndk-1.5_r1中,對應標記僅能用在c源文件上,不能用在c++源文件上。這能夠正確匹配所有的android編譯系統行爲。(你現在可以使用LOCAL_CPPFLAGS來僅爲c++源文件設置標識)。
LOCAL_CXXFLAGS
LOCAL_CPPFLAGS的一個別名。注意,這種標識的使用是過時的,因爲在以後的NDK版本中它可能被取消。
LOCAL_CPPFLAGS
當僅編譯 C++源文件時傳遞的一組可選的編譯器標識。他們將在LOCAL_CFLAGS之後顯示在命令行上。
注意:在android-ndk-1.5_r1中,對應標記在c源文件上和在c++源文件上都能使用。這能夠正確匹配所有的android編譯系統行爲。(你現在可以使用LOCAL_CFLAGS來爲c++源文件和c源文件設置標識)。
LOCAL_STATIC_LIBRARIES
將要被鏈接進這個module的static libraries modules的列表(用BUILD_STATIC_LIBRARY編譯過的)。這個僅在shared library modules中能被檢測到。
LOCAL_SHARED_LIBRARIES
依賴於運行時的shared libraries *modules*的列表。這在鏈接時是必要的,爲了把對應的信息嵌入到生成的文件中。
注意:他不會把列出的module添加到編譯表中,例如:你仍然需要在你的Application.mk文件中把他們添加到你的應用程序需要的module中。
LOCAL_LDLIBS
編譯你的module時使用到的額外的鏈接器標識列表。它能夠用一個帶 "-l" 前綴的表達式傳遞一個指明的系統庫的名字。例如:下面的定義將會通知編譯器在加載的時候生成一個鏈接到/system/lib/libz.so的module:
LOCAL_LDLIBS := -lz
閱讀docs目錄下的STABLE-APIS.TXT文檔,你將能瞭解到在這個NDK版本中你可以鏈接的系統庫的列表。
LOCAL_ALLOW_UNDEFINED_SYMBOLS
默認情況下,當嘗試編譯一個shared library時任何一個沒有定義的引用都將產生一個"undefined symbol"錯誤。這對在你的源代碼中捕獲bug很有幫助。
可是,如果由於某種原因你需要讓這些檢查失效,把這個變量設置爲'true'。注意,對應的shared library在運行時加載可能出錯。
LOCAL_ARM_MODE
默認情況下,ARM的二進制文件將生成每一條指令都是16位的'thumb'模式。你可以定義這個變量爲'arm',如果你想強制生成的module目標文件爲 'arm'(每條指令32位)模式的。例如:
LOCAL_ARM_MODE := arm
注意,你也可以通過在源文件名後面添加一個'.arm'來指示編譯系統只把指定的文件編譯'bar.c'。例如:
LOCAL_SRC_FILES := foo.c bar.c.arm
通知編譯系統總是把'bar.c'編譯成'.arm'模式,而依據LOCAL_ARM_MODE變量的值來編譯 foo.c 文件。
注意:在你的Application.mk文件中設置APP_OPTIM爲 'debug'也能強制生成ARM二進制文件。這是因爲在thumb模式下toolchain debugger 中的bug不能得到較好的處理。
LOCAL_ARM_NEON
把這個變量設置爲'true'能夠允許你在你的C和C++源碼中使用ARM Advanced SIMD(a.k.a. NEON)GCC,也能在彙編文件中使用NEON指令。
你僅僅應該在想要對應ARMv7指令序列的'armeabi-v7a' ABI時定義它。注意,不是所有基於ARMv7的CPU都支持NEON指令序列擴展,你應該開啓運行時檢查來確保你能安全地使用這些代碼在運行期間。更多的關於這些的內容,可以閱讀docs目錄下的CPU-ARM-NEON.TXT 和CPU-FEATURES.TXT.文檔。
二選一,你也可以在指定的文件後面加上'.neon'來指示表儀器把指定的文件編譯成NEON支持的文件,例如:
LOCAL_SRC_FILES = foo.c.neon bar.c zoo.c.arm.neon
在這個例子中'foo.c'將被編譯成thumb+neon模式,'bar.c'將被編譯成'thumb'模式,'zoo.c' 將被編譯成'arm+neon'模式。
注意,如果 '.neon' 後綴和'.arm' 後綴都是用,那麼 '.neon' 後綴和一定要添加到'.arm' 後綴之後。(例如: foo.c.arm.neon 可以,但是 foo.c.neon.arm 不可以)。
LOCAL_DISABLE_NO_EXECUTE
Android NDK r4添加的用來支持"NX bit"安全特性的。默認情況下是啓用的,但是你也可以設置這個變量爲 'true'來啓用它,如果你真的需要這麼做的話。
注意:這個特性不會修改ABI,它僅僅是使內核能夠指向ARMv6+ CPU設備。在這種特性下生成的機器碼將不需要做修改就能運行在更早的CPU架構上。